1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 #include <i18nutil/searchopt.hxx>
22 #include <o3tl/deleter.hxx>
23 #include <vcl/textview.hxx>
24 #include <vcl/texteng.hxx>
25 #include <vcl/settings.hxx>
26 #include "textdoc.hxx"
27 #include <vcl/textdata.hxx>
28 #include <vcl/xtextedt.hxx>
29 #include "textdat2.hxx"
30 #include <vcl/commandevent.hxx>
31 #include <vcl/inputctx.hxx>
33 #include <svl/undo.hxx>
34 #include <vcl/cursor.hxx>
35 #include <vcl/weld.hxx>
36 #include <vcl/window.hxx>
37 #include <vcl/svapp.hxx>
38 #include <tools/stream.hxx>
40 #include <sal/log.hxx>
41 #include <sot/formats.hxx>
43 #include <cppuhelper/weak.hxx>
44 #include <cppuhelper/queryinterface.hxx>
45 #include <com/sun/star/i18n/XBreakIterator.hpp>
46 #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
47 #include <com/sun/star/i18n/WordType.hpp>
48 #include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp>
49 #include <com/sun/star/datatransfer/XTransferable.hpp>
50 #include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
51 #include <com/sun/star/datatransfer/clipboard/XFlushableClipboard.hpp>
52 #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
53 #include <com/sun/star/datatransfer/dnd/XDragGestureRecognizer.hpp>
54 #include <com/sun/star/datatransfer/dnd/XDropTarget.hpp>
55 #include <com/sun/star/util/SearchFlags.hpp>
57 #include <vcl/toolkit/edit.hxx>
59 #include <sot/exchange.hxx>
64 TETextDataObject::TETextDataObject( const OUString
& rText
) : maText( rText
)
68 // css::uno::XInterface
69 css::uno::Any
TETextDataObject::queryInterface( const css::uno::Type
& rType
)
71 css::uno::Any aRet
= ::cppu::queryInterface( rType
, static_cast< css::datatransfer::XTransferable
* >(this) );
72 return (aRet
.hasValue() ? aRet
: OWeakObject::queryInterface( rType
));
75 // css::datatransfer::XTransferable
76 css::uno::Any
TETextDataObject::getTransferData( const css::datatransfer::DataFlavor
& rFlavor
)
80 SotClipboardFormatId nT
= SotExchange::GetFormat( rFlavor
);
81 if ( nT
== SotClipboardFormatId::STRING
)
85 else if ( nT
== SotClipboardFormatId::HTML
)
87 sal_uLong nLen
= GetHTMLStream().TellEnd();
88 GetHTMLStream().Seek(0);
90 css::uno::Sequence
< sal_Int8
> aSeq( nLen
);
91 memcpy( aSeq
.getArray(), GetHTMLStream().GetData(), nLen
);
96 throw css::datatransfer::UnsupportedFlavorException();
101 css::uno::Sequence
< css::datatransfer::DataFlavor
> TETextDataObject::getTransferDataFlavors( )
103 GetHTMLStream().Seek( STREAM_SEEK_TO_END
);
104 bool bHTML
= GetHTMLStream().Tell() > 0;
105 css::uno::Sequence
< css::datatransfer::DataFlavor
> aDataFlavors( bHTML
? 2 : 1 );
106 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING
, aDataFlavors
.getArray()[0] );
108 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::HTML
, aDataFlavors
.getArray()[1] );
112 sal_Bool
TETextDataObject::isDataFlavorSupported( const css::datatransfer::DataFlavor
& rFlavor
)
114 SotClipboardFormatId nT
= SotExchange::GetFormat( rFlavor
);
115 return ( nT
== SotClipboardFormatId::STRING
);
120 ExtTextEngine
* mpTextEngine
;
122 VclPtr
<vcl::Window
> mpWindow
;
123 TextSelection maSelection
;
126 std::unique_ptr
<vcl::Cursor
, o3tl::default_delete
<vcl::Cursor
>> mpCursor
;
128 std::unique_ptr
<TextDDInfo
, o3tl::default_delete
<TextDDInfo
>> mpDDInfo
;
130 std::unique_ptr
<SelectionEngine
> mpSelEngine
;
131 std::unique_ptr
<TextSelFunctionSet
> mpSelFuncSet
;
133 css::uno::Reference
< css::datatransfer::dnd::XDragSourceListener
> mxDnDListener
;
135 sal_uInt16 mnTravelXPos
;
137 bool mbAutoScroll
: 1;
138 bool mbInsertMode
: 1;
140 bool mbPaintSelection
: 1;
141 bool mbAutoIndent
: 1;
142 bool mbHighlightSelection
: 1;
143 bool mbCursorEnabled
: 1;
144 bool mbClickedInSelection
: 1;
145 bool mbCursorAtEndOfLine
;
148 TextView::TextView( ExtTextEngine
* pEng
, vcl::Window
* pWindow
) :
149 mpImpl(new ImpTextView
)
151 pWindow
->EnableRTL( false );
153 mpImpl
->mpWindow
= pWindow
;
154 mpImpl
->mpTextEngine
= pEng
;
156 mpImpl
->mbPaintSelection
= true;
157 mpImpl
->mbAutoScroll
= true;
158 mpImpl
->mbInsertMode
= true;
159 mpImpl
->mbReadOnly
= false;
160 mpImpl
->mbHighlightSelection
= false;
161 mpImpl
->mbAutoIndent
= false;
162 mpImpl
->mbCursorEnabled
= true;
163 mpImpl
->mbClickedInSelection
= false;
164 // mbInSelection = false;
166 mpImpl
->mnTravelXPos
= TRAVEL_X_DONTKNOW
;
168 mpImpl
->mpSelFuncSet
= std::make_unique
<TextSelFunctionSet
>( this );
169 mpImpl
->mpSelEngine
= std::make_unique
<SelectionEngine
>( mpImpl
->mpWindow
, mpImpl
->mpSelFuncSet
.get() );
170 mpImpl
->mpSelEngine
->SetSelectionMode( SelectionMode::Range
);
171 mpImpl
->mpSelEngine
->EnableDrag( true );
173 mpImpl
->mpCursor
.reset(new vcl::Cursor
);
174 mpImpl
->mpCursor
->Show();
175 pWindow
->SetCursor( mpImpl
->mpCursor
.get() );
176 pWindow
->SetInputContext( InputContext( pEng
->GetFont(), InputContextFlags::Text
|InputContextFlags::ExtText
) );
178 if ( pWindow
->GetSettings().GetStyleSettings().GetSelectionOptions() & SelectionOptions::Invert
)
179 mpImpl
->mbHighlightSelection
= true;
181 pWindow
->SetLineColor();
183 if ( pWindow
->GetDragGestureRecognizer().is() )
185 vcl::unohelper::DragAndDropWrapper
* pDnDWrapper
= new vcl::unohelper::DragAndDropWrapper( this );
186 mpImpl
->mxDnDListener
= pDnDWrapper
;
188 css::uno::Reference
< css::datatransfer::dnd::XDragGestureListener
> xDGL( mpImpl
->mxDnDListener
, css::uno::UNO_QUERY
);
189 pWindow
->GetDragGestureRecognizer()->addDragGestureListener( xDGL
);
190 css::uno::Reference
< css::datatransfer::dnd::XDropTargetListener
> xDTL( xDGL
, css::uno::UNO_QUERY
);
191 pWindow
->GetDropTarget()->addDropTargetListener( xDTL
);
192 pWindow
->GetDropTarget()->setActive( true );
193 pWindow
->GetDropTarget()->setDefaultActions( css::datatransfer::dnd::DNDConstants::ACTION_COPY_OR_MOVE
);
197 TextView::~TextView()
199 mpImpl
->mpSelEngine
.reset();
200 mpImpl
->mpSelFuncSet
.reset();
202 if ( mpImpl
->mpWindow
->GetCursor() == mpImpl
->mpCursor
.get() )
203 mpImpl
->mpWindow
->SetCursor( nullptr );
205 mpImpl
->mpCursor
.reset();
206 mpImpl
->mpDDInfo
.reset();
209 void TextView::Invalidate()
211 mpImpl
->mpWindow
->Invalidate();
214 void TextView::SetSelection( const TextSelection
& rTextSel
, bool bGotoCursor
)
216 // if someone left an empty attribute and then the Outliner manipulated the selection
217 if ( !mpImpl
->maSelection
.HasRange() )
218 mpImpl
->mpTextEngine
->CursorMoved( mpImpl
->maSelection
.GetStart().GetPara() );
220 // if the selection is manipulated after a KeyInput
221 mpImpl
->mpTextEngine
->CheckIdleFormatter();
224 TextSelection
aNewSel( rTextSel
);
225 mpImpl
->mpTextEngine
->ValidateSelection( aNewSel
);
226 ImpSetSelection( aNewSel
);
228 ShowCursor( bGotoCursor
);
231 void TextView::SetSelection( const TextSelection
& rTextSel
)
233 SetSelection( rTextSel
, mpImpl
->mbAutoScroll
);
236 const TextSelection
& TextView::GetSelection() const
238 return mpImpl
->maSelection
;
240 TextSelection
& TextView::GetSelection()
242 return mpImpl
->maSelection
;
245 void TextView::DeleteSelected()
249 mpImpl
->mpTextEngine
->UndoActionStart();
250 TextPaM aPaM
= mpImpl
->mpTextEngine
->ImpDeleteText( mpImpl
->maSelection
);
251 mpImpl
->mpTextEngine
->UndoActionEnd();
253 ImpSetSelection( aPaM
);
254 mpImpl
->mpTextEngine
->FormatAndUpdate( this );
258 void TextView::ImpPaint(vcl::RenderContext
& rRenderContext
, const Point
& rStartPos
, tools::Rectangle
const* pPaintArea
, TextSelection
const* pSelection
)
260 if (!mpImpl
->mbPaintSelection
)
262 pSelection
= nullptr;
266 // set correct background color;
267 // unfortunately we cannot detect if it has changed
268 vcl::Font aFont
= mpImpl
->mpTextEngine
->GetFont();
269 Color aColor
= rRenderContext
.GetBackground().GetColor();
270 aColor
.SetTransparency(0);
271 if (aColor
!= aFont
.GetFillColor())
273 if (aFont
.IsTransparent())
274 aColor
= COL_TRANSPARENT
;
275 aFont
.SetFillColor(aColor
);
276 mpImpl
->mpTextEngine
->maFont
= aFont
;
280 mpImpl
->mpTextEngine
->ImpPaint(&rRenderContext
, rStartPos
, pPaintArea
, pSelection
);
283 void TextView::Paint(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& rRect
)
285 ImpPaint(rRenderContext
, rRect
);
288 void TextView::ImpPaint(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& rRect
)
290 if ( !mpImpl
->mpTextEngine
->GetUpdateMode() || mpImpl
->mpTextEngine
->IsInUndo() )
293 TextSelection
*pDrawSelection
= nullptr;
294 if (!mpImpl
->mbHighlightSelection
&& mpImpl
->maSelection
.HasRange())
295 pDrawSelection
= &mpImpl
->maSelection
;
297 Point aStartPos
= ImpGetOutputStartPos(mpImpl
->maStartDocPos
);
298 ImpPaint(rRenderContext
, aStartPos
, &rRect
, pDrawSelection
);
299 if (mpImpl
->mbHighlightSelection
)
300 ImpHighlight(mpImpl
->maSelection
);
303 void TextView::ImpHighlight( const TextSelection
& rSel
)
305 TextSelection
aSel( rSel
);
307 if ( !(aSel
.HasRange() && !mpImpl
->mpTextEngine
->IsInUndo() && mpImpl
->mpTextEngine
->GetUpdateMode()) )
310 mpImpl
->mpCursor
->Hide();
312 SAL_WARN_IF( mpImpl
->mpTextEngine
->mpIdleFormatter
->IsActive(), "vcl", "ImpHighlight: Not formatted!" );
314 tools::Rectangle
aVisArea( mpImpl
->maStartDocPos
, mpImpl
->mpWindow
->GetOutputSizePixel() );
316 const sal_uInt32 nStartPara
= aSel
.GetStart().GetPara();
317 const sal_uInt32 nEndPara
= aSel
.GetEnd().GetPara();
318 for ( sal_uInt32 nPara
= 0; nPara
<= nEndPara
; ++nPara
)
320 const tools::Long nParaHeight
= mpImpl
->mpTextEngine
->CalcParaHeight( nPara
);
321 if ( ( nPara
>= nStartPara
) && ( ( nY
+ nParaHeight
) > aVisArea
.Top() ) )
323 TEParaPortion
* pTEParaPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( nPara
);
324 std::vector
<TextLine
>::size_type nStartLine
= 0;
325 std::vector
<TextLine
>::size_type nEndLine
= pTEParaPortion
->GetLines().size() -1;
326 if ( nPara
== nStartPara
)
327 nStartLine
= pTEParaPortion
->GetLineNumber( aSel
.GetStart().GetIndex(), false );
328 if ( nPara
== nEndPara
)
329 nEndLine
= pTEParaPortion
->GetLineNumber( aSel
.GetEnd().GetIndex(), true );
331 // iterate over all lines
332 for ( std::vector
<TextLine
>::size_type nLine
= nStartLine
; nLine
<= nEndLine
; nLine
++ )
334 TextLine
& rLine
= pTEParaPortion
->GetLines()[ nLine
];
335 sal_Int32 nStartIndex
= rLine
.GetStart();
336 sal_Int32 nEndIndex
= rLine
.GetEnd();
337 if ( ( nPara
== nStartPara
) && ( nLine
== nStartLine
) )
338 nStartIndex
= aSel
.GetStart().GetIndex();
339 if ( ( nPara
== nEndPara
) && ( nLine
== nEndLine
) )
340 nEndIndex
= aSel
.GetEnd().GetIndex();
342 // possible if at the beginning of a wrapped line
343 if ( nEndIndex
< nStartIndex
)
344 nEndIndex
= nStartIndex
;
346 tools::Rectangle
aTmpRect( mpImpl
->mpTextEngine
->GetEditCursor( TextPaM( nPara
, nStartIndex
), false ) );
347 aTmpRect
.AdjustTop(nY
);
348 aTmpRect
.AdjustBottom(nY
);
349 Point
aTopLeft( aTmpRect
.TopLeft() );
351 aTmpRect
= mpImpl
->mpTextEngine
->GetEditCursor( TextPaM( nPara
, nEndIndex
), true );
352 aTmpRect
.AdjustTop(nY
);
353 aTmpRect
.AdjustBottom(nY
);
354 Point
aBottomRight( aTmpRect
.BottomRight() );
355 aBottomRight
.AdjustX( -1 );
357 // only paint if in the visible region
358 if ( ( aTopLeft
.X() < aBottomRight
.X() ) && ( aBottomRight
.Y() >= aVisArea
.Top() ) )
360 Point
aPnt1( GetWindowPos( aTopLeft
) );
361 Point
aPnt2( GetWindowPos( aBottomRight
) );
363 tools::Rectangle
aRect( aPnt1
, aPnt2
);
364 mpImpl
->mpWindow
->Invert( aRect
);
370 if ( nY
>= aVisArea
.Bottom() )
375 void TextView::ImpSetSelection( const TextSelection
& rSelection
)
377 if (rSelection
== mpImpl
->maSelection
)
380 bool bCaret
= false, bSelection
= false;
381 const TextPaM
&rEnd
= rSelection
.GetEnd();
382 const TextPaM
&rOldEnd
= mpImpl
->maSelection
.GetEnd();
383 bool bGap
= rSelection
.HasRange(), bOldGap
= mpImpl
->maSelection
.HasRange();
389 mpImpl
->maSelection
= rSelection
;
392 mpImpl
->mpTextEngine
->Broadcast(TextHint(SfxHintId::TextViewSelectionChanged
));
395 mpImpl
->mpTextEngine
->Broadcast(TextHint(SfxHintId::TextViewCaretChanged
));
398 void TextView::ShowSelection()
400 ImpShowHideSelection();
403 void TextView::HideSelection()
405 ImpShowHideSelection();
408 void TextView::ShowSelection( const TextSelection
& rRange
)
410 ImpShowHideSelection( &rRange
);
413 void TextView::ImpShowHideSelection(const TextSelection
* pRange
)
415 const TextSelection
* pRangeOrSelection
= pRange
? pRange
: &mpImpl
->maSelection
;
417 if ( !pRangeOrSelection
->HasRange() )
420 if ( mpImpl
->mbHighlightSelection
)
422 ImpHighlight( *pRangeOrSelection
);
426 if( mpImpl
->mpWindow
->IsPaintTransparent() )
427 mpImpl
->mpWindow
->Invalidate();
430 TextSelection
aRange( *pRangeOrSelection
);
432 bool bVisCursor
= mpImpl
->mpCursor
->IsVisible();
433 mpImpl
->mpCursor
->Hide();
436 mpImpl
->mpCursor
->Show();
441 bool TextView::KeyInput( const KeyEvent
& rKeyEvent
)
444 bool bModified
= false;
446 bool bEndKey
= false; // special CursorPosition
447 bool bAllowIdle
= true;
450 // the local bModified is not set e.g. by Cut/Paste, as here
451 // the update happens somewhere else
452 bool bWasModified
= mpImpl
->mpTextEngine
->IsModified();
453 mpImpl
->mpTextEngine
->SetModified( false );
455 TextSelection
aCurSel( mpImpl
->maSelection
);
456 TextSelection
aOldSel( aCurSel
);
458 sal_uInt16 nCode
= rKeyEvent
.GetKeyCode().GetCode();
459 KeyFuncType eFunc
= rKeyEvent
.GetKeyCode().GetFunction();
460 if ( eFunc
!= KeyFuncType::DONTKNOW
)
464 case KeyFuncType::CUT
:
466 if ( !mpImpl
->mbReadOnly
)
470 case KeyFuncType::COPY
:
475 case KeyFuncType::PASTE
:
477 if ( !mpImpl
->mbReadOnly
)
481 case KeyFuncType::UNDO
:
483 if ( !mpImpl
->mbReadOnly
)
487 case KeyFuncType::REDO
:
489 if ( !mpImpl
->mbReadOnly
)
494 default: // might get processed below
495 eFunc
= KeyFuncType::DONTKNOW
;
498 if ( eFunc
== KeyFuncType::DONTKNOW
)
510 case css::awt::Key::MOVE_WORD_FORWARD
:
511 case css::awt::Key::SELECT_WORD_FORWARD
:
512 case css::awt::Key::MOVE_WORD_BACKWARD
:
513 case css::awt::Key::SELECT_WORD_BACKWARD
:
514 case css::awt::Key::MOVE_TO_BEGIN_OF_LINE
:
515 case css::awt::Key::MOVE_TO_END_OF_LINE
:
516 case css::awt::Key::SELECT_TO_BEGIN_OF_LINE
:
517 case css::awt::Key::SELECT_TO_END_OF_LINE
:
518 case css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH
:
519 case css::awt::Key::MOVE_TO_END_OF_PARAGRAPH
:
520 case css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH
:
521 case css::awt::Key::SELECT_TO_END_OF_PARAGRAPH
:
522 case css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT
:
523 case css::awt::Key::MOVE_TO_END_OF_DOCUMENT
:
524 case css::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT
:
525 case css::awt::Key::SELECT_TO_END_OF_DOCUMENT
:
527 if ( ( !rKeyEvent
.GetKeyCode().IsMod2() || ( nCode
== KEY_LEFT
) || ( nCode
== KEY_RIGHT
) )
528 && !( rKeyEvent
.GetKeyCode().IsMod1() && ( nCode
== KEY_PAGEUP
|| nCode
== KEY_PAGEDOWN
) ) )
530 aCurSel
= ImpMoveCursor( rKeyEvent
);
531 if ( aCurSel
.HasRange() ) {
532 css::uno::Reference
<css::datatransfer::clipboard::XClipboard
> aSelection(GetWindow()->GetPrimarySelection());
536 if ( nCode
== KEY_END
)
545 case css::awt::Key::DELETE_WORD_BACKWARD
:
546 case css::awt::Key::DELETE_WORD_FORWARD
:
547 case css::awt::Key::DELETE_TO_BEGIN_OF_LINE
:
548 case css::awt::Key::DELETE_TO_END_OF_LINE
:
550 if ( !mpImpl
->mbReadOnly
&& !rKeyEvent
.GetKeyCode().IsMod2() )
552 sal_uInt8 nDel
= ( nCode
== KEY_DELETE
) ? DEL_RIGHT
: DEL_LEFT
;
553 sal_uInt8 nMode
= rKeyEvent
.GetKeyCode().IsMod1() ? DELMODE_RESTOFWORD
: DELMODE_SIMPLE
;
554 if ( ( nMode
== DELMODE_RESTOFWORD
) && rKeyEvent
.GetKeyCode().IsShift() )
555 nMode
= DELMODE_RESTOFCONTENT
;
559 case css::awt::Key::DELETE_WORD_BACKWARD
:
561 nMode
= DELMODE_RESTOFWORD
;
563 case css::awt::Key::DELETE_WORD_FORWARD
:
565 nMode
= DELMODE_RESTOFWORD
;
567 case css::awt::Key::DELETE_TO_BEGIN_OF_LINE
:
569 nMode
= DELMODE_RESTOFCONTENT
;
571 case css::awt::Key::DELETE_TO_END_OF_LINE
:
573 nMode
= DELMODE_RESTOFCONTENT
;
578 mpImpl
->mpTextEngine
->UndoActionStart();
579 aCurSel
= ImpDelete( nDel
, nMode
);
580 mpImpl
->mpTextEngine
->UndoActionEnd();
590 if ( !mpImpl
->mbReadOnly
&& !rKeyEvent
.GetKeyCode().IsShift() &&
591 !rKeyEvent
.GetKeyCode().IsMod1() && !rKeyEvent
.GetKeyCode().IsMod2() &&
592 ImplCheckTextLen( OUString('x') ) )
594 aCurSel
= mpImpl
->mpTextEngine
->ImpInsertText( aCurSel
, '\t', !IsInsertMode() );
603 // do not swallow Shift-RETURN, as this would disable multi-line entries
604 // in dialogs & property editors
605 if ( !mpImpl
->mbReadOnly
&& !rKeyEvent
.GetKeyCode().IsMod1() &&
606 !rKeyEvent
.GetKeyCode().IsMod2() && ImplCheckTextLen( OUString('x') ) )
608 mpImpl
->mpTextEngine
->UndoActionStart();
609 aCurSel
= mpImpl
->mpTextEngine
->ImpInsertParaBreak( aCurSel
);
610 if ( mpImpl
->mbAutoIndent
)
612 TextNode
* pPrev
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aCurSel
.GetEnd().GetPara() - 1 ].get();
614 while ( ( n
< pPrev
->GetText().getLength() ) && (
615 ( pPrev
->GetText()[ n
] == ' ' ) ||
616 ( pPrev
->GetText()[ n
] == '\t' ) ) )
621 aCurSel
= mpImpl
->mpTextEngine
->ImpInsertText( aCurSel
, pPrev
->GetText().copy( 0, n
) );
623 mpImpl
->mpTextEngine
->UndoActionEnd();
632 if ( !mpImpl
->mbReadOnly
)
633 SetInsertMode( !IsInsertMode() );
638 if ( TextEngine::IsSimpleCharInput( rKeyEvent
) )
640 sal_Unicode nCharCode
= rKeyEvent
.GetCharCode();
641 if ( !mpImpl
->mbReadOnly
&& ImplCheckTextLen( OUString(nCharCode
) ) ) // otherwise swallow the character anyway
643 aCurSel
= mpImpl
->mpTextEngine
->ImpInsertText( nCharCode
, aCurSel
, !IsInsertMode(), true );
653 if ( aCurSel
!= aOldSel
) // Check if changed, maybe other method already changed mpImpl->maSelection, don't overwrite that!
654 ImpSetSelection( aCurSel
);
656 if ( ( nCode
!= KEY_UP
) && ( nCode
!= KEY_DOWN
) )
657 mpImpl
->mnTravelXPos
= TRAVEL_X_DONTKNOW
;
661 // Idle-Formatter only if AnyInput
662 if ( bAllowIdle
&& Application::AnyInput( VclInputFlags::KEYBOARD
) )
663 mpImpl
->mpTextEngine
->IdleFormatAndUpdate( this );
665 mpImpl
->mpTextEngine
->FormatAndUpdate( this);
669 // selection is painted now in ImpMoveCursor
670 ImpShowCursor( mpImpl
->mbAutoScroll
, true, bEndKey
);
673 if ( mpImpl
->mpTextEngine
->IsModified() )
674 mpImpl
->mpTextEngine
->Broadcast( TextHint( SfxHintId::TextModified
) );
675 else if ( bWasModified
)
676 mpImpl
->mpTextEngine
->SetModified( true );
681 void TextView::MouseButtonUp( const MouseEvent
& rMouseEvent
)
683 mpImpl
->mbClickedInSelection
= false;
684 mpImpl
->mnTravelXPos
= TRAVEL_X_DONTKNOW
;
685 mpImpl
->mpSelEngine
->SelMouseButtonUp( rMouseEvent
);
686 if ( rMouseEvent
.IsMiddle() && !IsReadOnly() &&
687 ( GetWindow()->GetSettings().GetMouseSettings().GetMiddleButtonAction() == MouseMiddleButtonAction::PasteSelection
) )
689 css::uno::Reference
<css::datatransfer::clipboard::XClipboard
> aSelection(GetWindow()->GetPrimarySelection());
691 if ( mpImpl
->mpTextEngine
->IsModified() )
692 mpImpl
->mpTextEngine
->Broadcast( TextHint( SfxHintId::TextModified
) );
694 else if ( rMouseEvent
.IsLeft() && GetSelection().HasRange() )
696 css::uno::Reference
<css::datatransfer::clipboard::XClipboard
> aSelection(GetWindow()->GetPrimarySelection());
701 void TextView::MouseButtonDown( const MouseEvent
& rMouseEvent
)
703 mpImpl
->mpTextEngine
->CheckIdleFormatter(); // for fast typing and MouseButtonDown
704 mpImpl
->mnTravelXPos
= TRAVEL_X_DONTKNOW
;
705 mpImpl
->mbClickedInSelection
= IsSelectionAtPoint( rMouseEvent
.GetPosPixel() );
707 mpImpl
->mpTextEngine
->SetActiveView( this );
709 mpImpl
->mpSelEngine
->SelMouseButtonDown( rMouseEvent
);
711 // mbu 20.01.2005 - SelMouseButtonDown() possibly triggers a 'selection changed'
712 // notification. The appropriate handler could change the current selection,
713 // which is the case in the MailMerge address block control. To enable select'n'drag
714 // we need to reevaluate the selection after the notification has been fired.
715 mpImpl
->mbClickedInSelection
= IsSelectionAtPoint( rMouseEvent
.GetPosPixel() );
718 if ( rMouseEvent
.IsShift() || ( rMouseEvent
.GetClicks() < 2 ))
721 if ( rMouseEvent
.IsMod2() )
724 ImpSetSelection( mpImpl
->maSelection
.GetEnd() );
725 SetCursorAtPoint( rMouseEvent
.GetPosPixel() ); // not set by SelectionEngine for MOD2
728 if ( rMouseEvent
.GetClicks() == 2 )
731 if ( mpImpl
->maSelection
.GetEnd().GetIndex() < mpImpl
->mpTextEngine
->GetTextLen( mpImpl
->maSelection
.GetEnd().GetPara() ) )
734 // tdf#57879 - expand selection to include connector punctuations
735 TextSelection aNewSel
;
736 mpImpl
->mpTextEngine
->GetWord( mpImpl
->maSelection
.GetEnd(), &aNewSel
.GetStart(), &aNewSel
.GetEnd() );
737 ImpSetSelection( aNewSel
);
742 else if ( rMouseEvent
.GetClicks() == 3 )
745 if ( mpImpl
->maSelection
.GetStart().GetIndex() || ( mpImpl
->maSelection
.GetEnd().GetIndex() < mpImpl
->mpTextEngine
->GetTextLen( mpImpl
->maSelection
.GetEnd().GetPara() ) ) )
748 TextSelection
aNewSel( mpImpl
->maSelection
);
749 aNewSel
.GetStart().GetIndex() = 0;
750 aNewSel
.GetEnd().GetIndex() = mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ mpImpl
->maSelection
.GetEnd().GetPara() ]->GetText().getLength();
751 ImpSetSelection( aNewSel
);
758 void TextView::MouseMove( const MouseEvent
& rMouseEvent
)
760 mpImpl
->mnTravelXPos
= TRAVEL_X_DONTKNOW
;
761 mpImpl
->mpSelEngine
->SelMouseMove( rMouseEvent
);
764 void TextView::Command( const CommandEvent
& rCEvt
)
766 mpImpl
->mpTextEngine
->CheckIdleFormatter(); // for fast typing and MouseButtonDown
767 mpImpl
->mpTextEngine
->SetActiveView( this );
769 if ( rCEvt
.GetCommand() == CommandEventId::StartExtTextInput
)
772 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ GetSelection().GetEnd().GetPara() ].get();
773 mpImpl
->mpTextEngine
->mpIMEInfos
= std::make_unique
<TEIMEInfos
>( GetSelection().GetEnd(), pNode
->GetText().copy( GetSelection().GetEnd().GetIndex() ) );
774 mpImpl
->mpTextEngine
->mpIMEInfos
->bWasCursorOverwrite
= !IsInsertMode();
776 else if ( rCEvt
.GetCommand() == CommandEventId::EndExtTextInput
)
778 SAL_WARN_IF( !mpImpl
->mpTextEngine
->mpIMEInfos
, "vcl", "CommandEventId::EndExtTextInput => No Start ?" );
779 if( mpImpl
->mpTextEngine
->mpIMEInfos
)
781 TEParaPortion
* pPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
.GetPara() );
782 pPortion
->MarkSelectionInvalid( mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
.GetIndex() );
784 bool bInsertMode
= !mpImpl
->mpTextEngine
->mpIMEInfos
->bWasCursorOverwrite
;
786 mpImpl
->mpTextEngine
->mpIMEInfos
.reset();
788 mpImpl
->mpTextEngine
->TextModified();
789 mpImpl
->mpTextEngine
->FormatAndUpdate( this );
791 SetInsertMode( bInsertMode
);
793 if ( mpImpl
->mpTextEngine
->IsModified() )
794 mpImpl
->mpTextEngine
->Broadcast( TextHint( SfxHintId::TextModified
) );
797 else if ( rCEvt
.GetCommand() == CommandEventId::ExtTextInput
)
799 SAL_WARN_IF( !mpImpl
->mpTextEngine
->mpIMEInfos
, "vcl", "CommandEventId::ExtTextInput => No Start ?" );
800 if( mpImpl
->mpTextEngine
->mpIMEInfos
)
802 const CommandExtTextInputData
* pData
= rCEvt
.GetExtTextInputData();
804 if ( !pData
->IsOnlyCursorChanged() )
806 TextSelection
aSelect( mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
);
807 aSelect
.GetEnd().GetIndex() += mpImpl
->mpTextEngine
->mpIMEInfos
->nLen
;
808 aSelect
= mpImpl
->mpTextEngine
->ImpDeleteText( aSelect
);
809 aSelect
= mpImpl
->mpTextEngine
->ImpInsertText( aSelect
, pData
->GetText() );
811 if ( mpImpl
->mpTextEngine
->mpIMEInfos
->bWasCursorOverwrite
)
813 const sal_Int32 nOldIMETextLen
= mpImpl
->mpTextEngine
->mpIMEInfos
->nLen
;
814 const sal_Int32 nNewIMETextLen
= pData
->GetText().getLength();
816 if ( ( nOldIMETextLen
> nNewIMETextLen
) &&
817 ( nNewIMETextLen
< mpImpl
->mpTextEngine
->mpIMEInfos
->aOldTextAfterStartPos
.getLength() ) )
819 // restore old characters
820 sal_Int32 nRestore
= nOldIMETextLen
- nNewIMETextLen
;
821 TextPaM
aPaM( mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
);
822 aPaM
.GetIndex() += nNewIMETextLen
;
823 mpImpl
->mpTextEngine
->ImpInsertText( aPaM
, mpImpl
->mpTextEngine
->mpIMEInfos
->aOldTextAfterStartPos
.copy( nNewIMETextLen
, nRestore
) );
825 else if ( ( nOldIMETextLen
< nNewIMETextLen
) &&
826 ( nOldIMETextLen
< mpImpl
->mpTextEngine
->mpIMEInfos
->aOldTextAfterStartPos
.getLength() ) )
829 const sal_Int32 nOverwrite
= std::min( nNewIMETextLen
, mpImpl
->mpTextEngine
->mpIMEInfos
->aOldTextAfterStartPos
.getLength() ) - nOldIMETextLen
;
830 SAL_WARN_IF( !nOverwrite
|| (nOverwrite
>= 0xFF00), "vcl", "IME Overwrite?!" );
831 TextPaM
aPaM( mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
);
832 aPaM
.GetIndex() += nNewIMETextLen
;
833 TextSelection
aSel( aPaM
);
834 aSel
.GetEnd().GetIndex() += nOverwrite
;
835 mpImpl
->mpTextEngine
->ImpDeleteText( aSel
);
839 if ( pData
->GetTextAttr() )
841 mpImpl
->mpTextEngine
->mpIMEInfos
->CopyAttribs( pData
->GetTextAttr(), pData
->GetText().getLength() );
845 mpImpl
->mpTextEngine
->mpIMEInfos
->DestroyAttribs();
848 TEParaPortion
* pPPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
.GetPara() );
849 pPPortion
->MarkSelectionInvalid( mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
.GetIndex() );
850 mpImpl
->mpTextEngine
->FormatAndUpdate( this );
853 TextSelection aNewSel
= TextPaM( mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
.GetPara(), mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
.GetIndex()+pData
->GetCursorPos() );
854 SetSelection( aNewSel
);
855 SetInsertMode( !pData
->IsCursorOverwrite() );
857 if ( pData
->IsCursorVisible() )
863 else if ( rCEvt
.GetCommand() == CommandEventId::CursorPos
)
865 if ( mpImpl
->mpTextEngine
->mpIMEInfos
&& mpImpl
->mpTextEngine
->mpIMEInfos
->nLen
)
867 TextPaM
aPaM( GetSelection().GetEnd() );
868 tools::Rectangle aR1
= mpImpl
->mpTextEngine
->PaMtoEditCursor( aPaM
);
870 sal_Int32 nInputEnd
= mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
.GetIndex() + mpImpl
->mpTextEngine
->mpIMEInfos
->nLen
;
872 if ( !mpImpl
->mpTextEngine
->IsFormatted() )
873 mpImpl
->mpTextEngine
->FormatDoc();
875 TEParaPortion
* pParaPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( aPaM
.GetPara() );
876 std::vector
<TextLine
>::size_type nLine
= pParaPortion
->GetLineNumber( aPaM
.GetIndex(), true );
877 TextLine
& rLine
= pParaPortion
->GetLines()[ nLine
];
878 if ( nInputEnd
> rLine
.GetEnd() )
879 nInputEnd
= rLine
.GetEnd();
880 tools::Rectangle aR2
= mpImpl
->mpTextEngine
->PaMtoEditCursor( TextPaM( aPaM
.GetPara(), nInputEnd
) );
882 tools::Long nWidth
= aR2
.Left()-aR1
.Right();
883 aR1
.Move( -GetStartDocPos().X(), -GetStartDocPos().Y() );
884 GetWindow()->SetCursorRect( &aR1
, nWidth
);
888 GetWindow()->SetCursorRect();
893 mpImpl
->mpSelEngine
->Command( rCEvt
);
897 void TextView::ShowCursor( bool bGotoCursor
, bool bForceVisCursor
)
899 // this setting has more weight
900 if ( !mpImpl
->mbAutoScroll
)
902 ImpShowCursor( bGotoCursor
, bForceVisCursor
, false );
905 void TextView::HideCursor()
907 mpImpl
->mpCursor
->Hide();
910 void TextView::Scroll( tools::Long ndX
, tools::Long ndY
)
912 SAL_WARN_IF( !mpImpl
->mpTextEngine
->IsFormatted(), "vcl", "Scroll: Not formatted!" );
917 Point
aNewStartPos( mpImpl
->maStartDocPos
);
920 aNewStartPos
.AdjustY( -ndY
);
921 if ( aNewStartPos
.Y() < 0 )
922 aNewStartPos
.setY( 0 );
925 aNewStartPos
.AdjustX( -ndX
);
926 if ( aNewStartPos
.X() < 0 )
927 aNewStartPos
.setX( 0 );
929 tools::Long nDiffX
= mpImpl
->maStartDocPos
.X() - aNewStartPos
.X();
930 tools::Long nDiffY
= mpImpl
->maStartDocPos
.Y() - aNewStartPos
.Y();
932 if ( nDiffX
|| nDiffY
)
934 bool bVisCursor
= mpImpl
->mpCursor
->IsVisible();
935 mpImpl
->mpCursor
->Hide();
936 mpImpl
->mpWindow
->PaintImmediately();
937 mpImpl
->maStartDocPos
= aNewStartPos
;
939 if ( mpImpl
->mpTextEngine
->IsRightToLeft() )
941 mpImpl
->mpWindow
->Scroll( nDiffX
, nDiffY
);
942 mpImpl
->mpWindow
->PaintImmediately();
943 mpImpl
->mpCursor
->SetPos( mpImpl
->mpCursor
->GetPos() + Point( nDiffX
, nDiffY
) );
944 if ( bVisCursor
&& !mpImpl
->mbReadOnly
)
945 mpImpl
->mpCursor
->Show();
948 mpImpl
->mpTextEngine
->Broadcast( TextHint( SfxHintId::TextViewScrolled
) );
951 void TextView::Undo()
953 mpImpl
->mpTextEngine
->SetActiveView( this );
954 mpImpl
->mpTextEngine
->GetUndoManager().Undo();
957 void TextView::Redo()
959 mpImpl
->mpTextEngine
->SetActiveView( this );
960 mpImpl
->mpTextEngine
->GetUndoManager().Redo();
965 mpImpl
->mpTextEngine
->UndoActionStart();
968 mpImpl
->mpTextEngine
->UndoActionEnd();
971 void TextView::Copy( css::uno::Reference
< css::datatransfer::clipboard::XClipboard
> const & rxClipboard
)
973 if ( !rxClipboard
.is() )
976 TETextDataObject
* pDataObj
= new TETextDataObject( GetSelected() );
978 SolarMutexReleaser aReleaser
;
982 rxClipboard
->setContents( pDataObj
, nullptr );
984 css::uno::Reference
< css::datatransfer::clipboard::XFlushableClipboard
> xFlushableClipboard( rxClipboard
, css::uno::UNO_QUERY
);
985 if( xFlushableClipboard
.is() )
986 xFlushableClipboard
->flushClipboard();
988 catch( const css::uno::Exception
& )
993 void TextView::Copy()
995 css::uno::Reference
<css::datatransfer::clipboard::XClipboard
> aClipboard(GetWindow()->GetClipboard());
999 void TextView::Paste( css::uno::Reference
< css::datatransfer::clipboard::XClipboard
> const & rxClipboard
)
1001 if ( !rxClipboard
.is() )
1004 css::uno::Reference
< css::datatransfer::XTransferable
> xDataObj
;
1008 SolarMutexReleaser aReleaser
;
1009 xDataObj
= rxClipboard
->getContents();
1011 catch( const css::uno::Exception
& )
1015 if ( !xDataObj
.is() )
1018 css::datatransfer::DataFlavor aFlavor
;
1019 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING
, aFlavor
);
1020 if ( !xDataObj
->isDataFlavorSupported( aFlavor
) )
1025 css::uno::Any aData
= xDataObj
->getTransferData( aFlavor
);
1028 bool bWasTruncated
= false;
1029 if( mpImpl
->mpTextEngine
->GetMaxTextLen() != 0 )
1030 bWasTruncated
= ImplTruncateNewText( aText
);
1031 InsertText( aText
);
1032 mpImpl
->mpTextEngine
->Broadcast( TextHint( SfxHintId::TextModified
) );
1035 Edit::ShowTruncationWarning(mpImpl
->mpWindow
->GetFrameWeld());
1037 catch( const css::datatransfer::UnsupportedFlavorException
& )
1042 void TextView::Paste()
1044 css::uno::Reference
<css::datatransfer::clipboard::XClipboard
> aClipboard(GetWindow()->GetClipboard());
1045 Paste( aClipboard
);
1048 OUString
TextView::GetSelected()
1050 return GetSelected( GetSystemLineEnd() );
1053 OUString
TextView::GetSelected( LineEnd aSeparator
)
1055 return mpImpl
->mpTextEngine
->GetText( mpImpl
->maSelection
, aSeparator
);
1058 void TextView::SetInsertMode( bool bInsert
)
1060 if ( mpImpl
->mbInsertMode
!= bInsert
)
1062 mpImpl
->mbInsertMode
= bInsert
;
1063 ShowCursor( mpImpl
->mbAutoScroll
, false );
1067 void TextView::SetReadOnly( bool bReadOnly
)
1069 if ( mpImpl
->mbReadOnly
!= bReadOnly
)
1071 mpImpl
->mbReadOnly
= bReadOnly
;
1072 if ( !mpImpl
->mbReadOnly
)
1073 ShowCursor( mpImpl
->mbAutoScroll
, false );
1077 GetWindow()->SetInputContext( InputContext( mpImpl
->mpTextEngine
->GetFont(), bReadOnly
? InputContextFlags::Text
|InputContextFlags::ExtText
: InputContextFlags::NONE
) );
1081 TextSelection
const & TextView::ImpMoveCursor( const KeyEvent
& rKeyEvent
)
1083 // normally only needed for Up/Down; but who cares
1084 mpImpl
->mpTextEngine
->CheckIdleFormatter();
1086 TextPaM
aPaM( mpImpl
->maSelection
.GetEnd() );
1087 TextPaM
aOldEnd( aPaM
);
1089 TextDirectionality eTextDirection
= TextDirectionality::LeftToRight_TopToBottom
;
1090 if ( mpImpl
->mpTextEngine
->IsRightToLeft() )
1091 eTextDirection
= TextDirectionality::RightToLeft_TopToBottom
;
1093 KeyEvent aTranslatedKeyEvent
= rKeyEvent
.LogicalTextDirectionality( eTextDirection
);
1095 bool bCtrl
= aTranslatedKeyEvent
.GetKeyCode().IsMod1();
1096 sal_uInt16 nCode
= aTranslatedKeyEvent
.GetKeyCode().GetCode();
1098 bool bSelect
= aTranslatedKeyEvent
.GetKeyCode().IsShift();
1101 case KEY_UP
: aPaM
= CursorUp( aPaM
);
1103 case KEY_DOWN
: aPaM
= CursorDown( aPaM
);
1105 case KEY_HOME
: aPaM
= bCtrl
? CursorStartOfDoc() : CursorStartOfLine( aPaM
);
1107 case KEY_END
: aPaM
= bCtrl
? CursorEndOfDoc() : CursorEndOfLine( aPaM
);
1109 case KEY_PAGEUP
: aPaM
= bCtrl
? CursorStartOfDoc() : PageUp( aPaM
);
1111 case KEY_PAGEDOWN
: aPaM
= bCtrl
? CursorEndOfDoc() : PageDown( aPaM
);
1113 case KEY_LEFT
: aPaM
= bCtrl
? CursorWordLeft( aPaM
) : CursorLeft( aPaM
, aTranslatedKeyEvent
.GetKeyCode().IsMod2() ? sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCHARACTER
) : sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCELL
) );
1115 case KEY_RIGHT
: aPaM
= bCtrl
? CursorWordRight( aPaM
) : CursorRight( aPaM
, aTranslatedKeyEvent
.GetKeyCode().IsMod2() ? sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCHARACTER
) : sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCELL
) );
1117 case css::awt::Key::SELECT_WORD_FORWARD
:
1120 case css::awt::Key::MOVE_WORD_FORWARD
:
1121 aPaM
= CursorWordRight( aPaM
);
1123 case css::awt::Key::SELECT_WORD_BACKWARD
:
1126 case css::awt::Key::MOVE_WORD_BACKWARD
:
1127 aPaM
= CursorWordLeft( aPaM
);
1129 case css::awt::Key::SELECT_TO_BEGIN_OF_LINE
:
1132 case css::awt::Key::MOVE_TO_BEGIN_OF_LINE
:
1133 aPaM
= CursorStartOfLine( aPaM
);
1135 case css::awt::Key::SELECT_TO_END_OF_LINE
:
1138 case css::awt::Key::MOVE_TO_END_OF_LINE
:
1139 aPaM
= CursorEndOfLine( aPaM
);
1141 case css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH
:
1144 case css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH
:
1145 aPaM
= CursorStartOfParagraph( aPaM
);
1147 case css::awt::Key::SELECT_TO_END_OF_PARAGRAPH
:
1150 case css::awt::Key::MOVE_TO_END_OF_PARAGRAPH
:
1151 aPaM
= CursorEndOfParagraph( aPaM
);
1153 case css::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT
:
1156 case css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT
:
1157 aPaM
= CursorStartOfDoc();
1159 case css::awt::Key::SELECT_TO_END_OF_DOCUMENT
:
1162 case css::awt::Key::MOVE_TO_END_OF_DOCUMENT
:
1163 aPaM
= CursorEndOfDoc();
1167 // might cause a CreateAnchor or Deselection all
1168 mpImpl
->mpSelEngine
->CursorPosChanging( bSelect
, aTranslatedKeyEvent
.GetKeyCode().IsMod1() );
1170 if ( aOldEnd
!= aPaM
)
1172 mpImpl
->mpTextEngine
->CursorMoved( aOldEnd
.GetPara() );
1174 TextSelection
aNewSelection( mpImpl
->maSelection
);
1175 aNewSelection
.GetEnd() = aPaM
;
1178 // extend the selection
1179 ImpSetSelection( aNewSelection
);
1180 ShowSelection( TextSelection( aOldEnd
, aPaM
) );
1184 aNewSelection
.GetStart() = aPaM
;
1185 ImpSetSelection( aNewSelection
);
1189 return mpImpl
->maSelection
;
1192 void TextView::InsertText( const OUString
& rStr
)
1194 mpImpl
->mpTextEngine
->UndoActionStart();
1196 TextSelection aNewSel
= mpImpl
->mpTextEngine
->ImpInsertText( mpImpl
->maSelection
, rStr
);
1198 ImpSetSelection( aNewSel
);
1200 mpImpl
->mpTextEngine
->UndoActionEnd();
1202 mpImpl
->mpTextEngine
->FormatAndUpdate( this );
1205 TextPaM
TextView::CursorLeft( const TextPaM
& rPaM
, sal_uInt16 nCharacterIteratorMode
)
1207 TextPaM
aPaM( rPaM
);
1209 if ( aPaM
.GetIndex() )
1211 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aPaM
.GetPara() ].get();
1212 css::uno::Reference
< css::i18n::XBreakIterator
> xBI
= mpImpl
->mpTextEngine
->GetBreakIterator();
1213 sal_Int32 nCount
= 1;
1214 aPaM
.GetIndex() = xBI
->previousCharacters( pNode
->GetText(), aPaM
.GetIndex(), mpImpl
->mpTextEngine
->GetLocale(), nCharacterIteratorMode
, nCount
, nCount
);
1216 else if ( aPaM
.GetPara() )
1219 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aPaM
.GetPara() ].get();
1220 aPaM
.GetIndex() = pNode
->GetText().getLength();
1225 TextPaM
TextView::CursorRight( const TextPaM
& rPaM
, sal_uInt16 nCharacterIteratorMode
)
1227 TextPaM
aPaM( rPaM
);
1229 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aPaM
.GetPara() ].get();
1230 if ( aPaM
.GetIndex() < pNode
->GetText().getLength() )
1232 css::uno::Reference
< css::i18n::XBreakIterator
> xBI
= mpImpl
->mpTextEngine
->GetBreakIterator();
1233 sal_Int32 nCount
= 1;
1234 aPaM
.GetIndex() = xBI
->nextCharacters( pNode
->GetText(), aPaM
.GetIndex(), mpImpl
->mpTextEngine
->GetLocale(), nCharacterIteratorMode
, nCount
, nCount
);
1236 else if ( aPaM
.GetPara() < ( mpImpl
->mpTextEngine
->mpDoc
->GetNodes().size()-1) )
1239 aPaM
.GetIndex() = 0;
1245 TextPaM
TextView::CursorWordLeft( const TextPaM
& rPaM
)
1247 TextPaM
aPaM( rPaM
);
1249 if ( aPaM
.GetIndex() )
1251 // tdf#57879 - expand selection to the left to include connector punctuations
1252 mpImpl
->mpTextEngine
->GetWord( rPaM
, &aPaM
);
1253 if ( aPaM
.GetIndex() >= rPaM
.GetIndex() )
1255 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aPaM
.GetPara() ].get();
1256 css::uno::Reference
< css::i18n::XBreakIterator
> xBI
= mpImpl
->mpTextEngine
->GetBreakIterator();
1257 aPaM
.GetIndex() = xBI
->previousWord( pNode
->GetText(), rPaM
.GetIndex(), mpImpl
->mpTextEngine
->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES
).startPos
;
1258 if ( aPaM
.GetIndex() > 0 )
1259 mpImpl
->mpTextEngine
->GetWord( aPaM
, &aPaM
);
1261 aPaM
.GetIndex() = 0;
1264 else if ( aPaM
.GetPara() )
1267 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aPaM
.GetPara() ].get();
1268 aPaM
.GetIndex() = pNode
->GetText().getLength();
1273 TextPaM
TextView::CursorWordRight( const TextPaM
& rPaM
)
1275 TextPaM
aPaM( rPaM
);
1277 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aPaM
.GetPara() ].get();
1278 if ( aPaM
.GetIndex() < pNode
->GetText().getLength() )
1280 css::uno::Reference
< css::i18n::XBreakIterator
> xBI
= mpImpl
->mpTextEngine
->GetBreakIterator();
1281 aPaM
.GetIndex() = xBI
->nextWord( pNode
->GetText(), aPaM
.GetIndex(), mpImpl
->mpTextEngine
->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES
).endPos
;
1282 mpImpl
->mpTextEngine
->GetWord( aPaM
, nullptr, &aPaM
);
1284 else if ( aPaM
.GetPara() < ( mpImpl
->mpTextEngine
->mpDoc
->GetNodes().size()-1) )
1287 aPaM
.GetIndex() = 0;
1293 TextPaM
TextView::ImpDelete( sal_uInt8 nMode
, sal_uInt8 nDelMode
)
1295 if ( mpImpl
->maSelection
.HasRange() ) // only delete selection
1296 return mpImpl
->mpTextEngine
->ImpDeleteText( mpImpl
->maSelection
);
1298 TextPaM aStartPaM
= mpImpl
->maSelection
.GetStart();
1299 TextPaM aEndPaM
= aStartPaM
;
1300 if ( nMode
== DEL_LEFT
)
1302 if ( nDelMode
== DELMODE_SIMPLE
)
1304 aEndPaM
= CursorLeft( aEndPaM
, sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCHARACTER
) );
1306 else if ( nDelMode
== DELMODE_RESTOFWORD
)
1308 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aEndPaM
.GetPara() ].get();
1309 css::uno::Reference
< css::i18n::XBreakIterator
> xBI
= mpImpl
->mpTextEngine
->GetBreakIterator();
1310 css::i18n::Boundary aBoundary
= xBI
->getWordBoundary( pNode
->GetText(), mpImpl
->maSelection
.GetEnd().GetIndex(), mpImpl
->mpTextEngine
->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES
, true );
1311 if ( aBoundary
.startPos
== mpImpl
->maSelection
.GetEnd().GetIndex() )
1312 aBoundary
= xBI
->previousWord( pNode
->GetText(), mpImpl
->maSelection
.GetEnd().GetIndex(), mpImpl
->mpTextEngine
->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES
);
1313 // #i63506# startPos is -1 when the paragraph starts with a tab
1314 aEndPaM
.GetIndex() = std::max
<sal_Int32
>(aBoundary
.startPos
, 0);
1316 else // DELMODE_RESTOFCONTENT
1318 if ( aEndPaM
.GetIndex() != 0 )
1319 aEndPaM
.GetIndex() = 0;
1320 else if ( aEndPaM
.GetPara() )
1322 // previous paragraph
1323 aEndPaM
.GetPara()--;
1324 aEndPaM
.GetIndex() = 0;
1330 if ( nDelMode
== DELMODE_SIMPLE
)
1332 aEndPaM
= CursorRight( aEndPaM
, sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCELL
) );
1334 else if ( nDelMode
== DELMODE_RESTOFWORD
)
1336 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aEndPaM
.GetPara() ].get();
1337 css::uno::Reference
< css::i18n::XBreakIterator
> xBI
= mpImpl
->mpTextEngine
->GetBreakIterator();
1338 css::i18n::Boundary aBoundary
= xBI
->nextWord( pNode
->GetText(), mpImpl
->maSelection
.GetEnd().GetIndex(), mpImpl
->mpTextEngine
->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES
);
1339 aEndPaM
.GetIndex() = aBoundary
.startPos
;
1341 else // DELMODE_RESTOFCONTENT
1343 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aEndPaM
.GetPara() ].get();
1344 if ( aEndPaM
.GetIndex() < pNode
->GetText().getLength() )
1345 aEndPaM
.GetIndex() = pNode
->GetText().getLength();
1346 else if ( aEndPaM
.GetPara() < ( mpImpl
->mpTextEngine
->mpDoc
->GetNodes().size() - 1 ) )
1349 aEndPaM
.GetPara()++;
1350 TextNode
* pNextNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aEndPaM
.GetPara() ].get();
1351 aEndPaM
.GetIndex() = pNextNode
->GetText().getLength();
1356 return mpImpl
->mpTextEngine
->ImpDeleteText( TextSelection( aStartPaM
, aEndPaM
) );
1359 TextPaM
TextView::CursorUp( const TextPaM
& rPaM
)
1361 TextPaM
aPaM( rPaM
);
1364 if ( mpImpl
->mnTravelXPos
== TRAVEL_X_DONTKNOW
)
1366 nX
= mpImpl
->mpTextEngine
->GetEditCursor( rPaM
, false ).Left();
1367 mpImpl
->mnTravelXPos
= static_cast<sal_uInt16
>(nX
)+1;
1370 nX
= mpImpl
->mnTravelXPos
;
1372 TEParaPortion
* pPPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( rPaM
.GetPara() );
1373 std::vector
<TextLine
>::size_type nLine
= pPPortion
->GetLineNumber( rPaM
.GetIndex(), false );
1374 if ( nLine
) // same paragraph
1376 aPaM
.GetIndex() = mpImpl
->mpTextEngine
->GetCharPos( rPaM
.GetPara(), nLine
-1, nX
);
1377 // If we need to go to the end of a line that was wrapped automatically,
1378 // the cursor ends up at the beginning of the 2nd line
1379 // Problem: Last character of an automatically wrapped line = Cursor
1380 TextLine
& rLine
= pPPortion
->GetLines()[ nLine
- 1 ];
1381 if ( aPaM
.GetIndex() && ( aPaM
.GetIndex() == rLine
.GetEnd() ) )
1384 else if ( rPaM
.GetPara() ) // previous paragraph
1387 pPPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( aPaM
.GetPara() );
1388 std::vector
<TextLine
>::size_type nL
= pPPortion
->GetLines().size() - 1;
1389 aPaM
.GetIndex() = mpImpl
->mpTextEngine
->GetCharPos( aPaM
.GetPara(), nL
, nX
+1 );
1395 TextPaM
TextView::CursorDown( const TextPaM
& rPaM
)
1397 TextPaM
aPaM( rPaM
);
1400 if ( mpImpl
->mnTravelXPos
== TRAVEL_X_DONTKNOW
)
1402 nX
= mpImpl
->mpTextEngine
->GetEditCursor( rPaM
, false ).Left();
1403 mpImpl
->mnTravelXPos
= static_cast<sal_uInt16
>(nX
)+1;
1406 nX
= mpImpl
->mnTravelXPos
;
1408 TEParaPortion
* pPPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( rPaM
.GetPara() );
1409 std::vector
<TextLine
>::size_type nLine
= pPPortion
->GetLineNumber( rPaM
.GetIndex(), false );
1410 if ( nLine
< ( pPPortion
->GetLines().size() - 1 ) )
1412 aPaM
.GetIndex() = mpImpl
->mpTextEngine
->GetCharPos( rPaM
.GetPara(), nLine
+1, nX
);
1414 // special case CursorUp
1415 TextLine
& rLine
= pPPortion
->GetLines()[ nLine
+ 1 ];
1416 if ( ( aPaM
.GetIndex() == rLine
.GetEnd() ) && ( aPaM
.GetIndex() > rLine
.GetStart() ) && aPaM
.GetIndex() < pPPortion
->GetNode()->GetText().getLength() )
1419 else if ( rPaM
.GetPara() < ( mpImpl
->mpTextEngine
->mpDoc
->GetNodes().size() - 1 ) ) // next paragraph
1422 pPPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( aPaM
.GetPara() );
1423 aPaM
.GetIndex() = mpImpl
->mpTextEngine
->GetCharPos( aPaM
.GetPara(), 0, nX
+1 );
1424 TextLine
& rLine
= pPPortion
->GetLines().front();
1425 if ( ( aPaM
.GetIndex() == rLine
.GetEnd() ) && ( aPaM
.GetIndex() > rLine
.GetStart() ) && ( pPPortion
->GetLines().size() > 1 ) )
1432 TextPaM
TextView::CursorStartOfLine( const TextPaM
& rPaM
)
1434 TextPaM
aPaM( rPaM
);
1436 TEParaPortion
* pPPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( rPaM
.GetPara() );
1437 std::vector
<TextLine
>::size_type nLine
= pPPortion
->GetLineNumber( aPaM
.GetIndex(), false );
1438 TextLine
& rLine
= pPPortion
->GetLines()[ nLine
];
1439 aPaM
.GetIndex() = rLine
.GetStart();
1444 TextPaM
TextView::CursorEndOfLine( const TextPaM
& rPaM
)
1446 TextPaM
aPaM( rPaM
);
1448 TEParaPortion
* pPPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( rPaM
.GetPara() );
1449 std::vector
<TextLine
>::size_type nLine
= pPPortion
->GetLineNumber( aPaM
.GetIndex(), false );
1450 TextLine
& rLine
= pPPortion
->GetLines()[ nLine
];
1451 aPaM
.GetIndex() = rLine
.GetEnd();
1453 if ( rLine
.GetEnd() > rLine
.GetStart() ) // empty line
1455 sal_Unicode cLastChar
= pPPortion
->GetNode()->GetText()[ aPaM
.GetIndex()-1 ];
1456 if ( ( cLastChar
== ' ' ) && ( aPaM
.GetIndex() != pPPortion
->GetNode()->GetText().getLength() ) )
1458 // for a blank in an automatically-wrapped line it is better to stand before it,
1459 // as the user will intend to stand behind the prior word.
1460 // If there is a change, special case for Pos1 after End!
1467 TextPaM
TextView::CursorStartOfParagraph( const TextPaM
& rPaM
)
1469 TextPaM
aPaM( rPaM
);
1470 aPaM
.GetIndex() = 0;
1474 TextPaM
TextView::CursorEndOfParagraph( const TextPaM
& rPaM
)
1476 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ rPaM
.GetPara() ].get();
1477 TextPaM
aPaM( rPaM
);
1478 aPaM
.GetIndex() = pNode
->GetText().getLength();
1482 TextPaM
TextView::CursorStartOfDoc()
1484 TextPaM
aPaM( 0, 0 );
1488 TextPaM
TextView::CursorEndOfDoc()
1490 const sal_uInt32 nNode
= static_cast<sal_uInt32
>(mpImpl
->mpTextEngine
->mpDoc
->GetNodes().size() - 1);
1491 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ nNode
].get();
1492 TextPaM
aPaM( nNode
, pNode
->GetText().getLength() );
1496 TextPaM
TextView::PageUp( const TextPaM
& rPaM
)
1498 tools::Rectangle aRect
= mpImpl
->mpTextEngine
->PaMtoEditCursor( rPaM
);
1499 Point aTopLeft
= aRect
.TopLeft();
1500 aTopLeft
.AdjustY( -(mpImpl
->mpWindow
->GetOutputSizePixel().Height() * 9/10) );
1501 aTopLeft
.AdjustX(1 );
1502 if ( aTopLeft
.Y() < 0 )
1505 TextPaM aPaM
= mpImpl
->mpTextEngine
->GetPaM( aTopLeft
);
1509 TextPaM
TextView::PageDown( const TextPaM
& rPaM
)
1511 tools::Rectangle aRect
= mpImpl
->mpTextEngine
->PaMtoEditCursor( rPaM
);
1512 Point aBottomRight
= aRect
.BottomRight();
1513 aBottomRight
.AdjustY(mpImpl
->mpWindow
->GetOutputSizePixel().Height() * 9/10 );
1514 aBottomRight
.AdjustX(1 );
1515 tools::Long nHeight
= mpImpl
->mpTextEngine
->GetTextHeight();
1516 if ( aBottomRight
.Y() > nHeight
)
1517 aBottomRight
.setY( nHeight
-1 );
1519 TextPaM aPaM
= mpImpl
->mpTextEngine
->GetPaM( aBottomRight
);
1523 void TextView::ImpShowCursor( bool bGotoCursor
, bool bForceVisCursor
, bool bSpecial
)
1525 if ( mpImpl
->mpTextEngine
->IsFormatting() )
1527 if ( !mpImpl
->mpTextEngine
->GetUpdateMode() )
1529 if ( mpImpl
->mpTextEngine
->IsInUndo() )
1532 mpImpl
->mpTextEngine
->CheckIdleFormatter();
1533 if ( !mpImpl
->mpTextEngine
->IsFormatted() )
1534 mpImpl
->mpTextEngine
->FormatAndUpdate( this );
1536 TextPaM
aPaM( mpImpl
->maSelection
.GetEnd() );
1537 tools::Rectangle aEditCursor
= mpImpl
->mpTextEngine
->PaMtoEditCursor( aPaM
, bSpecial
);
1539 // Remember that we placed the cursor behind the last character of a line
1540 mpImpl
->mbCursorAtEndOfLine
= false;
1543 TEParaPortion
* pParaPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( aPaM
.GetPara() );
1544 mpImpl
->mbCursorAtEndOfLine
=
1545 pParaPortion
->GetLineNumber( aPaM
.GetIndex(), true ) != pParaPortion
->GetLineNumber( aPaM
.GetIndex(), false );
1548 if ( !IsInsertMode() && !mpImpl
->maSelection
.HasRange() )
1550 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aPaM
.GetPara() ].get();
1551 if ( !pNode
->GetText().isEmpty() && ( aPaM
.GetIndex() < pNode
->GetText().getLength() ) )
1553 // If we are behind a portion, and the next portion has other direction, we must change position...
1554 aEditCursor
.SetLeft( mpImpl
->mpTextEngine
->GetEditCursor( aPaM
, false, true ).Left() );
1555 aEditCursor
.SetRight( aEditCursor
.Left() );
1557 TEParaPortion
* pParaPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( aPaM
.GetPara() );
1559 sal_Int32 nTextPortionStart
= 0;
1560 std::size_t nTextPortion
= pParaPortion
->GetTextPortions().FindPortion( aPaM
.GetIndex(), nTextPortionStart
, true );
1561 TETextPortion
* pTextPortion
= pParaPortion
->GetTextPortions()[ nTextPortion
];
1562 if ( pTextPortion
->GetKind() == PORTIONKIND_TAB
)
1564 aEditCursor
.AdjustRight(pTextPortion
->GetWidth() );
1568 TextPaM aNext
= CursorRight( TextPaM( aPaM
.GetPara(), aPaM
.GetIndex() ), sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCELL
) );
1569 aEditCursor
.SetRight( mpImpl
->mpTextEngine
->GetEditCursor( aNext
, true ).Left() );
1574 Size aOutSz
= mpImpl
->mpWindow
->GetOutputSizePixel();
1575 if ( aEditCursor
.GetHeight() > aOutSz
.Height() )
1576 aEditCursor
.SetBottom( aEditCursor
.Top() + aOutSz
.Height() - 1 );
1578 aEditCursor
.AdjustLeft( -1 );
1581 // #i81283# protect maStartDocPos against initialization problems
1582 && aOutSz
.Width() && aOutSz
.Height()
1585 tools::Long nVisStartY
= mpImpl
->maStartDocPos
.Y();
1586 tools::Long nVisEndY
= mpImpl
->maStartDocPos
.Y() + aOutSz
.Height();
1587 tools::Long nVisStartX
= mpImpl
->maStartDocPos
.X();
1588 tools::Long nVisEndX
= mpImpl
->maStartDocPos
.X() + aOutSz
.Width();
1589 tools::Long nMoreX
= aOutSz
.Width() / 4;
1591 Point
aNewStartPos( mpImpl
->maStartDocPos
);
1593 if ( aEditCursor
.Bottom() > nVisEndY
)
1595 aNewStartPos
.AdjustY( aEditCursor
.Bottom() - nVisEndY
);
1597 else if ( aEditCursor
.Top() < nVisStartY
)
1599 aNewStartPos
.AdjustY( -( nVisStartY
- aEditCursor
.Top() ) );
1602 if ( aEditCursor
.Right() >= nVisEndX
)
1604 aNewStartPos
.AdjustX( aEditCursor
.Right() - nVisEndX
);
1606 // do you want some more?
1607 aNewStartPos
.AdjustX(nMoreX
);
1609 else if ( aEditCursor
.Left() <= nVisStartX
)
1611 aNewStartPos
.AdjustX( -( nVisStartX
- aEditCursor
.Left() ) );
1613 // do you want some more?
1614 aNewStartPos
.AdjustX( -nMoreX
);
1617 // X can be wrong for the 'some more' above:
1618 // sal_uLong nMaxTextWidth = mpImpl->mpTextEngine->GetMaxTextWidth();
1619 // if ( !nMaxTextWidth || ( nMaxTextWidth > 0x7FFFFFFF ) )
1620 // nMaxTextWidth = 0x7FFFFFFF;
1621 // long nMaxX = (long)nMaxTextWidth - aOutSz.Width();
1622 tools::Long nMaxX
= mpImpl
->mpTextEngine
->CalcTextWidth() - aOutSz
.Width();
1626 if ( aNewStartPos
.X() < 0 )
1627 aNewStartPos
.setX( 0 );
1628 else if ( aNewStartPos
.X() > nMaxX
)
1629 aNewStartPos
.setX( nMaxX
);
1631 // Y should not be further down than needed
1632 tools::Long nYMax
= mpImpl
->mpTextEngine
->GetTextHeight() - aOutSz
.Height();
1635 if ( aNewStartPos
.Y() > nYMax
)
1636 aNewStartPos
.setY( nYMax
);
1638 if ( aNewStartPos
!= mpImpl
->maStartDocPos
)
1639 Scroll( -(aNewStartPos
.X() - mpImpl
->maStartDocPos
.X()), -(aNewStartPos
.Y() - mpImpl
->maStartDocPos
.Y()) );
1642 if ( aEditCursor
.Right() < aEditCursor
.Left() )
1644 tools::Long n
= aEditCursor
.Left();
1645 aEditCursor
.SetLeft( aEditCursor
.Right() );
1646 aEditCursor
.SetRight( n
);
1649 Point
aPoint( GetWindowPos( !mpImpl
->mpTextEngine
->IsRightToLeft() ? aEditCursor
.TopLeft() : aEditCursor
.TopRight() ) );
1650 mpImpl
->mpCursor
->SetPos( aPoint
);
1651 mpImpl
->mpCursor
->SetSize( aEditCursor
.GetSize() );
1652 if ( bForceVisCursor
&& mpImpl
->mbCursorEnabled
)
1653 mpImpl
->mpCursor
->Show();
1656 void TextView::SetCursorAtPoint( const Point
& rPosPixel
)
1658 mpImpl
->mpTextEngine
->CheckIdleFormatter();
1660 Point aDocPos
= GetDocPos( rPosPixel
);
1662 TextPaM aPaM
= mpImpl
->mpTextEngine
->GetPaM( aDocPos
);
1664 // aTmpNewSel: Diff between old and new; not the new selection
1665 TextSelection
aTmpNewSel( mpImpl
->maSelection
.GetEnd(), aPaM
);
1666 TextSelection
aNewSel( mpImpl
->maSelection
);
1667 aNewSel
.GetEnd() = aPaM
;
1669 if ( !mpImpl
->mpSelEngine
->HasAnchor() )
1671 if ( mpImpl
->maSelection
.GetStart() != aPaM
)
1672 mpImpl
->mpTextEngine
->CursorMoved( mpImpl
->maSelection
.GetStart().GetPara() );
1673 aNewSel
.GetStart() = aPaM
;
1674 ImpSetSelection( aNewSel
);
1678 ImpSetSelection( aNewSel
);
1679 ShowSelection( aTmpNewSel
);
1682 bool bForceCursor
= !mpImpl
->mpDDInfo
; // && !mbInSelection
1683 ImpShowCursor( mpImpl
->mbAutoScroll
, bForceCursor
, false );
1686 bool TextView::IsSelectionAtPoint( const Point
& rPosPixel
)
1688 Point aDocPos
= GetDocPos( rPosPixel
);
1689 TextPaM aPaM
= mpImpl
->mpTextEngine
->GetPaM( aDocPos
);
1690 // BeginDrag is only called, however, if IsSelectionAtPoint()
1691 // Problem: IsSelectionAtPoint is not called by Command()
1692 // if before MBDown returned false.
1693 return IsInSelection( aPaM
);
1696 bool TextView::IsInSelection( const TextPaM
& rPaM
)
1698 TextSelection aSel
= mpImpl
->maSelection
;
1701 const sal_uInt32 nStartNode
= aSel
.GetStart().GetPara();
1702 const sal_uInt32 nEndNode
= aSel
.GetEnd().GetPara();
1703 const sal_uInt32 nCurNode
= rPaM
.GetPara();
1705 if ( ( nCurNode
> nStartNode
) && ( nCurNode
< nEndNode
) )
1708 if ( nStartNode
== nEndNode
)
1710 if ( nCurNode
== nStartNode
)
1711 if ( ( rPaM
.GetIndex() >= aSel
.GetStart().GetIndex() ) && ( rPaM
.GetIndex() < aSel
.GetEnd().GetIndex() ) )
1714 else if ( ( nCurNode
== nStartNode
) && ( rPaM
.GetIndex() >= aSel
.GetStart().GetIndex() ) )
1716 else if ( ( nCurNode
== nEndNode
) && ( rPaM
.GetIndex() < aSel
.GetEnd().GetIndex() ) )
1722 void TextView::ImpHideDDCursor()
1724 if ( mpImpl
->mpDDInfo
&& mpImpl
->mpDDInfo
->mbVisCursor
)
1726 mpImpl
->mpDDInfo
->maCursor
.Hide();
1727 mpImpl
->mpDDInfo
->mbVisCursor
= false;
1731 void TextView::ImpShowDDCursor()
1733 if ( !mpImpl
->mpDDInfo
->mbVisCursor
)
1735 tools::Rectangle aCursor
= mpImpl
->mpTextEngine
->PaMtoEditCursor( mpImpl
->mpDDInfo
->maDropPos
, true );
1736 aCursor
.AdjustRight( 1 );
1737 aCursor
.SetPos( GetWindowPos( aCursor
.TopLeft() ) );
1739 mpImpl
->mpDDInfo
->maCursor
.SetWindow( mpImpl
->mpWindow
);
1740 mpImpl
->mpDDInfo
->maCursor
.SetPos( aCursor
.TopLeft() );
1741 mpImpl
->mpDDInfo
->maCursor
.SetSize( aCursor
.GetSize() );
1742 mpImpl
->mpDDInfo
->maCursor
.Show();
1743 mpImpl
->mpDDInfo
->mbVisCursor
= true;
1747 void TextView::SetPaintSelection( bool bPaint
)
1749 if ( bPaint
!= mpImpl
->mbPaintSelection
)
1751 mpImpl
->mbPaintSelection
= bPaint
;
1752 ShowSelection( mpImpl
->maSelection
);
1756 void TextView::Read( SvStream
& rInput
)
1758 mpImpl
->mpTextEngine
->Read( rInput
, &mpImpl
->maSelection
);
1762 bool TextView::ImplTruncateNewText( OUString
& rNewText
) const
1764 bool bTruncated
= false;
1766 const sal_Int32 nMaxLen
= mpImpl
->mpTextEngine
->GetMaxTextLen();
1767 // 0 means unlimited
1770 const sal_Int32 nCurLen
= mpImpl
->mpTextEngine
->GetTextLen();
1772 const sal_Int32 nNewLen
= rNewText
.getLength();
1773 if ( nCurLen
+ nNewLen
> nMaxLen
)
1775 // see how much text will be replaced
1776 const sal_Int32 nSelLen
= mpImpl
->mpTextEngine
->GetTextLen( mpImpl
->maSelection
);
1777 if ( nCurLen
+ nNewLen
- nSelLen
> nMaxLen
)
1779 const sal_Int32 nTruncatedLen
= nMaxLen
- (nCurLen
- nSelLen
);
1780 rNewText
= rNewText
.copy( 0, nTruncatedLen
);
1788 bool TextView::ImplCheckTextLen( const OUString
& rNewText
)
1791 if ( mpImpl
->mpTextEngine
->GetMaxTextLen() )
1793 sal_Int32 n
= mpImpl
->mpTextEngine
->GetTextLen() + rNewText
.getLength();
1794 if ( n
> mpImpl
->mpTextEngine
->GetMaxTextLen() )
1796 // calculate how much text is being deleted
1797 n
-= mpImpl
->mpTextEngine
->GetTextLen( mpImpl
->maSelection
);
1798 if ( n
> mpImpl
->mpTextEngine
->GetMaxTextLen() )
1805 void TextView::dragGestureRecognized( const css::datatransfer::dnd::DragGestureEvent
& rDGE
)
1807 if ( !mpImpl
->mbClickedInSelection
)
1810 SolarMutexGuard aVclGuard
;
1812 SAL_WARN_IF( !mpImpl
->maSelection
.HasRange(), "vcl", "TextView::dragGestureRecognized: mpImpl->mbClickedInSelection, but no selection?" );
1814 mpImpl
->mpDDInfo
.reset(new TextDDInfo
);
1815 mpImpl
->mpDDInfo
->mbStarterOfDD
= true;
1817 TETextDataObject
* pDataObj
= new TETextDataObject( GetSelected() );
1819 mpImpl
->mpCursor
->Hide();
1821 sal_Int8 nActions
= css::datatransfer::dnd::DNDConstants::ACTION_COPY
;
1822 if ( !IsReadOnly() )
1823 nActions
|= css::datatransfer::dnd::DNDConstants::ACTION_MOVE
;
1824 rDGE
.DragSource
->startDrag( rDGE
, nActions
, 0 /*cursor*/, 0 /*image*/, pDataObj
, mpImpl
->mxDnDListener
);
1827 void TextView::dragDropEnd( const css::datatransfer::dnd::DragSourceDropEvent
& )
1830 mpImpl
->mpDDInfo
.reset();
1833 void TextView::drop( const css::datatransfer::dnd::DropTargetDropEvent
& rDTDE
)
1835 SolarMutexGuard aVclGuard
;
1837 if ( !mpImpl
->mbReadOnly
&& mpImpl
->mpDDInfo
)
1841 // Data for deleting after DROP_MOVE:
1842 TextSelection
aPrevSel( mpImpl
->maSelection
);
1844 const sal_uInt32 nPrevParaCount
= mpImpl
->mpTextEngine
->GetParagraphCount();
1845 const sal_Int32 nPrevStartParaLen
= mpImpl
->mpTextEngine
->GetTextLen( aPrevSel
.GetStart().GetPara() );
1847 bool bStarterOfDD
= false;
1848 for ( sal_uInt16 nView
= mpImpl
->mpTextEngine
->GetViewCount(); nView
&& !bStarterOfDD
; )
1849 bStarterOfDD
= mpImpl
->mpTextEngine
->GetView( --nView
)->mpImpl
->mpDDInfo
&& mpImpl
->mpTextEngine
->GetView( nView
)->mpImpl
->mpDDInfo
->mbStarterOfDD
;
1852 ImpSetSelection( mpImpl
->mpDDInfo
->maDropPos
);
1854 mpImpl
->mpTextEngine
->UndoActionStart();
1857 css::uno::Reference
< css::datatransfer::XTransferable
> xDataObj
= rDTDE
.Transferable
;
1858 if ( xDataObj
.is() )
1860 css::datatransfer::DataFlavor aFlavor
;
1861 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING
, aFlavor
);
1862 if ( xDataObj
->isDataFlavorSupported( aFlavor
) )
1864 css::uno::Any aData
= xDataObj
->getTransferData( aFlavor
);
1866 aData
>>= aOUString
;
1867 aText
= convertLineEnd(aOUString
, LINEEND_LF
);
1871 if ( !aText
.isEmpty() && ( aText
[ aText
.getLength()-1 ] == LINE_SEP
) )
1872 aText
= aText
.copy(0, aText
.getLength()-1);
1874 if ( ImplCheckTextLen( aText
) )
1875 ImpSetSelection( mpImpl
->mpTextEngine
->ImpInsertText( mpImpl
->mpDDInfo
->maDropPos
, aText
) );
1877 if ( aPrevSel
.HasRange() &&
1878 (( rDTDE
.DropAction
& css::datatransfer::dnd::DNDConstants::ACTION_MOVE
) || !bStarterOfDD
) )
1880 // adjust selection if necessary
1881 if ( ( mpImpl
->mpDDInfo
->maDropPos
.GetPara() < aPrevSel
.GetStart().GetPara() ) ||
1882 ( ( mpImpl
->mpDDInfo
->maDropPos
.GetPara() == aPrevSel
.GetStart().GetPara() )
1883 && ( mpImpl
->mpDDInfo
->maDropPos
.GetIndex() < aPrevSel
.GetStart().GetIndex() ) ) )
1885 const sal_uInt32 nNewParasBeforeSelection
=
1886 mpImpl
->mpTextEngine
->GetParagraphCount() - nPrevParaCount
;
1888 aPrevSel
.GetStart().GetPara() += nNewParasBeforeSelection
;
1889 aPrevSel
.GetEnd().GetPara() += nNewParasBeforeSelection
;
1891 if ( mpImpl
->mpDDInfo
->maDropPos
.GetPara() == aPrevSel
.GetStart().GetPara() )
1893 const sal_Int32 nNewChars
=
1894 mpImpl
->mpTextEngine
->GetTextLen( aPrevSel
.GetStart().GetPara() ) - nPrevStartParaLen
;
1896 aPrevSel
.GetStart().GetIndex() += nNewChars
;
1897 if ( aPrevSel
.GetStart().GetPara() == aPrevSel
.GetEnd().GetPara() )
1898 aPrevSel
.GetEnd().GetIndex() += nNewChars
;
1903 // adjust current selection
1904 TextPaM aPaM
= mpImpl
->maSelection
.GetStart();
1905 aPaM
.GetPara() -= ( aPrevSel
.GetEnd().GetPara() - aPrevSel
.GetStart().GetPara() );
1906 if ( aPrevSel
.GetEnd().GetPara() == mpImpl
->mpDDInfo
->maDropPos
.GetPara() )
1908 aPaM
.GetIndex() -= aPrevSel
.GetEnd().GetIndex();
1909 if ( aPrevSel
.GetStart().GetPara() == mpImpl
->mpDDInfo
->maDropPos
.GetPara() )
1910 aPaM
.GetIndex() += aPrevSel
.GetStart().GetIndex();
1912 ImpSetSelection( aPaM
);
1915 mpImpl
->mpTextEngine
->ImpDeleteText( aPrevSel
);
1918 mpImpl
->mpTextEngine
->UndoActionEnd();
1920 mpImpl
->mpDDInfo
.reset();
1922 mpImpl
->mpTextEngine
->FormatAndUpdate( this );
1924 mpImpl
->mpTextEngine
->Broadcast( TextHint( SfxHintId::TextModified
) );
1926 rDTDE
.Context
->dropComplete( false/*bChanges*/ );
1929 void TextView::dragEnter( const css::datatransfer::dnd::DropTargetDragEnterEvent
& )
1933 void TextView::dragExit( const css::datatransfer::dnd::DropTargetEvent
& )
1935 SolarMutexGuard aVclGuard
;
1939 void TextView::dragOver( const css::datatransfer::dnd::DropTargetDragEvent
& rDTDE
)
1941 SolarMutexGuard aVclGuard
;
1943 if (!mpImpl
->mpDDInfo
)
1944 mpImpl
->mpDDInfo
.reset(new TextDDInfo
);
1946 TextPaM aPrevDropPos
= mpImpl
->mpDDInfo
->maDropPos
;
1947 Point
aMousePos( rDTDE
.LocationX
, rDTDE
.LocationY
);
1948 Point aDocPos
= GetDocPos( aMousePos
);
1949 mpImpl
->mpDDInfo
->maDropPos
= mpImpl
->mpTextEngine
->GetPaM( aDocPos
);
1951 // Don't drop in selection or in read only engine
1952 if ( IsReadOnly() || IsInSelection( mpImpl
->mpDDInfo
->maDropPos
))
1955 rDTDE
.Context
->rejectDrag();
1959 // delete old Cursor
1960 if ( !mpImpl
->mpDDInfo
->mbVisCursor
|| ( aPrevDropPos
!= mpImpl
->mpDDInfo
->maDropPos
) )
1965 rDTDE
.Context
->acceptDrag( rDTDE
.DropAction
);
1969 Point
TextView::ImpGetOutputStartPos( const Point
& rStartDocPos
) const
1971 Point
aStartPos( -rStartDocPos
.X(), -rStartDocPos
.Y() );
1972 if ( mpImpl
->mpTextEngine
->IsRightToLeft() )
1974 Size aSz
= mpImpl
->mpWindow
->GetOutputSizePixel();
1975 aStartPos
.setX( rStartDocPos
.X() + aSz
.Width() - 1 ); // -1: Start is 0
1980 Point
TextView::GetDocPos( const Point
& rWindowPos
) const
1982 // Window Position => Document Position
1986 aPoint
.setY( rWindowPos
.Y() + mpImpl
->maStartDocPos
.Y() );
1988 if ( !mpImpl
->mpTextEngine
->IsRightToLeft() )
1990 aPoint
.setX( rWindowPos
.X() + mpImpl
->maStartDocPos
.X() );
1994 Size aSz
= mpImpl
->mpWindow
->GetOutputSizePixel();
1995 aPoint
.setX( ( aSz
.Width() - 1 ) - rWindowPos
.X() + mpImpl
->maStartDocPos
.X() );
2001 Point
TextView::GetWindowPos( const Point
& rDocPos
) const
2003 // Document Position => Window Position
2007 aPoint
.setY( rDocPos
.Y() - mpImpl
->maStartDocPos
.Y() );
2009 if ( !mpImpl
->mpTextEngine
->IsRightToLeft() )
2011 aPoint
.setX( rDocPos
.X() - mpImpl
->maStartDocPos
.X() );
2015 Size aSz
= mpImpl
->mpWindow
->GetOutputSizePixel();
2016 aPoint
.setX( ( aSz
.Width() - 1 ) - ( rDocPos
.X() - mpImpl
->maStartDocPos
.X() ) );
2022 sal_Int32
TextView::GetLineNumberOfCursorInSelection() const
2025 sal_Int32 nLineNo
= -1;
2026 if( mpImpl
->mbCursorEnabled
)
2028 TextPaM aPaM
= GetSelection().GetEnd();
2029 TEParaPortion
* pPPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( aPaM
.GetPara() );
2030 nLineNo
= pPPortion
->GetLineNumber( aPaM
.GetIndex(), false );
2031 //TODO: std::vector<TextLine>::size_type -> sal_Int32!
2032 if( mpImpl
->mbCursorAtEndOfLine
)
2038 // (+) class TextSelFunctionSet
2040 TextSelFunctionSet::TextSelFunctionSet( TextView
* pView
)
2045 void TextSelFunctionSet::BeginDrag()
2049 void TextSelFunctionSet::CreateAnchor()
2051 // TextSelection aSel( mpView->GetSelection() );
2052 // aSel.GetStart() = aSel.GetEnd();
2053 // mpView->SetSelection( aSel );
2055 // may not be followed by ShowCursor
2056 mpView
->HideSelection();
2057 mpView
->ImpSetSelection( mpView
->mpImpl
->maSelection
.GetEnd() );
2060 void TextSelFunctionSet::SetCursorAtPoint( const Point
& rPointPixel
, bool )
2062 mpView
->SetCursorAtPoint( rPointPixel
);
2065 bool TextSelFunctionSet::IsSelectionAtPoint( const Point
& rPointPixel
)
2067 return mpView
->IsSelectionAtPoint( rPointPixel
);
2070 void TextSelFunctionSet::DeselectAll()
2075 void TextSelFunctionSet::DeselectAtPoint( const Point
& )
2077 // only for multiple selection
2080 void TextSelFunctionSet::DestroyAnchor()
2082 // only for multiple selection
2084 TextEngine
* TextView::GetTextEngine() const
2085 { return mpImpl
->mpTextEngine
; }
2086 vcl::Window
* TextView::GetWindow() const
2087 { return mpImpl
->mpWindow
; }
2088 void TextView::EnableCursor( bool bEnable
)
2089 { mpImpl
->mbCursorEnabled
= bEnable
; }
2090 bool TextView::IsCursorEnabled() const
2091 { return mpImpl
->mbCursorEnabled
; }
2092 void TextView::SetStartDocPos( const Point
& rPos
)
2093 { mpImpl
->maStartDocPos
= rPos
; }
2094 const Point
& TextView::GetStartDocPos() const
2095 { return mpImpl
->maStartDocPos
; }
2096 void TextView::SetAutoIndentMode( bool bAutoIndent
)
2097 { mpImpl
->mbAutoIndent
= bAutoIndent
; }
2098 bool TextView::IsReadOnly() const
2099 { return mpImpl
->mbReadOnly
; }
2100 void TextView::SetAutoScroll( bool bAutoScroll
)
2101 { mpImpl
->mbAutoScroll
= bAutoScroll
; }
2102 bool TextView::IsAutoScroll() const
2103 { return mpImpl
->mbAutoScroll
; }
2104 bool TextView::HasSelection() const
2105 { return mpImpl
->maSelection
.HasRange(); }
2106 bool TextView::IsInsertMode() const
2107 { return mpImpl
->mbInsertMode
; }
2109 void TextView::MatchGroup()
2111 TextSelection
aTmpSel( GetSelection() );
2113 if ( ( aTmpSel
.GetStart().GetPara() != aTmpSel
.GetEnd().GetPara() ) ||
2114 ( ( aTmpSel
.GetEnd().GetIndex() - aTmpSel
.GetStart().GetIndex() ) > 1 ) )
2119 TextSelection aMatchSel
= static_cast<ExtTextEngine
*>(GetTextEngine())->MatchGroup( aTmpSel
.GetStart() );
2120 if ( aMatchSel
.HasRange() )
2121 SetSelection( aMatchSel
);
2124 void TextView::CenterPaM( const TextPaM
& rPaM
)
2126 // Get textview size and the corresponding y-coordinates
2127 Size aOutSz
= mpImpl
->mpWindow
->GetOutputSizePixel();
2128 tools::Long nVisStartY
= mpImpl
->maStartDocPos
.Y();
2129 tools::Long nVisEndY
= mpImpl
->maStartDocPos
.Y() + aOutSz
.Height();
2131 // Retrieve the coordinates of the PaM
2132 tools::Rectangle aRect
= mpImpl
->mpTextEngine
->PaMtoEditCursor(rPaM
);
2134 // Recalculate the offset of the center y-coordinates and scroll
2135 Scroll(0, (nVisStartY
+ nVisEndY
) / 2 - aRect
.TopLeft().getY());
2138 bool TextView::Search( const i18nutil::SearchOptions
& rSearchOptions
, bool bForward
)
2140 bool bFound
= false;
2141 TextSelection
aSel( GetSelection() );
2142 if ( static_cast<ExtTextEngine
*>(GetTextEngine())->Search( aSel
, rSearchOptions
, bForward
) )
2145 // First add the beginning of the word to the selection,
2146 // so that the whole word is in the visible region.
2147 SetSelection( aSel
.GetStart() );
2148 ShowCursor( true, false );
2152 aSel
= GetSelection().GetEnd();
2155 SetSelection( aSel
);
2156 // tdf#49482: Move the start of the selection to the center of the textview
2159 CenterPaM( aSel
.GetStart() );
2166 sal_uInt16
TextView::Replace( const i18nutil::SearchOptions
& rSearchOptions
, bool bAll
, bool bForward
)
2168 sal_uInt16 nFound
= 0;
2172 if ( GetSelection().HasRange() )
2174 InsertText( rSearchOptions
.replaceString
);
2176 Search( rSearchOptions
, bForward
); // right away to the next
2180 if( Search( rSearchOptions
, bForward
) )
2186 // the writer replaces all, from beginning to end
2188 ExtTextEngine
* pTextEngine
= static_cast<ExtTextEngine
*>(GetTextEngine());
2193 bool bSearchInSelection
= (0 != (rSearchOptions
.searchFlag
& css::util::SearchFlags::REG_NOT_BEGINOFLINE
) );
2194 if ( bSearchInSelection
)
2196 aSel
= GetSelection();
2200 TextSelection
aSearchSel( aSel
);
2202 bool bFound
= pTextEngine
->Search( aSel
, rSearchOptions
);
2204 pTextEngine
->UndoActionStart();
2209 TextPaM aNewStart
= pTextEngine
->ImpInsertText( aSel
, rSearchOptions
.replaceString
);
2210 // tdf#64690 - extend selection to include inserted text portions
2211 if ( aSel
.GetEnd().GetPara() == aSearchSel
.GetEnd().GetPara() )
2213 aSearchSel
.GetEnd().GetIndex() += rSearchOptions
.replaceString
.getLength() - 1;
2216 aSel
.GetStart() = aNewStart
;
2217 bFound
= pTextEngine
->Search( aSel
, rSearchOptions
);
2221 SetSelection( aSel
.GetStart() );
2222 pTextEngine
->FormatAndUpdate( this );
2223 pTextEngine
->UndoActionEnd();
2229 bool TextView::ImpIndentBlock( bool bRight
)
2233 TextSelection aSel
= GetSelection();
2237 GetTextEngine()->UndoActionStart();
2239 const sal_uInt32 nStartPara
= aSel
.GetStart().GetPara();
2240 sal_uInt32 nEndPara
= aSel
.GetEnd().GetPara();
2241 if ( aSel
.HasRange() && !aSel
.GetEnd().GetIndex() )
2243 nEndPara
--; // do not indent
2246 for ( sal_uInt32 nPara
= nStartPara
; nPara
<= nEndPara
; ++nPara
)
2251 GetTextEngine()->ImpInsertText( TextPaM( nPara
, 0 ), '\t' );
2256 // remove Tabs/Blanks
2257 OUString aText
= GetTextEngine()->GetText( nPara
);
2258 if ( !aText
.isEmpty() && (
2259 ( aText
[ 0 ] == '\t' ) ||
2260 ( aText
[ 0 ] == ' ' ) ) )
2262 GetTextEngine()->ImpDeleteText( TextSelection( TextPaM( nPara
, 0 ), TextPaM( nPara
, 1 ) ) );
2268 GetTextEngine()->UndoActionEnd();
2270 bool bRange
= aSel
.HasRange();
2273 ++aSel
.GetStart().GetIndex();
2274 if ( bRange
&& ( aSel
.GetEnd().GetPara() == nEndPara
) )
2275 ++aSel
.GetEnd().GetIndex();
2279 if ( aSel
.GetStart().GetIndex() )
2280 --aSel
.GetStart().GetIndex();
2281 if ( bRange
&& aSel
.GetEnd().GetIndex() )
2282 --aSel
.GetEnd().GetIndex();
2285 ImpSetSelection( aSel
);
2286 GetTextEngine()->FormatAndUpdate( this );
2291 bool TextView::IndentBlock()
2293 return ImpIndentBlock( true );
2296 bool TextView::UnindentBlock()
2298 return ImpIndentBlock( false );
2302 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */