xref: /core/vcl/source/edit/textview.cxx (revision c7ea7664)
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 <memory>
21 #include <i18nutil/searchopt.hxx>
22 #include <o3tl/deleter.hxx>
23 #include <vcl/textview.hxx>
24 #include <vcl/texteng.hxx>
25 #include <vcl/settings.hxx>
26 #include "textdoc.hxx"
27 #include <vcl/textdata.hxx>
28 #include <vcl/xtextedt.hxx>
29 #include "textdat2.hxx"
30 #include <vcl/commandevent.hxx>
31 #include <vcl/inputctx.hxx>
32 
33 #include <svl/undo.hxx>
34 #include <vcl/cursor.hxx>
35 #include <vcl/weld.hxx>
36 #include <vcl/window.hxx>
37 #include <vcl/svapp.hxx>
38 #include <tools/stream.hxx>
39 
40 #include <sal/log.hxx>
41 #include <sot/formats.hxx>
42 
43 #include <cppuhelper/weak.hxx>
44 #include <cppuhelper/queryinterface.hxx>
45 #include <com/sun/star/i18n/XBreakIterator.hpp>
46 #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
47 #include <com/sun/star/i18n/WordType.hpp>
48 #include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp>
49 #include <com/sun/star/datatransfer/XTransferable.hpp>
50 #include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
51 #include <com/sun/star/datatransfer/clipboard/XFlushableClipboard.hpp>
52 #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
53 #include <com/sun/star/datatransfer/dnd/XDragGestureRecognizer.hpp>
54 #include <com/sun/star/datatransfer/dnd/XDropTarget.hpp>
55 #include <com/sun/star/util/SearchFlags.hpp>
56 
57 #include <vcl/edit.hxx>
58 
59 #include <sot/exchange.hxx>
60 
61 #include <algorithm>
62 #include <cstddef>
63 
64 TETextDataObject::TETextDataObject( const OUString& rText ) : maText( rText )
65 {
66 }
67 
68 // css::uno::XInterface
69 css::uno::Any TETextDataObject::queryInterface( const css::uno::Type & rType )
70 {
71     css::uno::Any aRet = ::cppu::queryInterface( rType, static_cast< css::datatransfer::XTransferable* >(this) );
72     return (aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType ));
73 }
74 
75 // css::datatransfer::XTransferable
76 css::uno::Any TETextDataObject::getTransferData( const css::datatransfer::DataFlavor& rFlavor )
77 {
78     css::uno::Any aAny;
79 
80     SotClipboardFormatId nT = SotExchange::GetFormat( rFlavor );
81     if ( nT == SotClipboardFormatId::STRING )
82     {
83         aAny <<= GetText();
84     }
85     else if ( nT == SotClipboardFormatId::HTML )
86     {
87         sal_uLong nLen = GetHTMLStream().TellEnd();
88         GetHTMLStream().Seek(0);
89 
90         css::uno::Sequence< sal_Int8 > aSeq( nLen );
91         memcpy( aSeq.getArray(), GetHTMLStream().GetData(), nLen );
92         aAny <<= aSeq;
93     }
94     else
95     {
96         throw css::datatransfer::UnsupportedFlavorException();
97     }
98     return aAny;
99 }
100 
101 css::uno::Sequence< css::datatransfer::DataFlavor > TETextDataObject::getTransferDataFlavors(  )
102 {
103     GetHTMLStream().Seek( STREAM_SEEK_TO_END );
104     bool bHTML = GetHTMLStream().Tell() > 0;
105     css::uno::Sequence< css::datatransfer::DataFlavor > aDataFlavors( bHTML ? 2 : 1 );
106     SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aDataFlavors.getArray()[0] );
107     if ( bHTML )
108         SotExchange::GetFormatDataFlavor( SotClipboardFormatId::HTML, aDataFlavors.getArray()[1] );
109     return aDataFlavors;
110 }
111 
112 sal_Bool TETextDataObject::isDataFlavorSupported( const css::datatransfer::DataFlavor& rFlavor )
113 {
114     SotClipboardFormatId nT = SotExchange::GetFormat( rFlavor );
115     return ( nT == SotClipboardFormatId::STRING );
116 }
117 
118 struct ImpTextView
119 {
120     ExtTextEngine*      mpTextEngine;
121 
122     VclPtr<vcl::Window> mpWindow;
123     TextSelection       maSelection;
124     Point               maStartDocPos;
125 
126     std::unique_ptr<vcl::Cursor, o3tl::default_delete<vcl::Cursor>> mpCursor;
127 
128     std::unique_ptr<TextDDInfo, o3tl::default_delete<TextDDInfo>> mpDDInfo;
129 
130     std::unique_ptr<SelectionEngine> mpSelEngine;
131     std::unique_ptr<TextSelFunctionSet> mpSelFuncSet;
132 
133     css::uno::Reference< css::datatransfer::dnd::XDragSourceListener > mxDnDListener;
134 
135     sal_uInt16              mnTravelXPos;
136 
137     bool                mbAutoScroll            : 1;
138     bool                mbInsertMode            : 1;
139     bool                mbReadOnly              : 1;
140     bool                mbPaintSelection        : 1;
141     bool                mbAutoIndent            : 1;
142     bool                mbHighlightSelection    : 1;
143     bool                mbCursorEnabled         : 1;
144     bool                mbClickedInSelection    : 1;
145     bool                mbSupportProtectAttribute : 1;
146     bool                mbCursorAtEndOfLine;
147 };
148 
149 TextView::TextView( ExtTextEngine* pEng, vcl::Window* pWindow ) :
150     mpImpl(new ImpTextView)
151 {
152     pWindow->EnableRTL( false );
153 
154     mpImpl->mpWindow = pWindow;
155     mpImpl->mpTextEngine = pEng;
156 
157     mpImpl->mbPaintSelection = true;
158     mpImpl->mbAutoScroll = true;
159     mpImpl->mbInsertMode = true;
160     mpImpl->mbReadOnly = false;
161     mpImpl->mbHighlightSelection = false;
162     mpImpl->mbAutoIndent = false;
163     mpImpl->mbCursorEnabled = true;
164     mpImpl->mbClickedInSelection = false;
165     mpImpl->mbSupportProtectAttribute = false;
166     mpImpl->mbCursorAtEndOfLine = false;
167 //  mbInSelection = false;
168 
169     mpImpl->mnTravelXPos = TRAVEL_X_DONTKNOW;
170 
171     mpImpl->mpSelFuncSet = std::make_unique<TextSelFunctionSet>( this );
172     mpImpl->mpSelEngine = std::make_unique<SelectionEngine>( mpImpl->mpWindow, mpImpl->mpSelFuncSet.get() );
173     mpImpl->mpSelEngine->SetSelectionMode( SelectionMode::Range );
174     mpImpl->mpSelEngine->EnableDrag( true );
175 
176     mpImpl->mpCursor.reset(new vcl::Cursor);
177     mpImpl->mpCursor->Show();
178     pWindow->SetCursor( mpImpl->mpCursor.get() );
179     pWindow->SetInputContext( InputContext( pEng->GetFont(), InputContextFlags::Text|InputContextFlags::ExtText ) );
180 
181     if ( pWindow->GetSettings().GetStyleSettings().GetSelectionOptions() & SelectionOptions::Invert )
182         mpImpl->mbHighlightSelection = true;
183 
184     pWindow->SetLineColor();
185 
186     if ( pWindow->GetDragGestureRecognizer().is() )
187     {
188         vcl::unohelper::DragAndDropWrapper* pDnDWrapper = new vcl::unohelper::DragAndDropWrapper( this );
189         mpImpl->mxDnDListener = pDnDWrapper;
190 
191         css::uno::Reference< css::datatransfer::dnd::XDragGestureListener> xDGL( mpImpl->mxDnDListener, css::uno::UNO_QUERY );
192         pWindow->GetDragGestureRecognizer()->addDragGestureListener( xDGL );
193         css::uno::Reference< css::datatransfer::dnd::XDropTargetListener> xDTL( xDGL, css::uno::UNO_QUERY );
194         pWindow->GetDropTarget()->addDropTargetListener( xDTL );
195         pWindow->GetDropTarget()->setActive( true );
196         pWindow->GetDropTarget()->setDefaultActions( css::datatransfer::dnd::DNDConstants::ACTION_COPY_OR_MOVE );
197     }
198 }
199 
200 TextView::~TextView()
201 {
202     mpImpl->mpSelEngine.reset();
203     mpImpl->mpSelFuncSet.reset();
204 
205     if ( mpImpl->mpWindow->GetCursor() == mpImpl->mpCursor.get() )
206         mpImpl->mpWindow->SetCursor( nullptr );
207 
208     mpImpl->mpCursor.reset();
209     mpImpl->mpDDInfo.reset();
210 }
211 
212 void TextView::Invalidate()
213 {
214     mpImpl->mpWindow->Invalidate();
215 }
216 
217 void TextView::SetSelection( const TextSelection& rTextSel, bool bGotoCursor )
218 {
219     // if someone left an empty attribute and then the Outliner manipulated the selection
220     if ( !mpImpl->maSelection.HasRange() )
221         mpImpl->mpTextEngine->CursorMoved( mpImpl->maSelection.GetStart().GetPara() );
222 
223     // if the selection is manipulated after a KeyInput
224     mpImpl->mpTextEngine->CheckIdleFormatter();
225 
226     HideSelection();
227     TextSelection aNewSel( rTextSel );
228     mpImpl->mpTextEngine->ValidateSelection( aNewSel );
229     ImpSetSelection( aNewSel );
230     ShowSelection();
231     ShowCursor( bGotoCursor );
232 }
233 
234 void TextView::SetSelection( const TextSelection& rTextSel )
235 {
236     SetSelection( rTextSel, mpImpl->mbAutoScroll );
237 }
238 
239 const TextSelection& TextView::GetSelection() const
240 {
241     return mpImpl->maSelection;
242 }
243 TextSelection&      TextView::GetSelection()
244 {
245     return mpImpl->maSelection;
246 }
247 
248 void TextView::DeleteSelected()
249 {
250 //  HideSelection();
251 
252     mpImpl->mpTextEngine->UndoActionStart();
253     TextPaM aPaM = mpImpl->mpTextEngine->ImpDeleteText( mpImpl->maSelection );
254     mpImpl->mpTextEngine->UndoActionEnd();
255 
256     ImpSetSelection( aPaM );
257     mpImpl->mpTextEngine->FormatAndUpdate( this );
258     ShowCursor();
259 }
260 
261 void TextView::ImpPaint(vcl::RenderContext& rRenderContext, const Point& rStartPos, tools::Rectangle const* pPaintArea, TextSelection const* pSelection)
262 {
263     if (!mpImpl->mbPaintSelection)
264     {
265         pSelection = nullptr;
266     }
267     else
268     {
269         // set correct background color;
270         // unfortunately we cannot detect if it has changed
271         vcl::Font aFont = mpImpl->mpTextEngine->GetFont();
272         Color aColor = rRenderContext.GetBackground().GetColor();
273         aColor.SetTransparency(0);
274         if (aColor != aFont.GetFillColor())
275         {
276             if (aFont.IsTransparent())
277                 aColor = COL_TRANSPARENT;
278             aFont.SetFillColor(aColor);
279             mpImpl->mpTextEngine->maFont = aFont;
280         }
281     }
282 
283     mpImpl->mpTextEngine->ImpPaint(&rRenderContext, rStartPos, pPaintArea, pSelection);
284 }
285 
286 void TextView::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
287 {
288     ImpPaint(rRenderContext, rRect);
289 }
290 
291 void TextView::ImpPaint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
292 {
293     if ( !mpImpl->mpTextEngine->GetUpdateMode() || mpImpl->mpTextEngine->IsInUndo() )
294         return;
295 
296     TextSelection *pDrawSelection = nullptr;
297     if (!mpImpl->mbHighlightSelection && mpImpl->maSelection.HasRange())
298         pDrawSelection = &mpImpl->maSelection;
299 
300     Point aStartPos = ImpGetOutputStartPos(mpImpl->maStartDocPos);
301     ImpPaint(rRenderContext, aStartPos, &rRect, pDrawSelection);
302     if (mpImpl->mbHighlightSelection)
303         ImpHighlight(mpImpl->maSelection);
304 }
305 
306 void TextView::ImpHighlight( const TextSelection& rSel )
307 {
308     TextSelection aSel( rSel );
309     aSel.Justify();
310     if ( aSel.HasRange() && !mpImpl->mpTextEngine->IsInUndo() && mpImpl->mpTextEngine->GetUpdateMode() )
311     {
312         mpImpl->mpCursor->Hide();
313 
314         SAL_WARN_IF( mpImpl->mpTextEngine->mpIdleFormatter->IsActive(), "vcl", "ImpHighlight: Not formatted!" );
315 
316         tools::Rectangle aVisArea( mpImpl->maStartDocPos, mpImpl->mpWindow->GetOutputSizePixel() );
317         long nY = 0;
318         const sal_uInt32 nStartPara = aSel.GetStart().GetPara();
319         const sal_uInt32 nEndPara = aSel.GetEnd().GetPara();
320         for ( sal_uInt32 nPara = 0; nPara <= nEndPara; ++nPara )
321         {
322             const long nParaHeight = mpImpl->mpTextEngine->CalcParaHeight( nPara );
323             if ( ( nPara >= nStartPara ) && ( ( nY + nParaHeight ) > aVisArea.Top() ) )
324             {
325                 TEParaPortion* pTEParaPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( nPara );
326                 std::vector<TextLine>::size_type nStartLine = 0;
327                 std::vector<TextLine>::size_type nEndLine = pTEParaPortion->GetLines().size() -1;
328                 if ( nPara == nStartPara )
329                     nStartLine = pTEParaPortion->GetLineNumber( aSel.GetStart().GetIndex(), false );
330                 if ( nPara == nEndPara )
331                     nEndLine = pTEParaPortion->GetLineNumber( aSel.GetEnd().GetIndex(), true );
332 
333                 // iterate over all lines
334                 for ( std::vector<TextLine>::size_type nLine = nStartLine; nLine <= nEndLine; nLine++ )
335                 {
336                     TextLine& rLine = pTEParaPortion->GetLines()[ nLine ];
337                     sal_Int32 nStartIndex = rLine.GetStart();
338                     sal_Int32 nEndIndex = rLine.GetEnd();
339                     if ( ( nPara == nStartPara ) && ( nLine == nStartLine ) )
340                         nStartIndex = aSel.GetStart().GetIndex();
341                     if ( ( nPara == nEndPara ) && ( nLine == nEndLine ) )
342                         nEndIndex = aSel.GetEnd().GetIndex();
343 
344                     // possible if at the beginning of a wrapped line
345                     if ( nEndIndex < nStartIndex )
346                         nEndIndex = nStartIndex;
347 
348                     tools::Rectangle aTmpRect( mpImpl->mpTextEngine->GetEditCursor( TextPaM( nPara, nStartIndex ), false ) );
349                     aTmpRect.AdjustTop(nY );
350                     aTmpRect.AdjustBottom(nY );
351                     Point aTopLeft( aTmpRect.TopLeft() );
352 
353                     aTmpRect = mpImpl->mpTextEngine->GetEditCursor( TextPaM( nPara, nEndIndex ), true );
354                     aTmpRect.AdjustTop(nY );
355                     aTmpRect.AdjustBottom(nY );
356                     Point aBottomRight( aTmpRect.BottomRight() );
357                     aBottomRight.AdjustX( -1 );
358 
359                     // only paint if in the visible region
360                     if ( ( aTopLeft.X() < aBottomRight.X() ) && ( aBottomRight.Y() >= aVisArea.Top() ) )
361                     {
362                         Point aPnt1( GetWindowPos( aTopLeft ) );
363                         Point aPnt2( GetWindowPos( aBottomRight ) );
364 
365                         tools::Rectangle aRect( aPnt1, aPnt2 );
366                         mpImpl->mpWindow->Invert( aRect );
367                     }
368                 }
369             }
370             nY += nParaHeight;
371 
372             if ( nY >= aVisArea.Bottom() )
373                 break;
374         }
375     }
376 }
377 
378 void TextView::ImpSetSelection( const TextSelection& rSelection )
379 {
380     if (rSelection != mpImpl->maSelection)
381     {
382         bool bCaret = false, bSelection = false;
383         const TextPaM &rEnd = rSelection.GetEnd();
384         const TextPaM &rOldEnd = mpImpl->maSelection.GetEnd();
385         bool bGap = rSelection.HasRange(), bOldGap = mpImpl->maSelection.HasRange();
386         if (rEnd != rOldEnd)
387             bCaret = true;
388         if (bGap || bOldGap)
389             bSelection = true;
390 
391         mpImpl->maSelection = rSelection;
392 
393         if (bSelection)
394             mpImpl->mpTextEngine->Broadcast(TextHint(SfxHintId::TextViewSelectionChanged));
395 
396         if (bCaret)
397             mpImpl->mpTextEngine->Broadcast(TextHint(SfxHintId::TextViewCaretChanged));
398     }
399 }
400 
401 void TextView::ShowSelection()
402 {
403     ImpShowHideSelection();
404 }
405 
406 void TextView::HideSelection()
407 {
408     ImpShowHideSelection();
409 }
410 
411 void TextView::ShowSelection( const TextSelection& rRange )
412 {
413     ImpShowHideSelection( &rRange );
414 }
415 
416 void TextView::ImpShowHideSelection(const TextSelection* pRange)
417 {
418     const TextSelection* pRangeOrSelection = pRange ? pRange : &mpImpl->maSelection;
419 
420     if ( pRangeOrSelection->HasRange() )
421     {
422         if ( mpImpl->mbHighlightSelection )
423         {
424             ImpHighlight( *pRangeOrSelection );
425         }
426         else
427         {
428             if( mpImpl->mpWindow->IsPaintTransparent() )
429                 mpImpl->mpWindow->Invalidate();
430             else
431             {
432                 TextSelection aRange( *pRangeOrSelection );
433                 aRange.Justify();
434                 bool bVisCursor = mpImpl->mpCursor->IsVisible();
435                 mpImpl->mpCursor->Hide();
436                 Invalidate();
437                 if (bVisCursor)
438                     mpImpl->mpCursor->Show();
439             }
440         }
441     }
442 }
443 
444 bool TextView::KeyInput( const KeyEvent& rKeyEvent )
445 {
446     bool bDone      = true;
447     bool bModified  = false;
448     bool bMoved     = false;
449     bool bEndKey    = false;    // special CursorPosition
450     bool bAllowIdle = true;
451 
452     // check mModified;
453     // the local bModified is not set e.g. by Cut/Paste, as here
454     // the update happens somewhere else
455     bool bWasModified = mpImpl->mpTextEngine->IsModified();
456     mpImpl->mpTextEngine->SetModified( false );
457 
458     TextSelection aCurSel( mpImpl->maSelection );
459     TextSelection aOldSel( aCurSel );
460 
461     sal_uInt16 nCode = rKeyEvent.GetKeyCode().GetCode();
462     KeyFuncType eFunc = rKeyEvent.GetKeyCode().GetFunction();
463     if ( eFunc != KeyFuncType::DONTKNOW )
464     {
465         switch ( eFunc )
466         {
467             case KeyFuncType::CUT:
468             {
469                 if ( !mpImpl->mbReadOnly )
470                     Cut();
471             }
472             break;
473             case KeyFuncType::COPY:
474             {
475                 Copy();
476             }
477             break;
478             case KeyFuncType::PASTE:
479             {
480                 if ( !mpImpl->mbReadOnly )
481                     Paste();
482             }
483             break;
484             case KeyFuncType::UNDO:
485             {
486                 if ( !mpImpl->mbReadOnly )
487                     Undo();
488             }
489             break;
490             case KeyFuncType::REDO:
491             {
492                 if ( !mpImpl->mbReadOnly )
493                     Redo();
494             }
495             break;
496 
497             default:    // might get processed below
498                         eFunc = KeyFuncType::DONTKNOW;
499         }
500     }
501     if ( eFunc == KeyFuncType::DONTKNOW )
502     {
503         switch ( nCode )
504         {
505             case KEY_UP:
506             case KEY_DOWN:
507             case KEY_LEFT:
508             case KEY_RIGHT:
509             case KEY_HOME:
510             case KEY_END:
511             case KEY_PAGEUP:
512             case KEY_PAGEDOWN:
513             case css::awt::Key::MOVE_WORD_FORWARD:
514             case css::awt::Key::SELECT_WORD_FORWARD:
515             case css::awt::Key::MOVE_WORD_BACKWARD:
516             case css::awt::Key::SELECT_WORD_BACKWARD:
517             case css::awt::Key::MOVE_TO_BEGIN_OF_LINE:
518             case css::awt::Key::MOVE_TO_END_OF_LINE:
519             case css::awt::Key::SELECT_TO_BEGIN_OF_LINE:
520             case css::awt::Key::SELECT_TO_END_OF_LINE:
521             case css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH:
522             case css::awt::Key::MOVE_TO_END_OF_PARAGRAPH:
523             case css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH:
524             case css::awt::Key::SELECT_TO_END_OF_PARAGRAPH:
525             case css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT:
526             case css::awt::Key::MOVE_TO_END_OF_DOCUMENT:
527             case css::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT:
528             case css::awt::Key::SELECT_TO_END_OF_DOCUMENT:
529             {
530                 if ( ( !rKeyEvent.GetKeyCode().IsMod2() || ( nCode == KEY_LEFT ) || ( nCode == KEY_RIGHT ) )
531                       && !( rKeyEvent.GetKeyCode().IsMod1() && ( nCode == KEY_PAGEUP || nCode == KEY_PAGEDOWN ) ) )
532                 {
533                     aCurSel = ImpMoveCursor( rKeyEvent );
534                     if ( aCurSel.HasRange() ) {
535                         css::uno::Reference<css::datatransfer::clipboard::XClipboard> aSelection(GetWindow()->GetPrimarySelection());
536                         Copy( aSelection );
537                     }
538                     bMoved = true;
539                     if ( nCode == KEY_END )
540                         bEndKey = true;
541                 }
542                 else
543                     bDone = false;
544             }
545             break;
546             case KEY_BACKSPACE:
547             case KEY_DELETE:
548             case css::awt::Key::DELETE_WORD_BACKWARD:
549             case css::awt::Key::DELETE_WORD_FORWARD:
550             case css::awt::Key::DELETE_TO_BEGIN_OF_LINE:
551             case css::awt::Key::DELETE_TO_END_OF_LINE:
552             {
553                 if ( !mpImpl->mbReadOnly && !rKeyEvent.GetKeyCode().IsMod2() )
554                 {
555                     sal_uInt8 nDel = ( nCode == KEY_DELETE ) ? DEL_RIGHT : DEL_LEFT;
556                     sal_uInt8 nMode = rKeyEvent.GetKeyCode().IsMod1() ? DELMODE_RESTOFWORD : DELMODE_SIMPLE;
557                     if ( ( nMode == DELMODE_RESTOFWORD ) && rKeyEvent.GetKeyCode().IsShift() )
558                         nMode = DELMODE_RESTOFCONTENT;
559 
560                     switch( nCode )
561                     {
562                     case css::awt::Key::DELETE_WORD_BACKWARD:
563                         nDel = DEL_LEFT;
564                         nMode = DELMODE_RESTOFWORD;
565                         break;
566                     case css::awt::Key::DELETE_WORD_FORWARD:
567                         nDel = DEL_RIGHT;
568                         nMode = DELMODE_RESTOFWORD;
569                         break;
570                     case css::awt::Key::DELETE_TO_BEGIN_OF_LINE:
571                         nDel = DEL_LEFT;
572                         nMode = DELMODE_RESTOFCONTENT;
573                         break;
574                     case css::awt::Key::DELETE_TO_END_OF_LINE:
575                         nDel = DEL_RIGHT;
576                         nMode = DELMODE_RESTOFCONTENT;
577                         break;
578                     default: break;
579                     }
580 
581                     mpImpl->mpTextEngine->UndoActionStart();
582                     if(mpImpl->mbSupportProtectAttribute)
583                     {
584                         //expand selection to include all protected content - if there is any
585                         const TextCharAttrib* pStartAttr = mpImpl->mpTextEngine->FindCharAttrib(
586                                     TextPaM(mpImpl->maSelection.GetStart().GetPara(),
587                                             mpImpl->maSelection.GetStart().GetIndex()),
588                                     TEXTATTR_PROTECTED );
589                         const TextCharAttrib* pEndAttr = mpImpl->mpTextEngine->FindCharAttrib(
590                                     TextPaM(mpImpl->maSelection.GetEnd().GetPara(),
591                                             mpImpl->maSelection.GetEnd().GetIndex()),
592                                     TEXTATTR_PROTECTED );
593                         if(pStartAttr && pStartAttr->GetStart() < mpImpl->maSelection.GetStart().GetIndex())
594                         {
595                             mpImpl->maSelection.GetStart().GetIndex() = pStartAttr->GetStart();
596                             aOldSel = mpImpl->maSelection; // update to deleted!
597                         }
598                         if(pEndAttr && pEndAttr->GetEnd() > mpImpl->maSelection.GetEnd().GetIndex())
599                         {
600                             mpImpl->maSelection.GetEnd().GetIndex() = pEndAttr->GetEnd();
601                             aOldSel = mpImpl->maSelection; // update to deleted!
602                         }
603                     }
604                     aCurSel = ImpDelete( nDel, nMode );
605                     mpImpl->mpTextEngine->UndoActionEnd();
606                     bModified = true;
607                     bAllowIdle = false;
608                 }
609                 else
610                     bDone = false;
611             }
612             break;
613             case KEY_TAB:
614             {
615                 if ( !mpImpl->mbReadOnly && !rKeyEvent.GetKeyCode().IsShift() &&
616                         !rKeyEvent.GetKeyCode().IsMod1() && !rKeyEvent.GetKeyCode().IsMod2() &&
617                         ImplCheckTextLen( OUString('x') ) )
618                 {
619                     aCurSel = mpImpl->mpTextEngine->ImpInsertText( aCurSel, '\t', !IsInsertMode() );
620                     bModified = true;
621                 }
622                 else
623                     bDone = false;
624             }
625             break;
626             case KEY_RETURN:
627             {
628                 // do not swallow Shift-RETURN, as this would disable multi-line entries
629                 // in dialogs & property editors
630                 if ( !mpImpl->mbReadOnly && !rKeyEvent.GetKeyCode().IsMod1() &&
631                         !rKeyEvent.GetKeyCode().IsMod2() && ImplCheckTextLen( OUString('x') ) )
632                 {
633                     mpImpl->mpTextEngine->UndoActionStart();
634                     aCurSel = mpImpl->mpTextEngine->ImpInsertParaBreak( aCurSel );
635                     if ( mpImpl->mbAutoIndent )
636                     {
637                         TextNode* pPrev = mpImpl->mpTextEngine->mpDoc->GetNodes()[ aCurSel.GetEnd().GetPara() - 1 ].get();
638                         sal_Int32 n = 0;
639                         while ( ( n < pPrev->GetText().getLength() ) && (
640                                     ( pPrev->GetText()[ n ] == ' ' ) ||
641                                     ( pPrev->GetText()[ n ] == '\t' ) ) )
642                         {
643                             n++;
644                         }
645                         if ( n )
646                             aCurSel = mpImpl->mpTextEngine->ImpInsertText( aCurSel, pPrev->GetText().copy( 0, n ) );
647                     }
648                     mpImpl->mpTextEngine->UndoActionEnd();
649                     bModified = true;
650                 }
651                 else
652                     bDone = false;
653             }
654             break;
655             case KEY_INSERT:
656             {
657                 if ( !mpImpl->mbReadOnly )
658                     SetInsertMode( !IsInsertMode() );
659             }
660             break;
661             default:
662             {
663                 if ( TextEngine::IsSimpleCharInput( rKeyEvent ) )
664                 {
665                     sal_Unicode nCharCode = rKeyEvent.GetCharCode();
666                     if ( !mpImpl->mbReadOnly && ImplCheckTextLen( OUString(nCharCode) ) )    // otherwise swallow the character anyway
667                     {
668                         aCurSel = mpImpl->mpTextEngine->ImpInsertText( nCharCode, aCurSel, !IsInsertMode(), true );
669                         bModified = true;
670                     }
671                 }
672                 else
673                     bDone = false;
674             }
675         }
676     }
677 
678     if ( aCurSel != aOldSel )   // Check if changed, maybe other method already changed mpImpl->maSelection, don't overwrite that!
679         ImpSetSelection( aCurSel );
680 
681     if ( ( nCode != KEY_UP ) && ( nCode != KEY_DOWN ) )
682         mpImpl->mnTravelXPos = TRAVEL_X_DONTKNOW;
683 
684     if ( bModified )
685     {
686         // Idle-Formatter only if AnyInput
687         if ( bAllowIdle && Application::AnyInput( VclInputFlags::KEYBOARD) )
688             mpImpl->mpTextEngine->IdleFormatAndUpdate( this );
689         else
690             mpImpl->mpTextEngine->FormatAndUpdate( this);
691     }
692     else if ( bMoved )
693     {
694         // selection is painted now in ImpMoveCursor
695         ImpShowCursor( mpImpl->mbAutoScroll, true, bEndKey );
696     }
697 
698     if ( mpImpl->mpTextEngine->IsModified() )
699         mpImpl->mpTextEngine->Broadcast( TextHint( SfxHintId::TextModified ) );
700     else if ( bWasModified )
701         mpImpl->mpTextEngine->SetModified( true );
702 
703     return bDone;
704 }
705 
706 void TextView::MouseButtonUp( const MouseEvent& rMouseEvent )
707 {
708     mpImpl->mbClickedInSelection = false;
709     mpImpl->mnTravelXPos = TRAVEL_X_DONTKNOW;
710     mpImpl->mpSelEngine->SelMouseButtonUp( rMouseEvent );
711     if ( rMouseEvent.IsMiddle() && !IsReadOnly() &&
712          ( GetWindow()->GetSettings().GetMouseSettings().GetMiddleButtonAction() == MouseMiddleButtonAction::PasteSelection ) )
713     {
714         css::uno::Reference<css::datatransfer::clipboard::XClipboard> aSelection(GetWindow()->GetPrimarySelection());
715         Paste( aSelection );
716         if ( mpImpl->mpTextEngine->IsModified() )
717             mpImpl->mpTextEngine->Broadcast( TextHint( SfxHintId::TextModified ) );
718     }
719     else if ( rMouseEvent.IsLeft() && GetSelection().HasRange() )
720     {
721         css::uno::Reference<css::datatransfer::clipboard::XClipboard> aSelection(GetWindow()->GetPrimarySelection());
722         Copy( aSelection );
723     }
724 }
725 
726 void TextView::MouseButtonDown( const MouseEvent& rMouseEvent )
727 {
728     mpImpl->mpTextEngine->CheckIdleFormatter();    // for fast typing and MouseButtonDown
729     mpImpl->mnTravelXPos = TRAVEL_X_DONTKNOW;
730     mpImpl->mbClickedInSelection = IsSelectionAtPoint( rMouseEvent.GetPosPixel() );
731 
732     mpImpl->mpTextEngine->SetActiveView( this );
733 
734     mpImpl->mpSelEngine->SelMouseButtonDown( rMouseEvent );
735 
736     // mbu 20.01.2005 - SelMouseButtonDown() possibly triggers a 'selection changed'
737     // notification. The appropriate handler could change the current selection,
738     // which is the case in the MailMerge address block control. To enable select'n'drag
739     // we need to reevaluate the selection after the notification has been fired.
740     mpImpl->mbClickedInSelection = IsSelectionAtPoint( rMouseEvent.GetPosPixel() );
741 
742     // special cases
743     if ( !rMouseEvent.IsShift() && ( rMouseEvent.GetClicks() >= 2 ) )
744     {
745         if ( rMouseEvent.IsMod2() )
746         {
747             HideSelection();
748             ImpSetSelection( mpImpl->maSelection.GetEnd() );
749             SetCursorAtPoint( rMouseEvent.GetPosPixel() );  // not set by SelectionEngine for MOD2
750         }
751 
752         if ( rMouseEvent.GetClicks() == 2 )
753         {
754             // select word
755             if ( mpImpl->maSelection.GetEnd().GetIndex() < mpImpl->mpTextEngine->GetTextLen( mpImpl->maSelection.GetEnd().GetPara() ) )
756             {
757                 HideSelection();
758                 TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes()[  mpImpl->maSelection.GetEnd().GetPara() ].get();
759                 css::uno::Reference < css::i18n::XBreakIterator > xBI = mpImpl->mpTextEngine->GetBreakIterator();
760                 css::i18n::Boundary aBoundary = xBI->getWordBoundary( pNode->GetText(), mpImpl->maSelection.GetEnd().GetIndex(), mpImpl->mpTextEngine->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES, true );
761                 TextSelection aNewSel( mpImpl->maSelection );
762                 aNewSel.GetStart().GetIndex() = aBoundary.startPos;
763                 aNewSel.GetEnd().GetIndex() = aBoundary.endPos;
764                 if(mpImpl->mbSupportProtectAttribute)
765                 {
766                     //expand selection to include all protected content - if there is any
767                     const TextCharAttrib* pStartAttr = mpImpl->mpTextEngine->FindCharAttrib(
768                                 TextPaM(aNewSel.GetStart().GetPara(), aBoundary.startPos),
769                                 TEXTATTR_PROTECTED );
770                     const TextCharAttrib* pEndAttr = mpImpl->mpTextEngine->FindCharAttrib(
771                                 TextPaM(aNewSel.GetEnd().GetPara(), aBoundary.endPos),
772                                 TEXTATTR_PROTECTED );
773                     if(pStartAttr && pStartAttr->GetStart() < aNewSel.GetStart().GetIndex())
774                     {
775                         aNewSel.GetStart().GetIndex() = pStartAttr->GetStart();
776                     }
777                     if(pEndAttr && pEndAttr->GetEnd() > aNewSel.GetEnd().GetIndex())
778                     {
779                         aNewSel.GetEnd().GetIndex() = pEndAttr->GetEnd();
780                     }
781                 }
782                 ImpSetSelection( aNewSel );
783                 ShowSelection();
784                 ShowCursor();
785             }
786         }
787         else if ( rMouseEvent.GetClicks() == 3 )
788         {
789             // select paragraph
790             if ( mpImpl->maSelection.GetStart().GetIndex() || ( mpImpl->maSelection.GetEnd().GetIndex() < mpImpl->mpTextEngine->GetTextLen( mpImpl->maSelection.GetEnd().GetPara() ) ) )
791             {
792                 HideSelection();
793                 TextSelection aNewSel( mpImpl->maSelection );
794                 aNewSel.GetStart().GetIndex() = 0;
795                 aNewSel.GetEnd().GetIndex() = mpImpl->mpTextEngine->mpDoc->GetNodes()[ mpImpl->maSelection.GetEnd().GetPara() ]->GetText().getLength();
796                 ImpSetSelection( aNewSel );
797                 ShowSelection();
798                 ShowCursor();
799             }
800         }
801     }
802 }
803 
804 void TextView::MouseMove( const MouseEvent& rMouseEvent )
805 {
806     mpImpl->mnTravelXPos = TRAVEL_X_DONTKNOW;
807     mpImpl->mpSelEngine->SelMouseMove( rMouseEvent );
808 }
809 
810 void TextView::Command( const CommandEvent& rCEvt )
811 {
812     mpImpl->mpTextEngine->CheckIdleFormatter();    // for fast typing and MouseButtonDown
813     mpImpl->mpTextEngine->SetActiveView( this );
814 
815     if ( rCEvt.GetCommand() == CommandEventId::StartExtTextInput )
816     {
817         DeleteSelected();
818         TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes()[ GetSelection().GetEnd().GetPara() ].get();
819         mpImpl->mpTextEngine->mpIMEInfos = std::make_unique<TEIMEInfos>( GetSelection().GetEnd(), pNode->GetText().copy( GetSelection().GetEnd().GetIndex() ) );
820         mpImpl->mpTextEngine->mpIMEInfos->bWasCursorOverwrite = !IsInsertMode();
821     }
822     else if ( rCEvt.GetCommand() == CommandEventId::EndExtTextInput )
823     {
824         SAL_WARN_IF( !mpImpl->mpTextEngine->mpIMEInfos, "vcl", "CommandEventId::EndExtTextInput => No Start ?" );
825         if( mpImpl->mpTextEngine->mpIMEInfos )
826         {
827             TEParaPortion* pPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( mpImpl->mpTextEngine->mpIMEInfos->aPos.GetPara() );
828             pPortion->MarkSelectionInvalid( mpImpl->mpTextEngine->mpIMEInfos->aPos.GetIndex() );
829 
830             bool bInsertMode = !mpImpl->mpTextEngine->mpIMEInfos->bWasCursorOverwrite;
831 
832             mpImpl->mpTextEngine->mpIMEInfos.reset();
833 
834             mpImpl->mpTextEngine->TextModified();
835             mpImpl->mpTextEngine->FormatAndUpdate( this );
836 
837             SetInsertMode( bInsertMode );
838 
839             if ( mpImpl->mpTextEngine->IsModified() )
840                 mpImpl->mpTextEngine->Broadcast( TextHint( SfxHintId::TextModified ) );
841         }
842     }
843     else if ( rCEvt.GetCommand() == CommandEventId::ExtTextInput )
844     {
845         SAL_WARN_IF( !mpImpl->mpTextEngine->mpIMEInfos, "vcl", "CommandEventId::ExtTextInput => No Start ?" );
846         if( mpImpl->mpTextEngine->mpIMEInfos )
847         {
848             const CommandExtTextInputData* pData = rCEvt.GetExtTextInputData();
849 
850             if ( !pData->IsOnlyCursorChanged() )
851             {
852                 TextSelection aSelect( mpImpl->mpTextEngine->mpIMEInfos->aPos );
853                 aSelect.GetEnd().GetIndex() += mpImpl->mpTextEngine->mpIMEInfos->nLen;
854                 aSelect = mpImpl->mpTextEngine->ImpDeleteText( aSelect );
855                 aSelect = mpImpl->mpTextEngine->ImpInsertText( aSelect, pData->GetText() );
856 
857                 if ( mpImpl->mpTextEngine->mpIMEInfos->bWasCursorOverwrite )
858                 {
859                     const sal_Int32 nOldIMETextLen = mpImpl->mpTextEngine->mpIMEInfos->nLen;
860                     const sal_Int32 nNewIMETextLen = pData->GetText().getLength();
861 
862                     if ( ( nOldIMETextLen > nNewIMETextLen ) &&
863                          ( nNewIMETextLen < mpImpl->mpTextEngine->mpIMEInfos->aOldTextAfterStartPos.getLength() ) )
864                     {
865                         // restore old characters
866                         sal_Int32 nRestore = nOldIMETextLen - nNewIMETextLen;
867                         TextPaM aPaM( mpImpl->mpTextEngine->mpIMEInfos->aPos );
868                         aPaM.GetIndex() += nNewIMETextLen;
869                         mpImpl->mpTextEngine->ImpInsertText( aPaM, mpImpl->mpTextEngine->mpIMEInfos->aOldTextAfterStartPos.copy( nNewIMETextLen, nRestore ) );
870                     }
871                     else if ( ( nOldIMETextLen < nNewIMETextLen ) &&
872                               ( nOldIMETextLen < mpImpl->mpTextEngine->mpIMEInfos->aOldTextAfterStartPos.getLength() ) )
873                     {
874                         // overwrite
875                         const sal_Int32 nOverwrite = std::min( nNewIMETextLen, mpImpl->mpTextEngine->mpIMEInfos->aOldTextAfterStartPos.getLength() ) - nOldIMETextLen;
876                         SAL_WARN_IF( !nOverwrite || (nOverwrite >= 0xFF00), "vcl", "IME Overwrite?!" );
877                         TextPaM aPaM( mpImpl->mpTextEngine->mpIMEInfos->aPos );
878                         aPaM.GetIndex() += nNewIMETextLen;
879                         TextSelection aSel( aPaM );
880                         aSel.GetEnd().GetIndex() += nOverwrite;
881                         mpImpl->mpTextEngine->ImpDeleteText( aSel );
882                     }
883                 }
884 
885                 if ( pData->GetTextAttr() )
886                 {
887                     mpImpl->mpTextEngine->mpIMEInfos->CopyAttribs( pData->GetTextAttr(), pData->GetText().getLength() );
888                 }
889                 else
890                 {
891                     mpImpl->mpTextEngine->mpIMEInfos->DestroyAttribs();
892                 }
893 
894                 TEParaPortion* pPPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( mpImpl->mpTextEngine->mpIMEInfos->aPos.GetPara() );
895                 pPPortion->MarkSelectionInvalid( mpImpl->mpTextEngine->mpIMEInfos->aPos.GetIndex() );
896                 mpImpl->mpTextEngine->FormatAndUpdate( this );
897             }
898 
899             TextSelection aNewSel = TextPaM( mpImpl->mpTextEngine->mpIMEInfos->aPos.GetPara(), mpImpl->mpTextEngine->mpIMEInfos->aPos.GetIndex()+pData->GetCursorPos() );
900             SetSelection( aNewSel );
901             SetInsertMode( !pData->IsCursorOverwrite() );
902 
903             if ( pData->IsCursorVisible() )
904                 ShowCursor();
905             else
906                 HideCursor();
907         }
908     }
909     else if ( rCEvt.GetCommand() == CommandEventId::CursorPos )
910     {
911         if ( mpImpl->mpTextEngine->mpIMEInfos && mpImpl->mpTextEngine->mpIMEInfos->nLen )
912         {
913             TextPaM aPaM( GetSelection().GetEnd() );
914             tools::Rectangle aR1 = mpImpl->mpTextEngine->PaMtoEditCursor( aPaM );
915 
916             sal_Int32 nInputEnd = mpImpl->mpTextEngine->mpIMEInfos->aPos.GetIndex() + mpImpl->mpTextEngine->mpIMEInfos->nLen;
917 
918             if ( !mpImpl->mpTextEngine->IsFormatted() )
919                 mpImpl->mpTextEngine->FormatDoc();
920 
921             TEParaPortion* pParaPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( aPaM.GetPara() );
922             std::vector<TextLine>::size_type nLine = pParaPortion->GetLineNumber( aPaM.GetIndex(), true );
923             TextLine& rLine = pParaPortion->GetLines()[ nLine ];
924             if ( nInputEnd > rLine.GetEnd() )
925                 nInputEnd = rLine.GetEnd();
926             tools::Rectangle aR2 = mpImpl->mpTextEngine->PaMtoEditCursor( TextPaM( aPaM.GetPara(), nInputEnd ) );
927 
928             long nWidth = aR2.Left()-aR1.Right();
929             aR1.Move( -GetStartDocPos().X(), -GetStartDocPos().Y() );
930             GetWindow()->SetCursorRect( &aR1, nWidth );
931         }
932         else
933         {
934             GetWindow()->SetCursorRect();
935         }
936     }
937     else
938     {
939         mpImpl->mpSelEngine->Command( rCEvt );
940     }
941 }
942 
943 void TextView::ShowCursor( bool bGotoCursor, bool bForceVisCursor )
944 {
945     // this setting has more weight
946     if ( !mpImpl->mbAutoScroll )
947         bGotoCursor = false;
948     ImpShowCursor( bGotoCursor, bForceVisCursor, false );
949 }
950 
951 void TextView::HideCursor()
952 {
953     mpImpl->mpCursor->Hide();
954 }
955 
956 void TextView::Scroll( long ndX, long ndY )
957 {
958     SAL_WARN_IF( !mpImpl->mpTextEngine->IsFormatted(), "vcl", "Scroll: Not formatted!" );
959 
960     if ( !ndX && !ndY )
961         return;
962 
963     Point aNewStartPos( mpImpl->maStartDocPos );
964 
965     // Vertical:
966     aNewStartPos.AdjustY( -ndY );
967     if ( aNewStartPos.Y() < 0 )
968         aNewStartPos.setY( 0 );
969 
970     // Horizontal:
971     aNewStartPos.AdjustX( -ndX );
972     if ( aNewStartPos.X() < 0 )
973         aNewStartPos.setX( 0 );
974 
975     long nDiffX = mpImpl->maStartDocPos.X() - aNewStartPos.X();
976     long nDiffY = mpImpl->maStartDocPos.Y() - aNewStartPos.Y();
977 
978     if ( nDiffX || nDiffY )
979     {
980         bool bVisCursor = mpImpl->mpCursor->IsVisible();
981         mpImpl->mpCursor->Hide();
982         mpImpl->mpWindow->Update();
983         mpImpl->maStartDocPos = aNewStartPos;
984 
985         if ( mpImpl->mpTextEngine->IsRightToLeft() )
986             nDiffX = -nDiffX;
987         mpImpl->mpWindow->Scroll( nDiffX, nDiffY );
988         mpImpl->mpWindow->Update();
989         mpImpl->mpCursor->SetPos( mpImpl->mpCursor->GetPos() + Point( nDiffX, nDiffY ) );
990         if ( bVisCursor && !mpImpl->mbReadOnly )
991             mpImpl->mpCursor->Show();
992     }
993 
994     mpImpl->mpTextEngine->Broadcast( TextHint( SfxHintId::TextViewScrolled ) );
995 }
996 
997 void TextView::Undo()
998 {
999     mpImpl->mpTextEngine->SetActiveView( this );
1000     mpImpl->mpTextEngine->GetUndoManager().Undo();
1001 }
1002 
1003 void TextView::Redo()
1004 {
1005     mpImpl->mpTextEngine->SetActiveView( this );
1006     mpImpl->mpTextEngine->GetUndoManager().Redo();
1007 }
1008 
1009 void TextView::Cut()
1010 {
1011     mpImpl->mpTextEngine->UndoActionStart();
1012     Copy();
1013     DeleteSelected();
1014     mpImpl->mpTextEngine->UndoActionEnd();
1015 }
1016 
1017 void TextView::Copy( css::uno::Reference< css::datatransfer::clipboard::XClipboard > const & rxClipboard )
1018 {
1019     if ( rxClipboard.is() )
1020     {
1021         TETextDataObject* pDataObj = new TETextDataObject( GetSelected() );
1022 
1023         SolarMutexReleaser aReleaser;
1024 
1025         try
1026         {
1027             rxClipboard->setContents( pDataObj, nullptr );
1028 
1029             css::uno::Reference< css::datatransfer::clipboard::XFlushableClipboard > xFlushableClipboard( rxClipboard, css::uno::UNO_QUERY );
1030             if( xFlushableClipboard.is() )
1031                 xFlushableClipboard->flushClipboard();
1032         }
1033         catch( const css::uno::Exception& )
1034         {
1035         }
1036     }
1037 }
1038 
1039 void TextView::Copy()
1040 {
1041     css::uno::Reference<css::datatransfer::clipboard::XClipboard> aClipboard(GetWindow()->GetClipboard());
1042     Copy( aClipboard );
1043 }
1044 
1045 void TextView::Paste( css::uno::Reference< css::datatransfer::clipboard::XClipboard > const & rxClipboard )
1046 {
1047     if ( rxClipboard.is() )
1048     {
1049         css::uno::Reference< css::datatransfer::XTransferable > xDataObj;
1050 
1051         try
1052             {
1053                 SolarMutexReleaser aReleaser;
1054                 xDataObj = rxClipboard->getContents();
1055             }
1056         catch( const css::uno::Exception& )
1057             {
1058             }
1059 
1060         if ( xDataObj.is() )
1061         {
1062             css::datatransfer::DataFlavor aFlavor;
1063             SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor );
1064             if ( xDataObj->isDataFlavorSupported( aFlavor ) )
1065             {
1066                 try
1067                 {
1068                     css::uno::Any aData = xDataObj->getTransferData( aFlavor );
1069                     OUString aText;
1070                     aData >>= aText;
1071                     bool bWasTruncated = false;
1072                     if( mpImpl->mpTextEngine->GetMaxTextLen() != 0 )
1073                         bWasTruncated = ImplTruncateNewText( aText );
1074                     InsertText( aText );
1075                     mpImpl->mpTextEngine->Broadcast( TextHint( SfxHintId::TextModified ) );
1076 
1077                     if( bWasTruncated )
1078                         Edit::ShowTruncationWarning(mpImpl->mpWindow->GetFrameWeld());
1079                 }
1080                 catch( const css::datatransfer::UnsupportedFlavorException& )
1081                 {
1082                 }
1083             }
1084         }
1085     }
1086 }
1087 
1088 void TextView::Paste()
1089 {
1090     css::uno::Reference<css::datatransfer::clipboard::XClipboard> aClipboard(GetWindow()->GetClipboard());
1091     Paste( aClipboard );
1092 }
1093 
1094 OUString TextView::GetSelected()
1095 {
1096     return GetSelected( GetSystemLineEnd() );
1097 }
1098 
1099 OUString TextView::GetSelected( LineEnd aSeparator )
1100 {
1101     return mpImpl->mpTextEngine->GetText( mpImpl->maSelection, aSeparator );
1102 }
1103 
1104 void TextView::SetInsertMode( bool bInsert )
1105 {
1106     if ( mpImpl->mbInsertMode != bInsert )
1107     {
1108         mpImpl->mbInsertMode = bInsert;
1109         ShowCursor( mpImpl->mbAutoScroll, false );
1110     }
1111 }
1112 
1113 void TextView::SetReadOnly( bool bReadOnly )
1114 {
1115     if ( mpImpl->mbReadOnly != bReadOnly )
1116     {
1117         mpImpl->mbReadOnly = bReadOnly;
1118         if ( !mpImpl->mbReadOnly )
1119             ShowCursor( mpImpl->mbAutoScroll, false );
1120         else
1121             HideCursor();
1122 
1123         GetWindow()->SetInputContext( InputContext( mpImpl->mpTextEngine->GetFont(), bReadOnly ? InputContextFlags::Text|InputContextFlags::ExtText : InputContextFlags::NONE ) );
1124     }
1125 }
1126 
1127 TextSelection const & TextView::ImpMoveCursor( const KeyEvent& rKeyEvent )
1128 {
1129     // normally only needed for Up/Down; but who cares
1130     mpImpl->mpTextEngine->CheckIdleFormatter();
1131 
1132     TextPaM aPaM( mpImpl->maSelection.GetEnd() );
1133     TextPaM aOldEnd( aPaM );
1134 
1135     TextDirectionality eTextDirection = TextDirectionality::LeftToRight_TopToBottom;
1136     if ( mpImpl->mpTextEngine->IsRightToLeft() )
1137         eTextDirection = TextDirectionality::RightToLeft_TopToBottom;
1138 
1139     KeyEvent aTranslatedKeyEvent = rKeyEvent.LogicalTextDirectionality( eTextDirection );
1140 
1141     bool bCtrl = aTranslatedKeyEvent.GetKeyCode().IsMod1();
1142     sal_uInt16 nCode = aTranslatedKeyEvent.GetKeyCode().GetCode();
1143 
1144     bool bSelect = aTranslatedKeyEvent.GetKeyCode().IsShift();
1145     switch ( nCode )
1146     {
1147         case KEY_UP:        aPaM = CursorUp( aPaM );
1148                             break;
1149         case KEY_DOWN:      aPaM = CursorDown( aPaM );
1150                             break;
1151         case KEY_HOME:      aPaM = bCtrl ? CursorStartOfDoc() : CursorStartOfLine( aPaM );
1152                             break;
1153         case KEY_END:       aPaM = bCtrl ? CursorEndOfDoc() : CursorEndOfLine( aPaM );
1154                             break;
1155         case KEY_PAGEUP:    aPaM = bCtrl ? CursorStartOfDoc() : PageUp( aPaM );
1156                             break;
1157         case KEY_PAGEDOWN:  aPaM = bCtrl ? CursorEndOfDoc() : PageDown( aPaM );
1158                             break;
1159         case KEY_LEFT:      aPaM = bCtrl ? CursorWordLeft( aPaM ) : CursorLeft( aPaM, aTranslatedKeyEvent.GetKeyCode().IsMod2() ? sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCHARACTER) : sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCELL) );
1160                             break;
1161         case KEY_RIGHT:     aPaM = bCtrl ? CursorWordRight( aPaM ) : CursorRight( aPaM, aTranslatedKeyEvent.GetKeyCode().IsMod2() ? sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCHARACTER) : sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCELL) );
1162                             break;
1163         case css::awt::Key::SELECT_WORD_FORWARD:
1164                             bSelect = true;
1165                             [[fallthrough]];
1166         case css::awt::Key::MOVE_WORD_FORWARD:
1167                             aPaM = CursorWordRight( aPaM );
1168                             break;
1169         case css::awt::Key::SELECT_WORD_BACKWARD:
1170                             bSelect = true;
1171                             [[fallthrough]];
1172         case css::awt::Key::MOVE_WORD_BACKWARD:
1173                             aPaM = CursorWordLeft( aPaM );
1174                             break;
1175         case css::awt::Key::SELECT_TO_BEGIN_OF_LINE:
1176                             bSelect = true;
1177                             [[fallthrough]];
1178         case css::awt::Key::MOVE_TO_BEGIN_OF_LINE:
1179                             aPaM = CursorStartOfLine( aPaM );
1180                             break;
1181         case css::awt::Key::SELECT_TO_END_OF_LINE:
1182                             bSelect = true;
1183                             [[fallthrough]];
1184         case css::awt::Key::MOVE_TO_END_OF_LINE:
1185                             aPaM = CursorEndOfLine( aPaM );
1186                             break;
1187         case css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH:
1188                             bSelect = true;
1189                             [[fallthrough]];
1190         case css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH:
1191                             aPaM = CursorStartOfParagraph( aPaM );
1192                             break;
1193         case css::awt::Key::SELECT_TO_END_OF_PARAGRAPH:
1194                             bSelect = true;
1195                             [[fallthrough]];
1196         case css::awt::Key::MOVE_TO_END_OF_PARAGRAPH:
1197                             aPaM = CursorEndOfParagraph( aPaM );
1198                             break;
1199         case css::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT:
1200                             bSelect = true;
1201                             [[fallthrough]];
1202         case css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT:
1203                             aPaM = CursorStartOfDoc();
1204                             break;
1205         case css::awt::Key::SELECT_TO_END_OF_DOCUMENT:
1206                             bSelect = true;
1207                             [[fallthrough]];
1208         case css::awt::Key::MOVE_TO_END_OF_DOCUMENT:
1209                             aPaM = CursorEndOfDoc();
1210                             break;
1211     }
1212 
1213     // might cause a CreateAnchor or Deselection all
1214     mpImpl->mpSelEngine->CursorPosChanging( bSelect, aTranslatedKeyEvent.GetKeyCode().IsMod1() );
1215 
1216     if ( aOldEnd != aPaM )
1217     {
1218         mpImpl->mpTextEngine->CursorMoved( aOldEnd.GetPara() );
1219 
1220         TextSelection aNewSelection( mpImpl->maSelection );
1221         aNewSelection.GetEnd() = aPaM;
1222         if ( bSelect )
1223         {
1224             // extend the selection
1225             ImpSetSelection( aNewSelection );
1226             ShowSelection( TextSelection( aOldEnd, aPaM ) );
1227         }
1228         else
1229         {
1230             aNewSelection.GetStart() = aPaM;
1231             ImpSetSelection( aNewSelection );
1232         }
1233     }
1234 
1235     return mpImpl->maSelection;
1236 }
1237 
1238 void TextView::InsertText( const OUString& rStr )
1239 {
1240     mpImpl->mpTextEngine->UndoActionStart();
1241 
1242     TextSelection aNewSel = mpImpl->mpTextEngine->ImpInsertText( mpImpl->maSelection, rStr );
1243 
1244     ImpSetSelection( aNewSel );
1245 
1246     mpImpl->mpTextEngine->UndoActionEnd();
1247 
1248     mpImpl->mpTextEngine->FormatAndUpdate( this );
1249 }
1250 
1251 TextPaM TextView::CursorLeft( const TextPaM& rPaM, sal_uInt16 nCharacterIteratorMode )
1252 {
1253     TextPaM aPaM( rPaM );
1254 
1255     if ( aPaM.GetIndex() )
1256     {
1257         TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes()[ aPaM.GetPara() ].get();
1258         css::uno::Reference < css::i18n::XBreakIterator > xBI = mpImpl->mpTextEngine->GetBreakIterator();
1259         sal_Int32 nCount = 1;
1260         aPaM.GetIndex() = xBI->previousCharacters( pNode->GetText(), aPaM.GetIndex(), mpImpl->mpTextEngine->GetLocale(), nCharacterIteratorMode, nCount, nCount );
1261     }
1262     else if ( aPaM.GetPara() )
1263     {
1264         aPaM.GetPara()--;
1265         TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes()[ aPaM.GetPara() ].get();
1266         aPaM.GetIndex() = pNode->GetText().getLength();
1267     }
1268     return aPaM;
1269 }
1270 
1271 TextPaM TextView::CursorRight( const TextPaM& rPaM, sal_uInt16 nCharacterIteratorMode )
1272 {
1273     TextPaM aPaM( rPaM );
1274 
1275     TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes()[ aPaM.GetPara() ].get();
1276     if ( aPaM.GetIndex() < pNode->GetText().getLength() )
1277     {
1278         css::uno::Reference < css::i18n::XBreakIterator > xBI = mpImpl->mpTextEngine->GetBreakIterator();
1279         sal_Int32 nCount = 1;
1280         aPaM.GetIndex() = xBI->nextCharacters( pNode->GetText(), aPaM.GetIndex(), mpImpl->mpTextEngine->GetLocale(), nCharacterIteratorMode, nCount, nCount );
1281     }
1282     else if ( aPaM.GetPara() < ( mpImpl->mpTextEngine->mpDoc->GetNodes().size()-1) )
1283     {
1284         aPaM.GetPara()++;
1285         aPaM.GetIndex() = 0;
1286     }
1287 
1288     return aPaM;
1289 }
1290 
1291 TextPaM TextView::CursorWordLeft( const TextPaM& rPaM )
1292 {
1293     TextPaM aPaM( rPaM );
1294 
1295     if ( aPaM.GetIndex() )
1296     {
1297         TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes()[ aPaM.GetPara() ].get();
1298         css::uno::Reference < css::i18n::XBreakIterator > xBI = mpImpl->mpTextEngine->GetBreakIterator();
1299         css::i18n::Boundary aBoundary = xBI->getWordBoundary( pNode->GetText(), rPaM.GetIndex(), mpImpl->mpTextEngine->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES, true );
1300         if ( aBoundary.startPos >= rPaM.GetIndex() )
1301             aBoundary = xBI->previousWord( pNode->GetText(), rPaM.GetIndex(), mpImpl->mpTextEngine->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES );
1302         aPaM.GetIndex() = ( aBoundary.startPos != -1 ) ? aBoundary.startPos : 0;
1303     }
1304     else if ( aPaM.GetPara() )
1305     {
1306         aPaM.GetPara()--;
1307         TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes()[ aPaM.GetPara() ].get();
1308         aPaM.GetIndex() = pNode->GetText().getLength();
1309     }
1310     return aPaM;
1311 }
1312 
1313 TextPaM TextView::CursorWordRight( const TextPaM& rPaM )
1314 {
1315     TextPaM aPaM( rPaM );
1316 
1317     TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes()[ aPaM.GetPara() ].get();
1318     if ( aPaM.GetIndex() < pNode->GetText().getLength() )
1319     {
1320         css::uno::Reference < css::i18n::XBreakIterator > xBI = mpImpl->mpTextEngine->GetBreakIterator();
1321         css::i18n::Boundary aBoundary = xBI->nextWord(  pNode->GetText(), aPaM.GetIndex(), mpImpl->mpTextEngine->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES );
1322         aPaM.GetIndex() = aBoundary.startPos;
1323     }
1324     else if ( aPaM.GetPara() < ( mpImpl->mpTextEngine->mpDoc->GetNodes().size()-1) )
1325     {
1326         aPaM.GetPara()++;
1327         aPaM.GetIndex() = 0;
1328     }
1329 
1330     return aPaM;
1331 }
1332 
1333 TextPaM TextView::ImpDelete( sal_uInt8 nMode, sal_uInt8 nDelMode )
1334 {
1335     if ( mpImpl->maSelection.HasRange() )  // only delete selection
1336         return mpImpl->mpTextEngine->ImpDeleteText( mpImpl->maSelection );
1337 
1338     TextPaM aStartPaM = mpImpl->maSelection.GetStart();
1339     TextPaM aEndPaM = aStartPaM;
1340     if ( nMode == DEL_LEFT )
1341     {
1342         if ( nDelMode == DELMODE_SIMPLE )
1343         {
1344             aEndPaM = CursorLeft( aEndPaM, sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCHARACTER) );
1345         }
1346         else if ( nDelMode == DELMODE_RESTOFWORD )
1347         {
1348             TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes()[  aEndPaM.GetPara() ].get();
1349             css::uno::Reference < css::i18n::XBreakIterator > xBI = mpImpl->mpTextEngine->GetBreakIterator();
1350             css::i18n::Boundary aBoundary = xBI->getWordBoundary( pNode->GetText(), mpImpl->maSelection.GetEnd().GetIndex(), mpImpl->mpTextEngine->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES, true );
1351             if ( aBoundary.startPos == mpImpl->maSelection.GetEnd().GetIndex() )
1352                 aBoundary = xBI->previousWord( pNode->GetText(), mpImpl->maSelection.GetEnd().GetIndex(), mpImpl->mpTextEngine->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES );
1353             // #i63506# startPos is -1 when the paragraph starts with a tab
1354             aEndPaM.GetIndex() = std::max<sal_Int32>(aBoundary.startPos, 0);
1355         }
1356         else    // DELMODE_RESTOFCONTENT
1357         {
1358             if ( aEndPaM.GetIndex() != 0 )
1359                 aEndPaM.GetIndex() = 0;
1360             else if ( aEndPaM.GetPara() )
1361             {
1362                 // previous paragraph
1363                 aEndPaM.GetPara()--;
1364                 aEndPaM.GetIndex() = 0;
1365             }
1366         }
1367     }
1368     else
1369     {
1370         if ( nDelMode == DELMODE_SIMPLE )
1371         {
1372             aEndPaM = CursorRight( aEndPaM, sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCELL) );
1373         }
1374         else if ( nDelMode == DELMODE_RESTOFWORD )
1375         {
1376             TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes()[  aEndPaM.GetPara() ].get();
1377             css::uno::Reference < css::i18n::XBreakIterator > xBI = mpImpl->mpTextEngine->GetBreakIterator();
1378             css::i18n::Boundary aBoundary = xBI->nextWord( pNode->GetText(), mpImpl->maSelection.GetEnd().GetIndex(), mpImpl->mpTextEngine->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES );
1379             aEndPaM.GetIndex() = aBoundary.startPos;
1380         }
1381         else    // DELMODE_RESTOFCONTENT
1382         {
1383             TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes()[ aEndPaM.GetPara() ].get();
1384             if ( aEndPaM.GetIndex() < pNode->GetText().getLength() )
1385                 aEndPaM.GetIndex() = pNode->GetText().getLength();
1386             else if ( aEndPaM.GetPara() < ( mpImpl->mpTextEngine->mpDoc->GetNodes().size() - 1 ) )
1387             {
1388                 // next paragraph
1389                 aEndPaM.GetPara()++;
1390                 TextNode* pNextNode = mpImpl->mpTextEngine->mpDoc->GetNodes()[ aEndPaM.GetPara() ].get();
1391                 aEndPaM.GetIndex() = pNextNode->GetText().getLength();
1392             }
1393         }
1394     }
1395 
1396     return mpImpl->mpTextEngine->ImpDeleteText( TextSelection( aStartPaM, aEndPaM ) );
1397 }
1398 
1399 TextPaM TextView::CursorUp( const TextPaM& rPaM )
1400 {
1401     TextPaM aPaM( rPaM );
1402 
1403     long nX;
1404     if ( mpImpl->mnTravelXPos == TRAVEL_X_DONTKNOW )
1405     {
1406         nX = mpImpl->mpTextEngine->GetEditCursor( rPaM, false ).Left();
1407         mpImpl->mnTravelXPos = static_cast<sal_uInt16>(nX)+1;
1408     }
1409     else
1410         nX = mpImpl->mnTravelXPos;
1411 
1412     TEParaPortion* pPPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( rPaM.GetPara() );
1413     std::vector<TextLine>::size_type nLine = pPPortion->GetLineNumber( rPaM.GetIndex(), false );
1414     if ( nLine )    // same paragraph
1415     {
1416         aPaM.GetIndex() = mpImpl->mpTextEngine->GetCharPos( rPaM.GetPara(), nLine-1, nX );
1417         // If we need to go to the end of a line that was wrapped automatically,
1418         // the cursor ends up at the beginning of the 2nd line
1419         // Problem: Last character of an automatically wrapped line = Cursor
1420         TextLine& rLine = pPPortion->GetLines()[ nLine - 1 ];
1421         if ( aPaM.GetIndex() && ( aPaM.GetIndex() == rLine.GetEnd() ) )
1422             --aPaM.GetIndex();
1423     }
1424     else if ( rPaM.GetPara() )  // previous paragraph
1425     {
1426         aPaM.GetPara()--;
1427         pPPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( aPaM.GetPara() );
1428         std::vector<TextLine>::size_type nL = pPPortion->GetLines().size() - 1;
1429         aPaM.GetIndex() = mpImpl->mpTextEngine->GetCharPos( aPaM.GetPara(), nL, nX+1 );
1430     }
1431 
1432     return aPaM;
1433 }
1434 
1435 TextPaM TextView::CursorDown( const TextPaM& rPaM )
1436 {
1437     TextPaM aPaM( rPaM );
1438 
1439     long nX;
1440     if ( mpImpl->mnTravelXPos == TRAVEL_X_DONTKNOW )
1441     {
1442         nX = mpImpl->mpTextEngine->GetEditCursor( rPaM, false ).Left();
1443         mpImpl->mnTravelXPos = static_cast<sal_uInt16>(nX)+1;
1444     }
1445     else
1446         nX = mpImpl->mnTravelXPos;
1447 
1448     TEParaPortion* pPPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( rPaM.GetPara() );
1449     std::vector<TextLine>::size_type nLine = pPPortion->GetLineNumber( rPaM.GetIndex(), false );
1450     if ( nLine < ( pPPortion->GetLines().size() - 1 ) )
1451     {
1452         aPaM.GetIndex() = mpImpl->mpTextEngine->GetCharPos( rPaM.GetPara(), nLine+1, nX );
1453 
1454         // special case CursorUp
1455         TextLine& rLine = pPPortion->GetLines()[ nLine + 1 ];
1456         if ( ( aPaM.GetIndex() == rLine.GetEnd() ) && ( aPaM.GetIndex() > rLine.GetStart() ) && aPaM.GetIndex() < pPPortion->GetNode()->GetText().getLength() )
1457             --aPaM.GetIndex();
1458     }
1459     else if ( rPaM.GetPara() < ( mpImpl->mpTextEngine->mpDoc->GetNodes().size() - 1 ) )   // next paragraph
1460     {
1461         aPaM.GetPara()++;
1462         pPPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( aPaM.GetPara() );
1463         aPaM.GetIndex() = mpImpl->mpTextEngine->GetCharPos( aPaM.GetPara(), 0, nX+1 );
1464         TextLine& rLine = pPPortion->GetLines().front();
1465         if ( ( aPaM.GetIndex() == rLine.GetEnd() ) && ( aPaM.GetIndex() > rLine.GetStart() ) && ( pPPortion->GetLines().size() > 1 ) )
1466             --aPaM.GetIndex();
1467     }
1468 
1469     return aPaM;
1470 }
1471 
1472 TextPaM TextView::CursorStartOfLine( const TextPaM& rPaM )
1473 {
1474     TextPaM aPaM( rPaM );
1475 
1476     TEParaPortion* pPPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( rPaM.GetPara() );
1477     std::vector<TextLine>::size_type nLine = pPPortion->GetLineNumber( aPaM.GetIndex(), false );
1478     TextLine& rLine = pPPortion->GetLines()[ nLine ];
1479     aPaM.GetIndex() = rLine.GetStart();
1480 
1481     return aPaM;
1482 }
1483 
1484 TextPaM TextView::CursorEndOfLine( const TextPaM& rPaM )
1485 {
1486     TextPaM aPaM( rPaM );
1487 
1488     TEParaPortion* pPPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( rPaM.GetPara() );
1489     std::vector<TextLine>::size_type nLine = pPPortion->GetLineNumber( aPaM.GetIndex(), false );
1490     TextLine& rLine = pPPortion->GetLines()[ nLine ];
1491     aPaM.GetIndex() = rLine.GetEnd();
1492 
1493     if ( rLine.GetEnd() > rLine.GetStart() )  // empty line
1494     {
1495         sal_Unicode cLastChar = pPPortion->GetNode()->GetText()[ aPaM.GetIndex()-1 ];
1496         if ( ( cLastChar == ' ' ) && ( aPaM.GetIndex() != pPPortion->GetNode()->GetText().getLength() ) )
1497         {
1498             // for a blank in an automatically-wrapped line it is better to stand before it,
1499             // as the user will intend to stand behind the prior word.
1500             // If there is a change, special case for Pos1 after End!
1501             --aPaM.GetIndex();
1502         }
1503     }
1504     return aPaM;
1505 }
1506 
1507 TextPaM TextView::CursorStartOfParagraph( const TextPaM& rPaM )
1508 {
1509     TextPaM aPaM( rPaM );
1510     aPaM.GetIndex() = 0;
1511     return aPaM;
1512 }
1513 
1514 TextPaM TextView::CursorEndOfParagraph( const TextPaM& rPaM )
1515 {
1516     TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes()[ rPaM.GetPara() ].get();
1517     TextPaM aPaM( rPaM );
1518     aPaM.GetIndex() = pNode->GetText().getLength();
1519     return aPaM;
1520 }
1521 
1522 TextPaM TextView::CursorStartOfDoc()
1523 {
1524     TextPaM aPaM( 0, 0 );
1525     return aPaM;
1526 }
1527 
1528 TextPaM TextView::CursorEndOfDoc()
1529 {
1530     const sal_uInt32 nNode = static_cast<sal_uInt32>(mpImpl->mpTextEngine->mpDoc->GetNodes().size() - 1);
1531     TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes()[ nNode ].get();
1532     TextPaM aPaM( nNode, pNode->GetText().getLength() );
1533     return aPaM;
1534 }
1535 
1536 TextPaM TextView::PageUp( const TextPaM& rPaM )
1537 {
1538     tools::Rectangle aRect = mpImpl->mpTextEngine->PaMtoEditCursor( rPaM );
1539     Point aTopLeft = aRect.TopLeft();
1540     aTopLeft.AdjustY( -(mpImpl->mpWindow->GetOutputSizePixel().Height() * 9/10) );
1541     aTopLeft.AdjustX(1 );
1542     if ( aTopLeft.Y() < 0 )
1543         aTopLeft.setY( 0 );
1544 
1545     TextPaM aPaM = mpImpl->mpTextEngine->GetPaM( aTopLeft );
1546     return aPaM;
1547 }
1548 
1549 TextPaM TextView::PageDown( const TextPaM& rPaM )
1550 {
1551     tools::Rectangle aRect = mpImpl->mpTextEngine->PaMtoEditCursor( rPaM );
1552     Point aBottomRight = aRect.BottomRight();
1553     aBottomRight.AdjustY(mpImpl->mpWindow->GetOutputSizePixel().Height() * 9/10 );
1554     aBottomRight.AdjustX(1 );
1555     long nHeight = mpImpl->mpTextEngine->GetTextHeight();
1556     if ( aBottomRight.Y() > nHeight )
1557         aBottomRight.setY( nHeight-1 );
1558 
1559     TextPaM aPaM = mpImpl->mpTextEngine->GetPaM( aBottomRight );
1560     return aPaM;
1561 }
1562 
1563 void TextView::ImpShowCursor( bool bGotoCursor, bool bForceVisCursor, bool bSpecial )
1564 {
1565     if ( mpImpl->mpTextEngine->IsFormatting() )
1566         return;
1567     if ( !mpImpl->mpTextEngine->GetUpdateMode() )
1568         return;
1569     if ( mpImpl->mpTextEngine->IsInUndo() )
1570         return;
1571 
1572     mpImpl->mpTextEngine->CheckIdleFormatter();
1573     if ( !mpImpl->mpTextEngine->IsFormatted() )
1574         mpImpl->mpTextEngine->FormatAndUpdate( this );
1575 
1576     TextPaM aPaM( mpImpl->maSelection.GetEnd() );
1577     tools::Rectangle aEditCursor = mpImpl->mpTextEngine->PaMtoEditCursor( aPaM, bSpecial );
1578 
1579     // Remember that we placed the cursor behind the last character of a line
1580     mpImpl->mbCursorAtEndOfLine = false;
1581     if( bSpecial )
1582     {
1583         TEParaPortion* pParaPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( aPaM.GetPara() );
1584         mpImpl->mbCursorAtEndOfLine =
1585             pParaPortion->GetLineNumber( aPaM.GetIndex(), true ) != pParaPortion->GetLineNumber( aPaM.GetIndex(), false );
1586     }
1587 
1588     if ( !IsInsertMode() && !mpImpl->maSelection.HasRange() )
1589     {
1590         TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes()[ aPaM.GetPara() ].get();
1591         if ( !pNode->GetText().isEmpty() && ( aPaM.GetIndex() < pNode->GetText().getLength() ) )
1592         {
1593             // If we are behind a portion, and the next portion has other direction, we must change position...
1594             aEditCursor.SetLeft( mpImpl->mpTextEngine->GetEditCursor( aPaM, false, true ).Left() );
1595             aEditCursor.SetRight( aEditCursor.Left() );
1596 
1597             TEParaPortion* pParaPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( aPaM.GetPara() );
1598 
1599             sal_Int32 nTextPortionStart = 0;
1600             std::size_t nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nTextPortionStart, true );
1601             TETextPortion* pTextPortion = pParaPortion->GetTextPortions()[ nTextPortion ];
1602             if ( pTextPortion->GetKind() == PORTIONKIND_TAB )
1603             {
1604                 aEditCursor.AdjustRight(pTextPortion->GetWidth() );
1605             }
1606             else
1607             {
1608                 TextPaM aNext = CursorRight( TextPaM( aPaM.GetPara(), aPaM.GetIndex() ), sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCELL) );
1609                 aEditCursor.SetRight( mpImpl->mpTextEngine->GetEditCursor( aNext, true ).Left() );
1610             }
1611         }
1612     }
1613 
1614     Size aOutSz = mpImpl->mpWindow->GetOutputSizePixel();
1615     if ( aEditCursor.GetHeight() > aOutSz.Height() )
1616         aEditCursor.SetBottom( aEditCursor.Top() + aOutSz.Height() - 1 );
1617 
1618     aEditCursor.AdjustLeft( -1 );
1619 
1620     if ( bGotoCursor
1621         // #i81283# protect maStartDocPos against initialization problems
1622         && aOutSz.Width() && aOutSz.Height()
1623     )
1624     {
1625         long nVisStartY = mpImpl->maStartDocPos.Y();
1626         long nVisEndY = mpImpl->maStartDocPos.Y() + aOutSz.Height();
1627         long nVisStartX = mpImpl->maStartDocPos.X();
1628         long nVisEndX = mpImpl->maStartDocPos.X() + aOutSz.Width();
1629         long nMoreX = aOutSz.Width() / 4;
1630 
1631         Point aNewStartPos( mpImpl->maStartDocPos );
1632 
1633         if ( aEditCursor.Bottom() > nVisEndY )
1634         {
1635             aNewStartPos.AdjustY( aEditCursor.Bottom() - nVisEndY);
1636         }
1637         else if ( aEditCursor.Top() < nVisStartY )
1638         {
1639             aNewStartPos.AdjustY( -( nVisStartY - aEditCursor.Top() ) );
1640         }
1641 
1642         if ( aEditCursor.Right() >= nVisEndX )
1643         {
1644             aNewStartPos.AdjustX( aEditCursor.Right() - nVisEndX );
1645 
1646             // do you want some more?
1647             aNewStartPos.AdjustX(nMoreX );
1648         }
1649         else if ( aEditCursor.Left() <= nVisStartX )
1650         {
1651             aNewStartPos.AdjustX( -( nVisStartX - aEditCursor.Left() ) );
1652 
1653             // do you want some more?
1654             aNewStartPos.AdjustX( -nMoreX );
1655         }
1656 
1657         // X can be wrong for the 'some more' above:
1658 //      sal_uLong nMaxTextWidth = mpImpl->mpTextEngine->GetMaxTextWidth();
1659 //      if ( !nMaxTextWidth || ( nMaxTextWidth > 0x7FFFFFFF ) )
1660 //          nMaxTextWidth = 0x7FFFFFFF;
1661 //      long nMaxX = (long)nMaxTextWidth - aOutSz.Width();
1662         long nMaxX = mpImpl->mpTextEngine->CalcTextWidth() - aOutSz.Width();
1663         if ( nMaxX < 0 )
1664             nMaxX = 0;
1665 
1666         if ( aNewStartPos.X() < 0 )
1667             aNewStartPos.setX( 0 );
1668         else if ( aNewStartPos.X() > nMaxX )
1669             aNewStartPos.setX( nMaxX );
1670 
1671         // Y should not be further down than needed
1672         long nYMax = mpImpl->mpTextEngine->GetTextHeight() - aOutSz.Height();
1673         if ( nYMax < 0 )
1674             nYMax = 0;
1675         if ( aNewStartPos.Y() > nYMax )
1676             aNewStartPos.setY( nYMax );
1677 
1678         if ( aNewStartPos != mpImpl->maStartDocPos )
1679             Scroll( -(aNewStartPos.X() - mpImpl->maStartDocPos.X()), -(aNewStartPos.Y() - mpImpl->maStartDocPos.Y()) );
1680     }
1681 
1682     if ( aEditCursor.Right() < aEditCursor.Left() )
1683     {
1684         long n = aEditCursor.Left();
1685         aEditCursor.SetLeft( aEditCursor.Right() );
1686         aEditCursor.SetRight( n );
1687     }
1688 
1689     Point aPoint( GetWindowPos( !mpImpl->mpTextEngine->IsRightToLeft() ? aEditCursor.TopLeft() : aEditCursor.TopRight() ) );
1690     mpImpl->mpCursor->SetPos( aPoint );
1691     mpImpl->mpCursor->SetSize( aEditCursor.GetSize() );
1692     if ( bForceVisCursor && mpImpl->mbCursorEnabled )
1693         mpImpl->mpCursor->Show();
1694 }
1695 
1696 void TextView::SetCursorAtPoint( const Point& rPosPixel )
1697 {
1698     mpImpl->mpTextEngine->CheckIdleFormatter();
1699 
1700     Point aDocPos = GetDocPos( rPosPixel );
1701 
1702     TextPaM aPaM = mpImpl->mpTextEngine->GetPaM( aDocPos );
1703 
1704     // aTmpNewSel: Diff between old and new; not the new selection
1705     TextSelection aTmpNewSel( mpImpl->maSelection.GetEnd(), aPaM );
1706     TextSelection aNewSel( mpImpl->maSelection );
1707     aNewSel.GetEnd() = aPaM;
1708 
1709     if ( !mpImpl->mpSelEngine->HasAnchor() )
1710     {
1711         if ( mpImpl->maSelection.GetStart() != aPaM )
1712             mpImpl->mpTextEngine->CursorMoved( mpImpl->maSelection.GetStart().GetPara() );
1713         aNewSel.GetStart() = aPaM;
1714         ImpSetSelection( aNewSel );
1715     }
1716     else
1717     {
1718         ImpSetSelection( aNewSel );
1719         ShowSelection( aTmpNewSel );
1720     }
1721 
1722     bool bForceCursor = !mpImpl->mpDDInfo; // && !mbInSelection
1723     ImpShowCursor( mpImpl->mbAutoScroll, bForceCursor, false );
1724 }
1725 
1726 bool TextView::IsSelectionAtPoint( const Point& rPosPixel )
1727 {
1728     Point aDocPos = GetDocPos( rPosPixel );
1729     TextPaM aPaM = mpImpl->mpTextEngine->GetPaM( aDocPos );
1730     // BeginDrag is only called, however, if IsSelectionAtPoint()
1731     // Problem: IsSelectionAtPoint is not called by Command()
1732     // if before MBDown returned false.
1733     return IsInSelection( aPaM );
1734 }
1735 
1736 bool TextView::IsInSelection( const TextPaM& rPaM )
1737 {
1738     TextSelection aSel = mpImpl->maSelection;
1739     aSel.Justify();
1740 
1741     const sal_uInt32 nStartNode = aSel.GetStart().GetPara();
1742     const sal_uInt32 nEndNode = aSel.GetEnd().GetPara();
1743     const sal_uInt32 nCurNode = rPaM.GetPara();
1744 
1745     if ( ( nCurNode > nStartNode ) && ( nCurNode < nEndNode ) )
1746         return true;
1747 
1748     if ( nStartNode == nEndNode )
1749     {
1750         if ( nCurNode == nStartNode )
1751             if ( ( rPaM.GetIndex() >= aSel.GetStart().GetIndex() ) && ( rPaM.GetIndex() < aSel.GetEnd().GetIndex() ) )
1752                 return true;
1753     }
1754     else if ( ( nCurNode == nStartNode ) && ( rPaM.GetIndex() >= aSel.GetStart().GetIndex() ) )
1755         return true;
1756     else if ( ( nCurNode == nEndNode ) && ( rPaM.GetIndex() < aSel.GetEnd().GetIndex() ) )
1757         return true;
1758 
1759     return false;
1760 }
1761 
1762 void TextView::ImpHideDDCursor()
1763 {
1764     if ( mpImpl->mpDDInfo && mpImpl->mpDDInfo->mbVisCursor )
1765     {
1766         mpImpl->mpDDInfo->maCursor.Hide();
1767         mpImpl->mpDDInfo->mbVisCursor = false;
1768     }
1769 }
1770 
1771 void TextView::ImpShowDDCursor()
1772 {
1773     if ( !mpImpl->mpDDInfo->mbVisCursor )
1774     {
1775         tools::Rectangle aCursor = mpImpl->mpTextEngine->PaMtoEditCursor( mpImpl->mpDDInfo->maDropPos, true );
1776         aCursor.AdjustRight( 1 );
1777         aCursor.SetPos( GetWindowPos( aCursor.TopLeft() ) );
1778 
1779         mpImpl->mpDDInfo->maCursor.SetWindow( mpImpl->mpWindow );
1780         mpImpl->mpDDInfo->maCursor.SetPos( aCursor.TopLeft() );
1781         mpImpl->mpDDInfo->maCursor.SetSize( aCursor.GetSize() );
1782         mpImpl->mpDDInfo->maCursor.Show();
1783         mpImpl->mpDDInfo->mbVisCursor = true;
1784     }
1785 }
1786 
1787 void TextView::SetPaintSelection( bool bPaint )
1788 {
1789     if ( bPaint != mpImpl->mbPaintSelection )
1790     {
1791         mpImpl->mbPaintSelection = bPaint;
1792         ShowSelection( mpImpl->maSelection );
1793     }
1794 }
1795 
1796 void TextView::Read( SvStream& rInput )
1797 {
1798     mpImpl->mpTextEngine->Read( rInput, &mpImpl->maSelection );
1799     ShowCursor();
1800 }
1801 
1802 bool TextView::ImplTruncateNewText( OUString& rNewText ) const
1803 {
1804     bool bTruncated = false;
1805 
1806     const sal_Int32 nMaxLen = mpImpl->mpTextEngine->GetMaxTextLen();
1807     // 0 means unlimited
1808     if( nMaxLen != 0 )
1809     {
1810         const sal_Int32 nCurLen = mpImpl->mpTextEngine->GetTextLen();
1811 
1812         const sal_Int32 nNewLen = rNewText.getLength();
1813         if ( nCurLen + nNewLen > nMaxLen )
1814         {
1815             // see how much text will be replaced
1816             const sal_Int32 nSelLen = mpImpl->mpTextEngine->GetTextLen( mpImpl->maSelection );
1817             if ( nCurLen + nNewLen - nSelLen > nMaxLen )
1818             {
1819                 const sal_Int32 nTruncatedLen = nMaxLen - (nCurLen - nSelLen);
1820                 rNewText = rNewText.copy( 0, nTruncatedLen );
1821                 bTruncated = true;
1822             }
1823         }
1824     }
1825     return bTruncated;
1826 }
1827 
1828 bool TextView::ImplCheckTextLen( const OUString& rNewText )
1829 {
1830     bool bOK = true;
1831     if ( mpImpl->mpTextEngine->GetMaxTextLen() )
1832     {
1833         sal_Int32 n = mpImpl->mpTextEngine->GetTextLen() + rNewText.getLength();
1834         if ( n > mpImpl->mpTextEngine->GetMaxTextLen() )
1835         {
1836             // calculate how much text is being deleted
1837             n -= mpImpl->mpTextEngine->GetTextLen( mpImpl->maSelection );
1838             if ( n > mpImpl->mpTextEngine->GetMaxTextLen() )
1839                 bOK = false;
1840         }
1841     }
1842     return bOK;
1843 }
1844 
1845 void TextView::dragGestureRecognized( const css::datatransfer::dnd::DragGestureEvent& rDGE )
1846 {
1847     if ( mpImpl->mbClickedInSelection )
1848     {
1849         SolarMutexGuard aVclGuard;
1850 
1851         SAL_WARN_IF( !mpImpl->maSelection.HasRange(), "vcl", "TextView::dragGestureRecognized: mpImpl->mbClickedInSelection, but no selection?" );
1852 
1853         mpImpl->mpDDInfo.reset(new TextDDInfo);
1854         mpImpl->mpDDInfo->mbStarterOfDD = true;
1855 
1856         TETextDataObject* pDataObj = new TETextDataObject( GetSelected() );
1857 
1858         mpImpl->mpCursor->Hide();
1859 
1860         sal_Int8 nActions = css::datatransfer::dnd::DNDConstants::ACTION_COPY;
1861         if ( !IsReadOnly() )
1862             nActions |= css::datatransfer::dnd::DNDConstants::ACTION_MOVE;
1863         rDGE.DragSource->startDrag( rDGE, nActions, 0 /*cursor*/, 0 /*image*/, pDataObj, mpImpl->mxDnDListener );
1864     }
1865 }
1866 
1867 void TextView::dragDropEnd( const css::datatransfer::dnd::DragSourceDropEvent& )
1868 {
1869     ImpHideDDCursor();
1870     mpImpl->mpDDInfo.reset();
1871 }
1872 
1873 void TextView::drop( const css::datatransfer::dnd::DropTargetDropEvent& rDTDE )
1874 {
1875     SolarMutexGuard aVclGuard;
1876 
1877     if ( !mpImpl->mbReadOnly && mpImpl->mpDDInfo )
1878     {
1879         ImpHideDDCursor();
1880 
1881         // Data for deleting after DROP_MOVE:
1882         TextSelection aPrevSel( mpImpl->maSelection );
1883         aPrevSel.Justify();
1884         const sal_uInt32 nPrevParaCount = mpImpl->mpTextEngine->GetParagraphCount();
1885         const sal_Int32 nPrevStartParaLen = mpImpl->mpTextEngine->GetTextLen( aPrevSel.GetStart().GetPara() );
1886 
1887         bool bStarterOfDD = false;
1888         for ( sal_uInt16 nView = mpImpl->mpTextEngine->GetViewCount(); nView && !bStarterOfDD; )
1889             bStarterOfDD = mpImpl->mpTextEngine->GetView( --nView )->mpImpl->mpDDInfo && mpImpl->mpTextEngine->GetView( nView )->mpImpl->mpDDInfo->mbStarterOfDD;
1890 
1891         HideSelection();
1892         ImpSetSelection( mpImpl->mpDDInfo->maDropPos );
1893 
1894         mpImpl->mpTextEngine->UndoActionStart();
1895 
1896         OUString aText;
1897         css::uno::Reference< css::datatransfer::XTransferable > xDataObj = rDTDE.Transferable;
1898         if ( xDataObj.is() )
1899         {
1900             css::datatransfer::DataFlavor aFlavor;
1901             SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor );
1902             if ( xDataObj->isDataFlavorSupported( aFlavor ) )
1903             {
1904                 css::uno::Any aData = xDataObj->getTransferData( aFlavor );
1905                 OUString aOUString;
1906                 aData >>= aOUString;
1907                 aText = convertLineEnd(aOUString, LINEEND_LF);
1908             }
1909         }
1910 
1911         if ( !aText.isEmpty() && ( aText[ aText.getLength()-1 ] == LINE_SEP ) )
1912             aText = aText.copy(0, aText.getLength()-1);
1913 
1914         TextPaM aTempStart = mpImpl->maSelection.GetStart();
1915         if ( ImplCheckTextLen( aText ) )
1916             ImpSetSelection( mpImpl->mpTextEngine->ImpInsertText( mpImpl->mpDDInfo->maDropPos, aText ) );
1917         if(mpImpl->mbSupportProtectAttribute)
1918         {
1919             mpImpl->mpTextEngine->SetAttrib( TextAttribProtect(),
1920                 aTempStart.GetPara(),
1921                 aTempStart.GetIndex(),
1922                 mpImpl->maSelection.GetEnd().GetIndex(), false );
1923         }
1924 
1925         if ( aPrevSel.HasRange() &&
1926                 !mpImpl->mbSupportProtectAttribute && // don't remove currently selected element
1927                 (( rDTDE.DropAction & css::datatransfer::dnd::DNDConstants::ACTION_MOVE ) || !bStarterOfDD) )
1928         {
1929             // adjust selection if necessary
1930             if ( ( mpImpl->mpDDInfo->maDropPos.GetPara() < aPrevSel.GetStart().GetPara() ) ||
1931                  ( ( mpImpl->mpDDInfo->maDropPos.GetPara() == aPrevSel.GetStart().GetPara() )
1932                         && ( mpImpl->mpDDInfo->maDropPos.GetIndex() < aPrevSel.GetStart().GetIndex() ) ) )
1933             {
1934                 const sal_uInt32 nNewParasBeforeSelection =
1935                     mpImpl->mpTextEngine->GetParagraphCount() - nPrevParaCount;
1936 
1937                 aPrevSel.GetStart().GetPara() += nNewParasBeforeSelection;
1938                 aPrevSel.GetEnd().GetPara() += nNewParasBeforeSelection;
1939 
1940                 if ( mpImpl->mpDDInfo->maDropPos.GetPara() == aPrevSel.GetStart().GetPara() )
1941                 {
1942                     const sal_Int32 nNewChars =
1943                         mpImpl->mpTextEngine->GetTextLen( aPrevSel.GetStart().GetPara() ) - nPrevStartParaLen;
1944 
1945                     aPrevSel.GetStart().GetIndex() += nNewChars;
1946                     if ( aPrevSel.GetStart().GetPara() == aPrevSel.GetEnd().GetPara() )
1947                         aPrevSel.GetEnd().GetIndex() += nNewChars;
1948                 }
1949             }
1950             else
1951             {
1952                 // adjust current selection
1953                 TextPaM aPaM = mpImpl->maSelection.GetStart();
1954                 aPaM.GetPara() -= ( aPrevSel.GetEnd().GetPara() - aPrevSel.GetStart().GetPara() );
1955                 if ( aPrevSel.GetEnd().GetPara() == mpImpl->mpDDInfo->maDropPos.GetPara() )
1956                 {
1957                     aPaM.GetIndex() -= aPrevSel.GetEnd().GetIndex();
1958                     if ( aPrevSel.GetStart().GetPara() == mpImpl->mpDDInfo->maDropPos.GetPara() )
1959                         aPaM.GetIndex() += aPrevSel.GetStart().GetIndex();
1960                 }
1961                 ImpSetSelection( aPaM );
1962 
1963             }
1964             mpImpl->mpTextEngine->ImpDeleteText( aPrevSel );
1965         }
1966 
1967         mpImpl->mpTextEngine->UndoActionEnd();
1968 
1969         mpImpl->mpDDInfo.reset();
1970 
1971         mpImpl->mpTextEngine->FormatAndUpdate( this );
1972 
1973         mpImpl->mpTextEngine->Broadcast( TextHint( SfxHintId::TextModified ) );
1974     }
1975     rDTDE.Context->dropComplete( false/*bChanges*/ );
1976 }
1977 
1978 void TextView::dragEnter( const css::datatransfer::dnd::DropTargetDragEnterEvent& )
1979 {
1980 }
1981 
1982 void TextView::dragExit( const css::datatransfer::dnd::DropTargetEvent& )
1983 {
1984     SolarMutexGuard aVclGuard;
1985     ImpHideDDCursor();
1986 }
1987 
1988 void TextView::dragOver( const css::datatransfer::dnd::DropTargetDragEvent& rDTDE )
1989 {
1990     SolarMutexGuard aVclGuard;
1991 
1992     if (!mpImpl->mpDDInfo)
1993         mpImpl->mpDDInfo.reset(new TextDDInfo);
1994 
1995     TextPaM aPrevDropPos = mpImpl->mpDDInfo->maDropPos;
1996     Point aMousePos( rDTDE.LocationX, rDTDE.LocationY );
1997     Point aDocPos = GetDocPos( aMousePos );
1998     mpImpl->mpDDInfo->maDropPos = mpImpl->mpTextEngine->GetPaM( aDocPos );
1999 
2000     bool bProtected = false;
2001     if(mpImpl->mbSupportProtectAttribute)
2002     {
2003         const TextCharAttrib* pStartAttr = mpImpl->mpTextEngine->FindCharAttrib(
2004                     mpImpl->mpDDInfo->maDropPos,
2005                     TEXTATTR_PROTECTED );
2006         bProtected = pStartAttr != nullptr &&
2007                 pStartAttr->GetStart() != mpImpl->mpDDInfo->maDropPos.GetIndex() &&
2008                 pStartAttr->GetEnd() != mpImpl->mpDDInfo->maDropPos.GetIndex();
2009     }
2010     // Don't drop in selection or in read only engine
2011     if ( IsReadOnly() || IsInSelection( mpImpl->mpDDInfo->maDropPos ) || bProtected)
2012     {
2013         ImpHideDDCursor();
2014         rDTDE.Context->rejectDrag();
2015     }
2016     else
2017     {
2018         // delete old Cursor
2019         if ( !mpImpl->mpDDInfo->mbVisCursor || ( aPrevDropPos != mpImpl->mpDDInfo->maDropPos ) )
2020         {
2021             ImpHideDDCursor();
2022             ImpShowDDCursor();
2023         }
2024         rDTDE.Context->acceptDrag( rDTDE.DropAction );
2025     }
2026 }
2027 
2028 Point TextView::ImpGetOutputStartPos( const Point& rStartDocPos ) const
2029 {
2030     Point aStartPos( -rStartDocPos.X(), -rStartDocPos.Y() );
2031     if ( mpImpl->mpTextEngine->IsRightToLeft() )
2032     {
2033         Size aSz = mpImpl->mpWindow->GetOutputSizePixel();
2034         aStartPos.setX( rStartDocPos.X() + aSz.Width() - 1 ); // -1: Start is 0
2035     }
2036     return aStartPos;
2037 }
2038 
2039 Point TextView::GetDocPos( const Point& rWindowPos ) const
2040 {
2041     // Window Position => Document Position
2042 
2043     Point aPoint;
2044 
2045     aPoint.setY( rWindowPos.Y() + mpImpl->maStartDocPos.Y() );
2046 
2047     if ( !mpImpl->mpTextEngine->IsRightToLeft() )
2048     {
2049         aPoint.setX( rWindowPos.X() + mpImpl->maStartDocPos.X() );
2050     }
2051     else
2052     {
2053         Size aSz = mpImpl->mpWindow->GetOutputSizePixel();
2054         aPoint.setX( ( aSz.Width() - 1 ) - rWindowPos.X() + mpImpl->maStartDocPos.X() );
2055     }
2056 
2057     return aPoint;
2058 }
2059 
2060 Point TextView::GetWindowPos( const Point& rDocPos ) const
2061 {
2062     // Document Position => Window Position
2063 
2064     Point aPoint;
2065 
2066     aPoint.setY( rDocPos.Y() - mpImpl->maStartDocPos.Y() );
2067 
2068     if ( !mpImpl->mpTextEngine->IsRightToLeft() )
2069     {
2070         aPoint.setX( rDocPos.X() - mpImpl->maStartDocPos.X() );
2071     }
2072     else
2073     {
2074         Size aSz = mpImpl->mpWindow->GetOutputSizePixel();
2075         aPoint.setX( ( aSz.Width() - 1 ) - ( rDocPos.X() - mpImpl->maStartDocPos.X() ) );
2076     }
2077 
2078     return aPoint;
2079 }
2080 
2081 sal_Int32 TextView::GetLineNumberOfCursorInSelection() const
2082 {
2083  // PROGRESS
2084     sal_Int32 nLineNo = -1;
2085     if( mpImpl->mbCursorEnabled )
2086     {
2087         TextPaM aPaM = GetSelection().GetEnd();
2088         TEParaPortion* pPPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( aPaM.GetPara() );
2089         nLineNo = pPPortion->GetLineNumber( aPaM.GetIndex(), false );
2090             //TODO: std::vector<TextLine>::size_type -> sal_Int32!
2091         if( mpImpl->mbCursorAtEndOfLine )
2092             --nLineNo;
2093     }
2094     return nLineNo;
2095 }
2096 
2097 // (+) class TextSelFunctionSet
2098 
2099 TextSelFunctionSet::TextSelFunctionSet( TextView* pView )
2100 {
2101     mpView = pView;
2102 }
2103 
2104 void TextSelFunctionSet::BeginDrag()
2105 {
2106 }
2107 
2108 void TextSelFunctionSet::CreateAnchor()
2109 {
2110 //  TextSelection aSel( mpView->GetSelection() );
2111 //  aSel.GetStart() = aSel.GetEnd();
2112 //  mpView->SetSelection( aSel );
2113 
2114     // may not be followed by ShowCursor
2115     mpView->HideSelection();
2116     mpView->ImpSetSelection( mpView->mpImpl->maSelection.GetEnd() );
2117 }
2118 
2119 void TextSelFunctionSet::SetCursorAtPoint( const Point& rPointPixel, bool )
2120 {
2121     mpView->SetCursorAtPoint( rPointPixel );
2122 }
2123 
2124 bool TextSelFunctionSet::IsSelectionAtPoint( const Point& rPointPixel )
2125 {
2126     return mpView->IsSelectionAtPoint( rPointPixel );
2127 }
2128 
2129 void TextSelFunctionSet::DeselectAll()
2130 {
2131     CreateAnchor();
2132 }
2133 
2134 void TextSelFunctionSet::DeselectAtPoint( const Point& )
2135 {
2136     // only for multiple selection
2137 }
2138 
2139 void TextSelFunctionSet::DestroyAnchor()
2140 {
2141     // only for multiple selection
2142 }
2143 TextEngine*         TextView::GetTextEngine() const
2144 { return mpImpl->mpTextEngine; }
2145 vcl::Window*             TextView::GetWindow() const
2146 { return mpImpl->mpWindow; }
2147 void                TextView::EnableCursor( bool bEnable )
2148 { mpImpl->mbCursorEnabled = bEnable; }
2149 bool                TextView::IsCursorEnabled() const
2150 { return mpImpl->mbCursorEnabled; }
2151 void                TextView::SetStartDocPos( const Point& rPos )
2152 { mpImpl->maStartDocPos = rPos; }
2153 const Point&        TextView::GetStartDocPos() const
2154 { return mpImpl->maStartDocPos; }
2155 void                TextView::SetAutoIndentMode( bool bAutoIndent )
2156 { mpImpl->mbAutoIndent = bAutoIndent; }
2157 bool                TextView::IsReadOnly() const
2158 { return mpImpl->mbReadOnly; }
2159 void                TextView::SetAutoScroll( bool bAutoScroll )
2160 { mpImpl->mbAutoScroll = bAutoScroll; }
2161 bool                TextView::IsAutoScroll() const
2162 { return mpImpl->mbAutoScroll; }
2163 bool                TextView::HasSelection() const
2164 { return mpImpl->maSelection.HasRange(); }
2165 bool                TextView::IsInsertMode() const
2166 { return mpImpl->mbInsertMode; }
2167 
2168 void TextView::MatchGroup()
2169 {
2170     TextSelection aTmpSel( GetSelection() );
2171     aTmpSel.Justify();
2172     if ( ( aTmpSel.GetStart().GetPara() != aTmpSel.GetEnd().GetPara() ) ||
2173          ( ( aTmpSel.GetEnd().GetIndex() - aTmpSel.GetStart().GetIndex() ) > 1 ) )
2174     {
2175         return;
2176     }
2177 
2178     TextSelection aMatchSel = static_cast<ExtTextEngine*>(GetTextEngine())->MatchGroup( aTmpSel.GetStart() );
2179     if ( aMatchSel.HasRange() )
2180         SetSelection( aMatchSel );
2181 }
2182 
2183 void TextView::CenterPaM( const TextPaM& rPaM )
2184 {
2185     // Get textview size and the corresponding y-coordinates
2186     Size aOutSz = mpImpl->mpWindow->GetOutputSizePixel();
2187     long nVisStartY = mpImpl->maStartDocPos.Y();
2188     long nVisEndY = mpImpl->maStartDocPos.Y() + aOutSz.Height();
2189 
2190     // Retrieve the coordinates of the PaM
2191     tools::Rectangle aRect = mpImpl->mpTextEngine->PaMtoEditCursor(rPaM);
2192 
2193     // Recalculate the offset of the center y-coordinates and scroll
2194     Scroll(0, (nVisStartY + nVisEndY) / 2 - aRect.TopLeft().getY());
2195 }
2196 
2197 bool TextView::Search( const i18nutil::SearchOptions& rSearchOptions, bool bForward )
2198 {
2199     bool bFound = false;
2200     TextSelection aSel( GetSelection() );
2201     if ( static_cast<ExtTextEngine*>(GetTextEngine())->Search( aSel, rSearchOptions, bForward ) )
2202     {
2203         bFound = true;
2204         // First add the beginning of the word to the selection,
2205         // so that the whole word is in the visible region.
2206         SetSelection( aSel.GetStart() );
2207         ShowCursor( true, false );
2208     }
2209     else
2210     {
2211         aSel = GetSelection().GetEnd();
2212     }
2213 
2214     SetSelection( aSel );
2215     // tdf#49482: Move the start of the selection to the center of the textview
2216     if (bFound)
2217     {
2218         CenterPaM( aSel.GetStart() );
2219     }
2220     ShowCursor();
2221 
2222     return bFound;
2223 }
2224 
2225 sal_uInt16 TextView::Replace( const i18nutil::SearchOptions& rSearchOptions, bool bAll, bool bForward )
2226 {
2227     sal_uInt16 nFound = 0;
2228 
2229     if ( !bAll )
2230     {
2231         if ( GetSelection().HasRange() )
2232         {
2233             InsertText( rSearchOptions.replaceString );
2234             nFound = 1;
2235             Search( rSearchOptions, bForward ); // right away to the next
2236         }
2237         else
2238         {
2239             if( Search( rSearchOptions, bForward ) )
2240                 nFound = 1;
2241         }
2242     }
2243     else
2244     {
2245         // the writer replaces all, from beginning to end
2246 
2247         ExtTextEngine* pTextEngine = static_cast<ExtTextEngine*>(GetTextEngine());
2248 
2249         // HideSelection();
2250         TextSelection aSel;
2251 
2252         bool bSearchInSelection = (0 != (rSearchOptions.searchFlag & css::util::SearchFlags::REG_NOT_BEGINOFLINE) );
2253         if ( bSearchInSelection )
2254         {
2255             aSel = GetSelection();
2256             aSel.Justify();
2257         }
2258 
2259         TextSelection aSearchSel( aSel );
2260 
2261         bool bFound = pTextEngine->Search( aSel, rSearchOptions );
2262         if ( bFound )
2263             pTextEngine->UndoActionStart();
2264         while ( bFound )
2265         {
2266             nFound++;
2267 
2268             TextPaM aNewStart = pTextEngine->ImpInsertText( aSel, rSearchOptions.replaceString );
2269             aSel = aSearchSel;
2270             aSel.GetStart() = aNewStart;
2271             bFound = pTextEngine->Search( aSel, rSearchOptions );
2272         }
2273         if ( nFound )
2274         {
2275             SetSelection( aSel.GetStart() );
2276             pTextEngine->FormatAndUpdate( this );
2277             pTextEngine->UndoActionEnd();
2278         }
2279     }
2280     return nFound;
2281 }
2282 
2283 bool TextView::ImpIndentBlock( bool bRight )
2284 {
2285     bool bDone = false;
2286 
2287     TextSelection aSel = GetSelection();
2288     aSel.Justify();
2289 
2290     HideSelection();
2291     GetTextEngine()->UndoActionStart();
2292 
2293     const sal_uInt32 nStartPara = aSel.GetStart().GetPara();
2294     sal_uInt32 nEndPara = aSel.GetEnd().GetPara();
2295     if ( aSel.HasRange() && !aSel.GetEnd().GetIndex() )
2296     {
2297         nEndPara--; // do not indent
2298     }
2299 
2300     for ( sal_uInt32 nPara = nStartPara; nPara <= nEndPara; ++nPara )
2301     {
2302         if ( bRight )
2303         {
2304             // add tabs
2305             GetTextEngine()->ImpInsertText( TextPaM( nPara, 0 ), '\t' );
2306             bDone = true;
2307         }
2308         else
2309         {
2310             // remove Tabs/Blanks
2311             OUString aText = GetTextEngine()->GetText( nPara );
2312             if ( !aText.isEmpty() && (
2313                     ( aText[ 0 ] == '\t' ) ||
2314                     ( aText[ 0 ] == ' ' ) ) )
2315             {
2316                 GetTextEngine()->ImpDeleteText( TextSelection( TextPaM( nPara, 0 ), TextPaM( nPara, 1 ) ) );
2317                 bDone = true;
2318             }
2319         }
2320     }
2321 
2322     GetTextEngine()->UndoActionEnd();
2323 
2324     bool bRange = aSel.HasRange();
2325     if ( bRight )
2326     {
2327         ++aSel.GetStart().GetIndex();
2328         if ( bRange && ( aSel.GetEnd().GetPara() == nEndPara ) )
2329             ++aSel.GetEnd().GetIndex();
2330     }
2331     else
2332     {
2333         if ( aSel.GetStart().GetIndex() )
2334             --aSel.GetStart().GetIndex();
2335         if ( bRange && aSel.GetEnd().GetIndex() )
2336             --aSel.GetEnd().GetIndex();
2337     }
2338 
2339     ImpSetSelection( aSel );
2340     GetTextEngine()->FormatAndUpdate( this );
2341 
2342     return bDone;
2343 }
2344 
2345 bool TextView::IndentBlock()
2346 {
2347     return ImpIndentBlock( true );
2348 }
2349 
2350 bool TextView::UnindentBlock()
2351 {
2352     return ImpIndentBlock( false );
2353 }
2354 
2355 
2356 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2357