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