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/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() )
309 mpImpl
->mpCursor
->Hide();
311 SAL_WARN_IF( mpImpl
->mpTextEngine
->mpIdleFormatter
->IsActive(), "vcl", "ImpHighlight: Not formatted!" );
313 tools::Rectangle
aVisArea( mpImpl
->maStartDocPos
, mpImpl
->mpWindow
->GetOutputSizePixel() );
315 const sal_uInt32 nStartPara
= aSel
.GetStart().GetPara();
316 const sal_uInt32 nEndPara
= aSel
.GetEnd().GetPara();
317 for ( sal_uInt32 nPara
= 0; nPara
<= nEndPara
; ++nPara
)
319 const long nParaHeight
= mpImpl
->mpTextEngine
->CalcParaHeight( nPara
);
320 if ( ( nPara
>= nStartPara
) && ( ( nY
+ nParaHeight
) > aVisArea
.Top() ) )
322 TEParaPortion
* pTEParaPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( nPara
);
323 std::vector
<TextLine
>::size_type nStartLine
= 0;
324 std::vector
<TextLine
>::size_type nEndLine
= pTEParaPortion
->GetLines().size() -1;
325 if ( nPara
== nStartPara
)
326 nStartLine
= pTEParaPortion
->GetLineNumber( aSel
.GetStart().GetIndex(), false );
327 if ( nPara
== nEndPara
)
328 nEndLine
= pTEParaPortion
->GetLineNumber( aSel
.GetEnd().GetIndex(), true );
330 // iterate over all lines
331 for ( std::vector
<TextLine
>::size_type nLine
= nStartLine
; nLine
<= nEndLine
; nLine
++ )
333 TextLine
& rLine
= pTEParaPortion
->GetLines()[ nLine
];
334 sal_Int32 nStartIndex
= rLine
.GetStart();
335 sal_Int32 nEndIndex
= rLine
.GetEnd();
336 if ( ( nPara
== nStartPara
) && ( nLine
== nStartLine
) )
337 nStartIndex
= aSel
.GetStart().GetIndex();
338 if ( ( nPara
== nEndPara
) && ( nLine
== nEndLine
) )
339 nEndIndex
= aSel
.GetEnd().GetIndex();
341 // possible if at the beginning of a wrapped line
342 if ( nEndIndex
< nStartIndex
)
343 nEndIndex
= nStartIndex
;
345 tools::Rectangle
aTmpRect( mpImpl
->mpTextEngine
->GetEditCursor( TextPaM( nPara
, nStartIndex
), false ) );
346 aTmpRect
.AdjustTop(nY
);
347 aTmpRect
.AdjustBottom(nY
);
348 Point
aTopLeft( aTmpRect
.TopLeft() );
350 aTmpRect
= mpImpl
->mpTextEngine
->GetEditCursor( TextPaM( nPara
, nEndIndex
), true );
351 aTmpRect
.AdjustTop(nY
);
352 aTmpRect
.AdjustBottom(nY
);
353 Point
aBottomRight( aTmpRect
.BottomRight() );
354 aBottomRight
.AdjustX( -1 );
356 // only paint if in the visible region
357 if ( ( aTopLeft
.X() < aBottomRight
.X() ) && ( aBottomRight
.Y() >= aVisArea
.Top() ) )
359 Point
aPnt1( GetWindowPos( aTopLeft
) );
360 Point
aPnt2( GetWindowPos( aBottomRight
) );
362 tools::Rectangle
aRect( aPnt1
, aPnt2
);
363 mpImpl
->mpWindow
->Invert( aRect
);
369 if ( nY
>= aVisArea
.Bottom() )
375 void TextView::ImpSetSelection( const TextSelection
& rSelection
)
377 if (rSelection
!= mpImpl
->maSelection
)
379 bool bCaret
= false, bSelection
= false;
380 const TextPaM
&rEnd
= rSelection
.GetEnd();
381 const TextPaM
&rOldEnd
= mpImpl
->maSelection
.GetEnd();
382 bool bGap
= rSelection
.HasRange(), bOldGap
= mpImpl
->maSelection
.HasRange();
388 mpImpl
->maSelection
= rSelection
;
391 mpImpl
->mpTextEngine
->Broadcast(TextHint(SfxHintId::TextViewSelectionChanged
));
394 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() )
419 if ( mpImpl
->mbHighlightSelection
)
421 ImpHighlight( *pRangeOrSelection
);
425 if( mpImpl
->mpWindow
->IsPaintTransparent() )
426 mpImpl
->mpWindow
->Invalidate();
429 TextSelection
aRange( *pRangeOrSelection
);
431 bool bVisCursor
= mpImpl
->mpCursor
->IsVisible();
432 mpImpl
->mpCursor
->Hide();
435 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 ) )
720 if ( rMouseEvent
.IsMod2() )
723 ImpSetSelection( mpImpl
->maSelection
.GetEnd() );
724 SetCursorAtPoint( rMouseEvent
.GetPosPixel() ); // not set by SelectionEngine for MOD2
727 if ( rMouseEvent
.GetClicks() == 2 )
730 if ( mpImpl
->maSelection
.GetEnd().GetIndex() < mpImpl
->mpTextEngine
->GetTextLen( mpImpl
->maSelection
.GetEnd().GetPara() ) )
733 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ mpImpl
->maSelection
.GetEnd().GetPara() ].get();
734 css::uno::Reference
< css::i18n::XBreakIterator
> xBI
= mpImpl
->mpTextEngine
->GetBreakIterator();
735 css::i18n::Boundary aBoundary
= xBI
->getWordBoundary( pNode
->GetText(), mpImpl
->maSelection
.GetEnd().GetIndex(), mpImpl
->mpTextEngine
->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES
, true );
736 TextSelection
aNewSel( mpImpl
->maSelection
);
737 aNewSel
.GetStart().GetIndex() = aBoundary
.startPos
;
738 aNewSel
.GetEnd().GetIndex() = aBoundary
.endPos
;
739 ImpSetSelection( aNewSel
);
744 else if ( rMouseEvent
.GetClicks() == 3 )
747 if ( mpImpl
->maSelection
.GetStart().GetIndex() || ( mpImpl
->maSelection
.GetEnd().GetIndex() < mpImpl
->mpTextEngine
->GetTextLen( mpImpl
->maSelection
.GetEnd().GetPara() ) ) )
750 TextSelection
aNewSel( mpImpl
->maSelection
);
751 aNewSel
.GetStart().GetIndex() = 0;
752 aNewSel
.GetEnd().GetIndex() = mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ mpImpl
->maSelection
.GetEnd().GetPara() ]->GetText().getLength();
753 ImpSetSelection( aNewSel
);
761 void TextView::MouseMove( const MouseEvent
& rMouseEvent
)
763 mpImpl
->mnTravelXPos
= TRAVEL_X_DONTKNOW
;
764 mpImpl
->mpSelEngine
->SelMouseMove( rMouseEvent
);
767 void TextView::Command( const CommandEvent
& rCEvt
)
769 mpImpl
->mpTextEngine
->CheckIdleFormatter(); // for fast typing and MouseButtonDown
770 mpImpl
->mpTextEngine
->SetActiveView( this );
772 if ( rCEvt
.GetCommand() == CommandEventId::StartExtTextInput
)
775 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ GetSelection().GetEnd().GetPara() ].get();
776 mpImpl
->mpTextEngine
->mpIMEInfos
= std::make_unique
<TEIMEInfos
>( GetSelection().GetEnd(), pNode
->GetText().copy( GetSelection().GetEnd().GetIndex() ) );
777 mpImpl
->mpTextEngine
->mpIMEInfos
->bWasCursorOverwrite
= !IsInsertMode();
779 else if ( rCEvt
.GetCommand() == CommandEventId::EndExtTextInput
)
781 SAL_WARN_IF( !mpImpl
->mpTextEngine
->mpIMEInfos
, "vcl", "CommandEventId::EndExtTextInput => No Start ?" );
782 if( mpImpl
->mpTextEngine
->mpIMEInfos
)
784 TEParaPortion
* pPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
.GetPara() );
785 pPortion
->MarkSelectionInvalid( mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
.GetIndex() );
787 bool bInsertMode
= !mpImpl
->mpTextEngine
->mpIMEInfos
->bWasCursorOverwrite
;
789 mpImpl
->mpTextEngine
->mpIMEInfos
.reset();
791 mpImpl
->mpTextEngine
->TextModified();
792 mpImpl
->mpTextEngine
->FormatAndUpdate( this );
794 SetInsertMode( bInsertMode
);
796 if ( mpImpl
->mpTextEngine
->IsModified() )
797 mpImpl
->mpTextEngine
->Broadcast( TextHint( SfxHintId::TextModified
) );
800 else if ( rCEvt
.GetCommand() == CommandEventId::ExtTextInput
)
802 SAL_WARN_IF( !mpImpl
->mpTextEngine
->mpIMEInfos
, "vcl", "CommandEventId::ExtTextInput => No Start ?" );
803 if( mpImpl
->mpTextEngine
->mpIMEInfos
)
805 const CommandExtTextInputData
* pData
= rCEvt
.GetExtTextInputData();
807 if ( !pData
->IsOnlyCursorChanged() )
809 TextSelection
aSelect( mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
);
810 aSelect
.GetEnd().GetIndex() += mpImpl
->mpTextEngine
->mpIMEInfos
->nLen
;
811 aSelect
= mpImpl
->mpTextEngine
->ImpDeleteText( aSelect
);
812 aSelect
= mpImpl
->mpTextEngine
->ImpInsertText( aSelect
, pData
->GetText() );
814 if ( mpImpl
->mpTextEngine
->mpIMEInfos
->bWasCursorOverwrite
)
816 const sal_Int32 nOldIMETextLen
= mpImpl
->mpTextEngine
->mpIMEInfos
->nLen
;
817 const sal_Int32 nNewIMETextLen
= pData
->GetText().getLength();
819 if ( ( nOldIMETextLen
> nNewIMETextLen
) &&
820 ( nNewIMETextLen
< mpImpl
->mpTextEngine
->mpIMEInfos
->aOldTextAfterStartPos
.getLength() ) )
822 // restore old characters
823 sal_Int32 nRestore
= nOldIMETextLen
- nNewIMETextLen
;
824 TextPaM
aPaM( mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
);
825 aPaM
.GetIndex() += nNewIMETextLen
;
826 mpImpl
->mpTextEngine
->ImpInsertText( aPaM
, mpImpl
->mpTextEngine
->mpIMEInfos
->aOldTextAfterStartPos
.copy( nNewIMETextLen
, nRestore
) );
828 else if ( ( nOldIMETextLen
< nNewIMETextLen
) &&
829 ( nOldIMETextLen
< mpImpl
->mpTextEngine
->mpIMEInfos
->aOldTextAfterStartPos
.getLength() ) )
832 const sal_Int32 nOverwrite
= std::min( nNewIMETextLen
, mpImpl
->mpTextEngine
->mpIMEInfos
->aOldTextAfterStartPos
.getLength() ) - nOldIMETextLen
;
833 SAL_WARN_IF( !nOverwrite
|| (nOverwrite
>= 0xFF00), "vcl", "IME Overwrite?!" );
834 TextPaM
aPaM( mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
);
835 aPaM
.GetIndex() += nNewIMETextLen
;
836 TextSelection
aSel( aPaM
);
837 aSel
.GetEnd().GetIndex() += nOverwrite
;
838 mpImpl
->mpTextEngine
->ImpDeleteText( aSel
);
842 if ( pData
->GetTextAttr() )
844 mpImpl
->mpTextEngine
->mpIMEInfos
->CopyAttribs( pData
->GetTextAttr(), pData
->GetText().getLength() );
848 mpImpl
->mpTextEngine
->mpIMEInfos
->DestroyAttribs();
851 TEParaPortion
* pPPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
.GetPara() );
852 pPPortion
->MarkSelectionInvalid( mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
.GetIndex() );
853 mpImpl
->mpTextEngine
->FormatAndUpdate( this );
856 TextSelection aNewSel
= TextPaM( mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
.GetPara(), mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
.GetIndex()+pData
->GetCursorPos() );
857 SetSelection( aNewSel
);
858 SetInsertMode( !pData
->IsCursorOverwrite() );
860 if ( pData
->IsCursorVisible() )
866 else if ( rCEvt
.GetCommand() == CommandEventId::CursorPos
)
868 if ( mpImpl
->mpTextEngine
->mpIMEInfos
&& mpImpl
->mpTextEngine
->mpIMEInfos
->nLen
)
870 TextPaM
aPaM( GetSelection().GetEnd() );
871 tools::Rectangle aR1
= mpImpl
->mpTextEngine
->PaMtoEditCursor( aPaM
);
873 sal_Int32 nInputEnd
= mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
.GetIndex() + mpImpl
->mpTextEngine
->mpIMEInfos
->nLen
;
875 if ( !mpImpl
->mpTextEngine
->IsFormatted() )
876 mpImpl
->mpTextEngine
->FormatDoc();
878 TEParaPortion
* pParaPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( aPaM
.GetPara() );
879 std::vector
<TextLine
>::size_type nLine
= pParaPortion
->GetLineNumber( aPaM
.GetIndex(), true );
880 TextLine
& rLine
= pParaPortion
->GetLines()[ nLine
];
881 if ( nInputEnd
> rLine
.GetEnd() )
882 nInputEnd
= rLine
.GetEnd();
883 tools::Rectangle aR2
= mpImpl
->mpTextEngine
->PaMtoEditCursor( TextPaM( aPaM
.GetPara(), nInputEnd
) );
885 long nWidth
= aR2
.Left()-aR1
.Right();
886 aR1
.Move( -GetStartDocPos().X(), -GetStartDocPos().Y() );
887 GetWindow()->SetCursorRect( &aR1
, nWidth
);
891 GetWindow()->SetCursorRect();
896 mpImpl
->mpSelEngine
->Command( rCEvt
);
900 void TextView::ShowCursor( bool bGotoCursor
, bool bForceVisCursor
)
902 // this setting has more weight
903 if ( !mpImpl
->mbAutoScroll
)
905 ImpShowCursor( bGotoCursor
, bForceVisCursor
, false );
908 void TextView::HideCursor()
910 mpImpl
->mpCursor
->Hide();
913 void TextView::Scroll( long ndX
, long ndY
)
915 SAL_WARN_IF( !mpImpl
->mpTextEngine
->IsFormatted(), "vcl", "Scroll: Not formatted!" );
920 Point
aNewStartPos( mpImpl
->maStartDocPos
);
923 aNewStartPos
.AdjustY( -ndY
);
924 if ( aNewStartPos
.Y() < 0 )
925 aNewStartPos
.setY( 0 );
928 aNewStartPos
.AdjustX( -ndX
);
929 if ( aNewStartPos
.X() < 0 )
930 aNewStartPos
.setX( 0 );
932 long nDiffX
= mpImpl
->maStartDocPos
.X() - aNewStartPos
.X();
933 long nDiffY
= mpImpl
->maStartDocPos
.Y() - aNewStartPos
.Y();
935 if ( nDiffX
|| nDiffY
)
937 bool bVisCursor
= mpImpl
->mpCursor
->IsVisible();
938 mpImpl
->mpCursor
->Hide();
939 mpImpl
->mpWindow
->Update();
940 mpImpl
->maStartDocPos
= aNewStartPos
;
942 if ( mpImpl
->mpTextEngine
->IsRightToLeft() )
944 mpImpl
->mpWindow
->Scroll( nDiffX
, nDiffY
);
945 mpImpl
->mpWindow
->Update();
946 mpImpl
->mpCursor
->SetPos( mpImpl
->mpCursor
->GetPos() + Point( nDiffX
, nDiffY
) );
947 if ( bVisCursor
&& !mpImpl
->mbReadOnly
)
948 mpImpl
->mpCursor
->Show();
951 mpImpl
->mpTextEngine
->Broadcast( TextHint( SfxHintId::TextViewScrolled
) );
954 void TextView::Undo()
956 mpImpl
->mpTextEngine
->SetActiveView( this );
957 mpImpl
->mpTextEngine
->GetUndoManager().Undo();
960 void TextView::Redo()
962 mpImpl
->mpTextEngine
->SetActiveView( this );
963 mpImpl
->mpTextEngine
->GetUndoManager().Redo();
968 mpImpl
->mpTextEngine
->UndoActionStart();
971 mpImpl
->mpTextEngine
->UndoActionEnd();
974 void TextView::Copy( css::uno::Reference
< css::datatransfer::clipboard::XClipboard
> const & rxClipboard
)
976 if ( rxClipboard
.is() )
978 TETextDataObject
* pDataObj
= new TETextDataObject( GetSelected() );
980 SolarMutexReleaser aReleaser
;
984 rxClipboard
->setContents( pDataObj
, nullptr );
986 css::uno::Reference
< css::datatransfer::clipboard::XFlushableClipboard
> xFlushableClipboard( rxClipboard
, css::uno::UNO_QUERY
);
987 if( xFlushableClipboard
.is() )
988 xFlushableClipboard
->flushClipboard();
990 catch( const css::uno::Exception
& )
996 void TextView::Copy()
998 css::uno::Reference
<css::datatransfer::clipboard::XClipboard
> aClipboard(GetWindow()->GetClipboard());
1002 void TextView::Paste( css::uno::Reference
< css::datatransfer::clipboard::XClipboard
> const & rxClipboard
)
1004 if ( rxClipboard
.is() )
1006 css::uno::Reference
< css::datatransfer::XTransferable
> xDataObj
;
1010 SolarMutexReleaser aReleaser
;
1011 xDataObj
= rxClipboard
->getContents();
1013 catch( const css::uno::Exception
& )
1017 if ( xDataObj
.is() )
1019 css::datatransfer::DataFlavor aFlavor
;
1020 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING
, aFlavor
);
1021 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
& )
1045 void TextView::Paste()
1047 css::uno::Reference
<css::datatransfer::clipboard::XClipboard
> aClipboard(GetWindow()->GetClipboard());
1048 Paste( aClipboard
);
1051 OUString
TextView::GetSelected()
1053 return GetSelected( GetSystemLineEnd() );
1056 OUString
TextView::GetSelected( LineEnd aSeparator
)
1058 return mpImpl
->mpTextEngine
->GetText( mpImpl
->maSelection
, aSeparator
);
1061 void TextView::SetInsertMode( bool bInsert
)
1063 if ( mpImpl
->mbInsertMode
!= bInsert
)
1065 mpImpl
->mbInsertMode
= bInsert
;
1066 ShowCursor( mpImpl
->mbAutoScroll
, false );
1070 void TextView::SetReadOnly( bool bReadOnly
)
1072 if ( mpImpl
->mbReadOnly
!= bReadOnly
)
1074 mpImpl
->mbReadOnly
= bReadOnly
;
1075 if ( !mpImpl
->mbReadOnly
)
1076 ShowCursor( mpImpl
->mbAutoScroll
, false );
1080 GetWindow()->SetInputContext( InputContext( mpImpl
->mpTextEngine
->GetFont(), bReadOnly
? InputContextFlags::Text
|InputContextFlags::ExtText
: InputContextFlags::NONE
) );
1084 TextSelection
const & TextView::ImpMoveCursor( const KeyEvent
& rKeyEvent
)
1086 // normally only needed for Up/Down; but who cares
1087 mpImpl
->mpTextEngine
->CheckIdleFormatter();
1089 TextPaM
aPaM( mpImpl
->maSelection
.GetEnd() );
1090 TextPaM
aOldEnd( aPaM
);
1092 TextDirectionality eTextDirection
= TextDirectionality::LeftToRight_TopToBottom
;
1093 if ( mpImpl
->mpTextEngine
->IsRightToLeft() )
1094 eTextDirection
= TextDirectionality::RightToLeft_TopToBottom
;
1096 KeyEvent aTranslatedKeyEvent
= rKeyEvent
.LogicalTextDirectionality( eTextDirection
);
1098 bool bCtrl
= aTranslatedKeyEvent
.GetKeyCode().IsMod1();
1099 sal_uInt16 nCode
= aTranslatedKeyEvent
.GetKeyCode().GetCode();
1101 bool bSelect
= aTranslatedKeyEvent
.GetKeyCode().IsShift();
1104 case KEY_UP
: aPaM
= CursorUp( aPaM
);
1106 case KEY_DOWN
: aPaM
= CursorDown( aPaM
);
1108 case KEY_HOME
: aPaM
= bCtrl
? CursorStartOfDoc() : CursorStartOfLine( aPaM
);
1110 case KEY_END
: aPaM
= bCtrl
? CursorEndOfDoc() : CursorEndOfLine( aPaM
);
1112 case KEY_PAGEUP
: aPaM
= bCtrl
? CursorStartOfDoc() : PageUp( aPaM
);
1114 case KEY_PAGEDOWN
: aPaM
= bCtrl
? CursorEndOfDoc() : PageDown( aPaM
);
1116 case KEY_LEFT
: aPaM
= bCtrl
? CursorWordLeft( aPaM
) : CursorLeft( aPaM
, aTranslatedKeyEvent
.GetKeyCode().IsMod2() ? sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCHARACTER
) : sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCELL
) );
1118 case KEY_RIGHT
: aPaM
= bCtrl
? CursorWordRight( aPaM
) : CursorRight( aPaM
, aTranslatedKeyEvent
.GetKeyCode().IsMod2() ? sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCHARACTER
) : sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCELL
) );
1120 case css::awt::Key::SELECT_WORD_FORWARD
:
1123 case css::awt::Key::MOVE_WORD_FORWARD
:
1124 aPaM
= CursorWordRight( aPaM
);
1126 case css::awt::Key::SELECT_WORD_BACKWARD
:
1129 case css::awt::Key::MOVE_WORD_BACKWARD
:
1130 aPaM
= CursorWordLeft( aPaM
);
1132 case css::awt::Key::SELECT_TO_BEGIN_OF_LINE
:
1135 case css::awt::Key::MOVE_TO_BEGIN_OF_LINE
:
1136 aPaM
= CursorStartOfLine( aPaM
);
1138 case css::awt::Key::SELECT_TO_END_OF_LINE
:
1141 case css::awt::Key::MOVE_TO_END_OF_LINE
:
1142 aPaM
= CursorEndOfLine( aPaM
);
1144 case css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH
:
1147 case css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH
:
1148 aPaM
= CursorStartOfParagraph( aPaM
);
1150 case css::awt::Key::SELECT_TO_END_OF_PARAGRAPH
:
1153 case css::awt::Key::MOVE_TO_END_OF_PARAGRAPH
:
1154 aPaM
= CursorEndOfParagraph( aPaM
);
1156 case css::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT
:
1159 case css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT
:
1160 aPaM
= CursorStartOfDoc();
1162 case css::awt::Key::SELECT_TO_END_OF_DOCUMENT
:
1165 case css::awt::Key::MOVE_TO_END_OF_DOCUMENT
:
1166 aPaM
= CursorEndOfDoc();
1170 // might cause a CreateAnchor or Deselection all
1171 mpImpl
->mpSelEngine
->CursorPosChanging( bSelect
, aTranslatedKeyEvent
.GetKeyCode().IsMod1() );
1173 if ( aOldEnd
!= aPaM
)
1175 mpImpl
->mpTextEngine
->CursorMoved( aOldEnd
.GetPara() );
1177 TextSelection
aNewSelection( mpImpl
->maSelection
);
1178 aNewSelection
.GetEnd() = aPaM
;
1181 // extend the selection
1182 ImpSetSelection( aNewSelection
);
1183 ShowSelection( TextSelection( aOldEnd
, aPaM
) );
1187 aNewSelection
.GetStart() = aPaM
;
1188 ImpSetSelection( aNewSelection
);
1192 return mpImpl
->maSelection
;
1195 void TextView::InsertText( const OUString
& rStr
)
1197 mpImpl
->mpTextEngine
->UndoActionStart();
1199 TextSelection aNewSel
= mpImpl
->mpTextEngine
->ImpInsertText( mpImpl
->maSelection
, rStr
);
1201 ImpSetSelection( aNewSel
);
1203 mpImpl
->mpTextEngine
->UndoActionEnd();
1205 mpImpl
->mpTextEngine
->FormatAndUpdate( this );
1208 TextPaM
TextView::CursorLeft( const TextPaM
& rPaM
, sal_uInt16 nCharacterIteratorMode
)
1210 TextPaM
aPaM( rPaM
);
1212 if ( aPaM
.GetIndex() )
1214 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aPaM
.GetPara() ].get();
1215 css::uno::Reference
< css::i18n::XBreakIterator
> xBI
= mpImpl
->mpTextEngine
->GetBreakIterator();
1216 sal_Int32 nCount
= 1;
1217 aPaM
.GetIndex() = xBI
->previousCharacters( pNode
->GetText(), aPaM
.GetIndex(), mpImpl
->mpTextEngine
->GetLocale(), nCharacterIteratorMode
, nCount
, nCount
);
1219 else if ( aPaM
.GetPara() )
1222 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aPaM
.GetPara() ].get();
1223 aPaM
.GetIndex() = pNode
->GetText().getLength();
1228 TextPaM
TextView::CursorRight( const TextPaM
& rPaM
, sal_uInt16 nCharacterIteratorMode
)
1230 TextPaM
aPaM( rPaM
);
1232 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aPaM
.GetPara() ].get();
1233 if ( aPaM
.GetIndex() < pNode
->GetText().getLength() )
1235 css::uno::Reference
< css::i18n::XBreakIterator
> xBI
= mpImpl
->mpTextEngine
->GetBreakIterator();
1236 sal_Int32 nCount
= 1;
1237 aPaM
.GetIndex() = xBI
->nextCharacters( pNode
->GetText(), aPaM
.GetIndex(), mpImpl
->mpTextEngine
->GetLocale(), nCharacterIteratorMode
, nCount
, nCount
);
1239 else if ( aPaM
.GetPara() < ( mpImpl
->mpTextEngine
->mpDoc
->GetNodes().size()-1) )
1242 aPaM
.GetIndex() = 0;
1248 TextPaM
TextView::CursorWordLeft( const TextPaM
& rPaM
)
1250 TextPaM
aPaM( rPaM
);
1252 if ( aPaM
.GetIndex() )
1254 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aPaM
.GetPara() ].get();
1255 css::uno::Reference
< css::i18n::XBreakIterator
> xBI
= mpImpl
->mpTextEngine
->GetBreakIterator();
1256 css::i18n::Boundary aBoundary
= xBI
->getWordBoundary( pNode
->GetText(), rPaM
.GetIndex(), mpImpl
->mpTextEngine
->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES
, true );
1257 if ( aBoundary
.startPos
>= rPaM
.GetIndex() )
1258 aBoundary
= xBI
->previousWord( pNode
->GetText(), rPaM
.GetIndex(), mpImpl
->mpTextEngine
->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES
);
1259 aPaM
.GetIndex() = ( aBoundary
.startPos
!= -1 ) ? aBoundary
.startPos
: 0;
1261 else if ( aPaM
.GetPara() )
1264 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aPaM
.GetPara() ].get();
1265 aPaM
.GetIndex() = pNode
->GetText().getLength();
1270 TextPaM
TextView::CursorWordRight( const TextPaM
& rPaM
)
1272 TextPaM
aPaM( rPaM
);
1274 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aPaM
.GetPara() ].get();
1275 if ( aPaM
.GetIndex() < pNode
->GetText().getLength() )
1277 css::uno::Reference
< css::i18n::XBreakIterator
> xBI
= mpImpl
->mpTextEngine
->GetBreakIterator();
1278 css::i18n::Boundary aBoundary
= xBI
->nextWord( pNode
->GetText(), aPaM
.GetIndex(), mpImpl
->mpTextEngine
->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES
);
1279 aPaM
.GetIndex() = aBoundary
.startPos
;
1281 else if ( aPaM
.GetPara() < ( mpImpl
->mpTextEngine
->mpDoc
->GetNodes().size()-1) )
1284 aPaM
.GetIndex() = 0;
1290 TextPaM
TextView::ImpDelete( sal_uInt8 nMode
, sal_uInt8 nDelMode
)
1292 if ( mpImpl
->maSelection
.HasRange() ) // only delete selection
1293 return mpImpl
->mpTextEngine
->ImpDeleteText( mpImpl
->maSelection
);
1295 TextPaM aStartPaM
= mpImpl
->maSelection
.GetStart();
1296 TextPaM aEndPaM
= aStartPaM
;
1297 if ( nMode
== DEL_LEFT
)
1299 if ( nDelMode
== DELMODE_SIMPLE
)
1301 aEndPaM
= CursorLeft( aEndPaM
, sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCHARACTER
) );
1303 else if ( nDelMode
== DELMODE_RESTOFWORD
)
1305 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aEndPaM
.GetPara() ].get();
1306 css::uno::Reference
< css::i18n::XBreakIterator
> xBI
= mpImpl
->mpTextEngine
->GetBreakIterator();
1307 css::i18n::Boundary aBoundary
= xBI
->getWordBoundary( pNode
->GetText(), mpImpl
->maSelection
.GetEnd().GetIndex(), mpImpl
->mpTextEngine
->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES
, true );
1308 if ( aBoundary
.startPos
== mpImpl
->maSelection
.GetEnd().GetIndex() )
1309 aBoundary
= xBI
->previousWord( pNode
->GetText(), mpImpl
->maSelection
.GetEnd().GetIndex(), mpImpl
->mpTextEngine
->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES
);
1310 // #i63506# startPos is -1 when the paragraph starts with a tab
1311 aEndPaM
.GetIndex() = std::max
<sal_Int32
>(aBoundary
.startPos
, 0);
1313 else // DELMODE_RESTOFCONTENT
1315 if ( aEndPaM
.GetIndex() != 0 )
1316 aEndPaM
.GetIndex() = 0;
1317 else if ( aEndPaM
.GetPara() )
1319 // previous paragraph
1320 aEndPaM
.GetPara()--;
1321 aEndPaM
.GetIndex() = 0;
1327 if ( nDelMode
== DELMODE_SIMPLE
)
1329 aEndPaM
= CursorRight( aEndPaM
, sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCELL
) );
1331 else if ( nDelMode
== DELMODE_RESTOFWORD
)
1333 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aEndPaM
.GetPara() ].get();
1334 css::uno::Reference
< css::i18n::XBreakIterator
> xBI
= mpImpl
->mpTextEngine
->GetBreakIterator();
1335 css::i18n::Boundary aBoundary
= xBI
->nextWord( pNode
->GetText(), mpImpl
->maSelection
.GetEnd().GetIndex(), mpImpl
->mpTextEngine
->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES
);
1336 aEndPaM
.GetIndex() = aBoundary
.startPos
;
1338 else // DELMODE_RESTOFCONTENT
1340 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aEndPaM
.GetPara() ].get();
1341 if ( aEndPaM
.GetIndex() < pNode
->GetText().getLength() )
1342 aEndPaM
.GetIndex() = pNode
->GetText().getLength();
1343 else if ( aEndPaM
.GetPara() < ( mpImpl
->mpTextEngine
->mpDoc
->GetNodes().size() - 1 ) )
1346 aEndPaM
.GetPara()++;
1347 TextNode
* pNextNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aEndPaM
.GetPara() ].get();
1348 aEndPaM
.GetIndex() = pNextNode
->GetText().getLength();
1353 return mpImpl
->mpTextEngine
->ImpDeleteText( TextSelection( aStartPaM
, aEndPaM
) );
1356 TextPaM
TextView::CursorUp( const TextPaM
& rPaM
)
1358 TextPaM
aPaM( rPaM
);
1361 if ( mpImpl
->mnTravelXPos
== TRAVEL_X_DONTKNOW
)
1363 nX
= mpImpl
->mpTextEngine
->GetEditCursor( rPaM
, false ).Left();
1364 mpImpl
->mnTravelXPos
= static_cast<sal_uInt16
>(nX
)+1;
1367 nX
= mpImpl
->mnTravelXPos
;
1369 TEParaPortion
* pPPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( rPaM
.GetPara() );
1370 std::vector
<TextLine
>::size_type nLine
= pPPortion
->GetLineNumber( rPaM
.GetIndex(), false );
1371 if ( nLine
) // same paragraph
1373 aPaM
.GetIndex() = mpImpl
->mpTextEngine
->GetCharPos( rPaM
.GetPara(), nLine
-1, nX
);
1374 // If we need to go to the end of a line that was wrapped automatically,
1375 // the cursor ends up at the beginning of the 2nd line
1376 // Problem: Last character of an automatically wrapped line = Cursor
1377 TextLine
& rLine
= pPPortion
->GetLines()[ nLine
- 1 ];
1378 if ( aPaM
.GetIndex() && ( aPaM
.GetIndex() == rLine
.GetEnd() ) )
1381 else if ( rPaM
.GetPara() ) // previous paragraph
1384 pPPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( aPaM
.GetPara() );
1385 std::vector
<TextLine
>::size_type nL
= pPPortion
->GetLines().size() - 1;
1386 aPaM
.GetIndex() = mpImpl
->mpTextEngine
->GetCharPos( aPaM
.GetPara(), nL
, nX
+1 );
1392 TextPaM
TextView::CursorDown( const TextPaM
& rPaM
)
1394 TextPaM
aPaM( rPaM
);
1397 if ( mpImpl
->mnTravelXPos
== TRAVEL_X_DONTKNOW
)
1399 nX
= mpImpl
->mpTextEngine
->GetEditCursor( rPaM
, false ).Left();
1400 mpImpl
->mnTravelXPos
= static_cast<sal_uInt16
>(nX
)+1;
1403 nX
= mpImpl
->mnTravelXPos
;
1405 TEParaPortion
* pPPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( rPaM
.GetPara() );
1406 std::vector
<TextLine
>::size_type nLine
= pPPortion
->GetLineNumber( rPaM
.GetIndex(), false );
1407 if ( nLine
< ( pPPortion
->GetLines().size() - 1 ) )
1409 aPaM
.GetIndex() = mpImpl
->mpTextEngine
->GetCharPos( rPaM
.GetPara(), nLine
+1, nX
);
1411 // special case CursorUp
1412 TextLine
& rLine
= pPPortion
->GetLines()[ nLine
+ 1 ];
1413 if ( ( aPaM
.GetIndex() == rLine
.GetEnd() ) && ( aPaM
.GetIndex() > rLine
.GetStart() ) && aPaM
.GetIndex() < pPPortion
->GetNode()->GetText().getLength() )
1416 else if ( rPaM
.GetPara() < ( mpImpl
->mpTextEngine
->mpDoc
->GetNodes().size() - 1 ) ) // next paragraph
1419 pPPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( aPaM
.GetPara() );
1420 aPaM
.GetIndex() = mpImpl
->mpTextEngine
->GetCharPos( aPaM
.GetPara(), 0, nX
+1 );
1421 TextLine
& rLine
= pPPortion
->GetLines().front();
1422 if ( ( aPaM
.GetIndex() == rLine
.GetEnd() ) && ( aPaM
.GetIndex() > rLine
.GetStart() ) && ( pPPortion
->GetLines().size() > 1 ) )
1429 TextPaM
TextView::CursorStartOfLine( const TextPaM
& rPaM
)
1431 TextPaM
aPaM( rPaM
);
1433 TEParaPortion
* pPPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( rPaM
.GetPara() );
1434 std::vector
<TextLine
>::size_type nLine
= pPPortion
->GetLineNumber( aPaM
.GetIndex(), false );
1435 TextLine
& rLine
= pPPortion
->GetLines()[ nLine
];
1436 aPaM
.GetIndex() = rLine
.GetStart();
1441 TextPaM
TextView::CursorEndOfLine( const TextPaM
& rPaM
)
1443 TextPaM
aPaM( rPaM
);
1445 TEParaPortion
* pPPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( rPaM
.GetPara() );
1446 std::vector
<TextLine
>::size_type nLine
= pPPortion
->GetLineNumber( aPaM
.GetIndex(), false );
1447 TextLine
& rLine
= pPPortion
->GetLines()[ nLine
];
1448 aPaM
.GetIndex() = rLine
.GetEnd();
1450 if ( rLine
.GetEnd() > rLine
.GetStart() ) // empty line
1452 sal_Unicode cLastChar
= pPPortion
->GetNode()->GetText()[ aPaM
.GetIndex()-1 ];
1453 if ( ( cLastChar
== ' ' ) && ( aPaM
.GetIndex() != pPPortion
->GetNode()->GetText().getLength() ) )
1455 // for a blank in an automatically-wrapped line it is better to stand before it,
1456 // as the user will intend to stand behind the prior word.
1457 // If there is a change, special case for Pos1 after End!
1464 TextPaM
TextView::CursorStartOfParagraph( const TextPaM
& rPaM
)
1466 TextPaM
aPaM( rPaM
);
1467 aPaM
.GetIndex() = 0;
1471 TextPaM
TextView::CursorEndOfParagraph( const TextPaM
& rPaM
)
1473 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ rPaM
.GetPara() ].get();
1474 TextPaM
aPaM( rPaM
);
1475 aPaM
.GetIndex() = pNode
->GetText().getLength();
1479 TextPaM
TextView::CursorStartOfDoc()
1481 TextPaM
aPaM( 0, 0 );
1485 TextPaM
TextView::CursorEndOfDoc()
1487 const sal_uInt32 nNode
= static_cast<sal_uInt32
>(mpImpl
->mpTextEngine
->mpDoc
->GetNodes().size() - 1);
1488 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ nNode
].get();
1489 TextPaM
aPaM( nNode
, pNode
->GetText().getLength() );
1493 TextPaM
TextView::PageUp( const TextPaM
& rPaM
)
1495 tools::Rectangle aRect
= mpImpl
->mpTextEngine
->PaMtoEditCursor( rPaM
);
1496 Point aTopLeft
= aRect
.TopLeft();
1497 aTopLeft
.AdjustY( -(mpImpl
->mpWindow
->GetOutputSizePixel().Height() * 9/10) );
1498 aTopLeft
.AdjustX(1 );
1499 if ( aTopLeft
.Y() < 0 )
1502 TextPaM aPaM
= mpImpl
->mpTextEngine
->GetPaM( aTopLeft
);
1506 TextPaM
TextView::PageDown( const TextPaM
& rPaM
)
1508 tools::Rectangle aRect
= mpImpl
->mpTextEngine
->PaMtoEditCursor( rPaM
);
1509 Point aBottomRight
= aRect
.BottomRight();
1510 aBottomRight
.AdjustY(mpImpl
->mpWindow
->GetOutputSizePixel().Height() * 9/10 );
1511 aBottomRight
.AdjustX(1 );
1512 long nHeight
= mpImpl
->mpTextEngine
->GetTextHeight();
1513 if ( aBottomRight
.Y() > nHeight
)
1514 aBottomRight
.setY( nHeight
-1 );
1516 TextPaM aPaM
= mpImpl
->mpTextEngine
->GetPaM( aBottomRight
);
1520 void TextView::ImpShowCursor( bool bGotoCursor
, bool bForceVisCursor
, bool bSpecial
)
1522 if ( mpImpl
->mpTextEngine
->IsFormatting() )
1524 if ( !mpImpl
->mpTextEngine
->GetUpdateMode() )
1526 if ( mpImpl
->mpTextEngine
->IsInUndo() )
1529 mpImpl
->mpTextEngine
->CheckIdleFormatter();
1530 if ( !mpImpl
->mpTextEngine
->IsFormatted() )
1531 mpImpl
->mpTextEngine
->FormatAndUpdate( this );
1533 TextPaM
aPaM( mpImpl
->maSelection
.GetEnd() );
1534 tools::Rectangle aEditCursor
= mpImpl
->mpTextEngine
->PaMtoEditCursor( aPaM
, bSpecial
);
1536 // Remember that we placed the cursor behind the last character of a line
1537 mpImpl
->mbCursorAtEndOfLine
= false;
1540 TEParaPortion
* pParaPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( aPaM
.GetPara() );
1541 mpImpl
->mbCursorAtEndOfLine
=
1542 pParaPortion
->GetLineNumber( aPaM
.GetIndex(), true ) != pParaPortion
->GetLineNumber( aPaM
.GetIndex(), false );
1545 if ( !IsInsertMode() && !mpImpl
->maSelection
.HasRange() )
1547 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aPaM
.GetPara() ].get();
1548 if ( !pNode
->GetText().isEmpty() && ( aPaM
.GetIndex() < pNode
->GetText().getLength() ) )
1550 // If we are behind a portion, and the next portion has other direction, we must change position...
1551 aEditCursor
.SetLeft( mpImpl
->mpTextEngine
->GetEditCursor( aPaM
, false, true ).Left() );
1552 aEditCursor
.SetRight( aEditCursor
.Left() );
1554 TEParaPortion
* pParaPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( aPaM
.GetPara() );
1556 sal_Int32 nTextPortionStart
= 0;
1557 std::size_t nTextPortion
= pParaPortion
->GetTextPortions().FindPortion( aPaM
.GetIndex(), nTextPortionStart
, true );
1558 TETextPortion
* pTextPortion
= pParaPortion
->GetTextPortions()[ nTextPortion
];
1559 if ( pTextPortion
->GetKind() == PORTIONKIND_TAB
)
1561 aEditCursor
.AdjustRight(pTextPortion
->GetWidth() );
1565 TextPaM aNext
= CursorRight( TextPaM( aPaM
.GetPara(), aPaM
.GetIndex() ), sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCELL
) );
1566 aEditCursor
.SetRight( mpImpl
->mpTextEngine
->GetEditCursor( aNext
, true ).Left() );
1571 Size aOutSz
= mpImpl
->mpWindow
->GetOutputSizePixel();
1572 if ( aEditCursor
.GetHeight() > aOutSz
.Height() )
1573 aEditCursor
.SetBottom( aEditCursor
.Top() + aOutSz
.Height() - 1 );
1575 aEditCursor
.AdjustLeft( -1 );
1578 // #i81283# protect maStartDocPos against initialization problems
1579 && aOutSz
.Width() && aOutSz
.Height()
1582 long nVisStartY
= mpImpl
->maStartDocPos
.Y();
1583 long nVisEndY
= mpImpl
->maStartDocPos
.Y() + aOutSz
.Height();
1584 long nVisStartX
= mpImpl
->maStartDocPos
.X();
1585 long nVisEndX
= mpImpl
->maStartDocPos
.X() + aOutSz
.Width();
1586 long nMoreX
= aOutSz
.Width() / 4;
1588 Point
aNewStartPos( mpImpl
->maStartDocPos
);
1590 if ( aEditCursor
.Bottom() > nVisEndY
)
1592 aNewStartPos
.AdjustY( aEditCursor
.Bottom() - nVisEndY
);
1594 else if ( aEditCursor
.Top() < nVisStartY
)
1596 aNewStartPos
.AdjustY( -( nVisStartY
- aEditCursor
.Top() ) );
1599 if ( aEditCursor
.Right() >= nVisEndX
)
1601 aNewStartPos
.AdjustX( aEditCursor
.Right() - nVisEndX
);
1603 // do you want some more?
1604 aNewStartPos
.AdjustX(nMoreX
);
1606 else if ( aEditCursor
.Left() <= nVisStartX
)
1608 aNewStartPos
.AdjustX( -( nVisStartX
- aEditCursor
.Left() ) );
1610 // do you want some more?
1611 aNewStartPos
.AdjustX( -nMoreX
);
1614 // X can be wrong for the 'some more' above:
1615 // sal_uLong nMaxTextWidth = mpImpl->mpTextEngine->GetMaxTextWidth();
1616 // if ( !nMaxTextWidth || ( nMaxTextWidth > 0x7FFFFFFF ) )
1617 // nMaxTextWidth = 0x7FFFFFFF;
1618 // long nMaxX = (long)nMaxTextWidth - aOutSz.Width();
1619 long nMaxX
= mpImpl
->mpTextEngine
->CalcTextWidth() - aOutSz
.Width();
1623 if ( aNewStartPos
.X() < 0 )
1624 aNewStartPos
.setX( 0 );
1625 else if ( aNewStartPos
.X() > nMaxX
)
1626 aNewStartPos
.setX( nMaxX
);
1628 // Y should not be further down than needed
1629 long nYMax
= mpImpl
->mpTextEngine
->GetTextHeight() - aOutSz
.Height();
1632 if ( aNewStartPos
.Y() > nYMax
)
1633 aNewStartPos
.setY( nYMax
);
1635 if ( aNewStartPos
!= mpImpl
->maStartDocPos
)
1636 Scroll( -(aNewStartPos
.X() - mpImpl
->maStartDocPos
.X()), -(aNewStartPos
.Y() - mpImpl
->maStartDocPos
.Y()) );
1639 if ( aEditCursor
.Right() < aEditCursor
.Left() )
1641 long n
= aEditCursor
.Left();
1642 aEditCursor
.SetLeft( aEditCursor
.Right() );
1643 aEditCursor
.SetRight( n
);
1646 Point
aPoint( GetWindowPos( !mpImpl
->mpTextEngine
->IsRightToLeft() ? aEditCursor
.TopLeft() : aEditCursor
.TopRight() ) );
1647 mpImpl
->mpCursor
->SetPos( aPoint
);
1648 mpImpl
->mpCursor
->SetSize( aEditCursor
.GetSize() );
1649 if ( bForceVisCursor
&& mpImpl
->mbCursorEnabled
)
1650 mpImpl
->mpCursor
->Show();
1653 void TextView::SetCursorAtPoint( const Point
& rPosPixel
)
1655 mpImpl
->mpTextEngine
->CheckIdleFormatter();
1657 Point aDocPos
= GetDocPos( rPosPixel
);
1659 TextPaM aPaM
= mpImpl
->mpTextEngine
->GetPaM( aDocPos
);
1661 // aTmpNewSel: Diff between old and new; not the new selection
1662 TextSelection
aTmpNewSel( mpImpl
->maSelection
.GetEnd(), aPaM
);
1663 TextSelection
aNewSel( mpImpl
->maSelection
);
1664 aNewSel
.GetEnd() = aPaM
;
1666 if ( !mpImpl
->mpSelEngine
->HasAnchor() )
1668 if ( mpImpl
->maSelection
.GetStart() != aPaM
)
1669 mpImpl
->mpTextEngine
->CursorMoved( mpImpl
->maSelection
.GetStart().GetPara() );
1670 aNewSel
.GetStart() = aPaM
;
1671 ImpSetSelection( aNewSel
);
1675 ImpSetSelection( aNewSel
);
1676 ShowSelection( aTmpNewSel
);
1679 bool bForceCursor
= !mpImpl
->mpDDInfo
; // && !mbInSelection
1680 ImpShowCursor( mpImpl
->mbAutoScroll
, bForceCursor
, false );
1683 bool TextView::IsSelectionAtPoint( const Point
& rPosPixel
)
1685 Point aDocPos
= GetDocPos( rPosPixel
);
1686 TextPaM aPaM
= mpImpl
->mpTextEngine
->GetPaM( aDocPos
);
1687 // BeginDrag is only called, however, if IsSelectionAtPoint()
1688 // Problem: IsSelectionAtPoint is not called by Command()
1689 // if before MBDown returned false.
1690 return IsInSelection( aPaM
);
1693 bool TextView::IsInSelection( const TextPaM
& rPaM
)
1695 TextSelection aSel
= mpImpl
->maSelection
;
1698 const sal_uInt32 nStartNode
= aSel
.GetStart().GetPara();
1699 const sal_uInt32 nEndNode
= aSel
.GetEnd().GetPara();
1700 const sal_uInt32 nCurNode
= rPaM
.GetPara();
1702 if ( ( nCurNode
> nStartNode
) && ( nCurNode
< nEndNode
) )
1705 if ( nStartNode
== nEndNode
)
1707 if ( nCurNode
== nStartNode
)
1708 if ( ( rPaM
.GetIndex() >= aSel
.GetStart().GetIndex() ) && ( rPaM
.GetIndex() < aSel
.GetEnd().GetIndex() ) )
1711 else if ( ( nCurNode
== nStartNode
) && ( rPaM
.GetIndex() >= aSel
.GetStart().GetIndex() ) )
1713 else if ( ( nCurNode
== nEndNode
) && ( rPaM
.GetIndex() < aSel
.GetEnd().GetIndex() ) )
1719 void TextView::ImpHideDDCursor()
1721 if ( mpImpl
->mpDDInfo
&& mpImpl
->mpDDInfo
->mbVisCursor
)
1723 mpImpl
->mpDDInfo
->maCursor
.Hide();
1724 mpImpl
->mpDDInfo
->mbVisCursor
= false;
1728 void TextView::ImpShowDDCursor()
1730 if ( !mpImpl
->mpDDInfo
->mbVisCursor
)
1732 tools::Rectangle aCursor
= mpImpl
->mpTextEngine
->PaMtoEditCursor( mpImpl
->mpDDInfo
->maDropPos
, true );
1733 aCursor
.AdjustRight( 1 );
1734 aCursor
.SetPos( GetWindowPos( aCursor
.TopLeft() ) );
1736 mpImpl
->mpDDInfo
->maCursor
.SetWindow( mpImpl
->mpWindow
);
1737 mpImpl
->mpDDInfo
->maCursor
.SetPos( aCursor
.TopLeft() );
1738 mpImpl
->mpDDInfo
->maCursor
.SetSize( aCursor
.GetSize() );
1739 mpImpl
->mpDDInfo
->maCursor
.Show();
1740 mpImpl
->mpDDInfo
->mbVisCursor
= true;
1744 void TextView::SetPaintSelection( bool bPaint
)
1746 if ( bPaint
!= mpImpl
->mbPaintSelection
)
1748 mpImpl
->mbPaintSelection
= bPaint
;
1749 ShowSelection( mpImpl
->maSelection
);
1753 void TextView::Read( SvStream
& rInput
)
1755 mpImpl
->mpTextEngine
->Read( rInput
, &mpImpl
->maSelection
);
1759 bool TextView::ImplTruncateNewText( OUString
& rNewText
) const
1761 bool bTruncated
= false;
1763 const sal_Int32 nMaxLen
= mpImpl
->mpTextEngine
->GetMaxTextLen();
1764 // 0 means unlimited
1767 const sal_Int32 nCurLen
= mpImpl
->mpTextEngine
->GetTextLen();
1769 const sal_Int32 nNewLen
= rNewText
.getLength();
1770 if ( nCurLen
+ nNewLen
> nMaxLen
)
1772 // see how much text will be replaced
1773 const sal_Int32 nSelLen
= mpImpl
->mpTextEngine
->GetTextLen( mpImpl
->maSelection
);
1774 if ( nCurLen
+ nNewLen
- nSelLen
> nMaxLen
)
1776 const sal_Int32 nTruncatedLen
= nMaxLen
- (nCurLen
- nSelLen
);
1777 rNewText
= rNewText
.copy( 0, nTruncatedLen
);
1785 bool TextView::ImplCheckTextLen( const OUString
& rNewText
)
1788 if ( mpImpl
->mpTextEngine
->GetMaxTextLen() )
1790 sal_Int32 n
= mpImpl
->mpTextEngine
->GetTextLen() + rNewText
.getLength();
1791 if ( n
> mpImpl
->mpTextEngine
->GetMaxTextLen() )
1793 // calculate how much text is being deleted
1794 n
-= mpImpl
->mpTextEngine
->GetTextLen( mpImpl
->maSelection
);
1795 if ( n
> mpImpl
->mpTextEngine
->GetMaxTextLen() )
1802 void TextView::dragGestureRecognized( const css::datatransfer::dnd::DragGestureEvent
& rDGE
)
1804 if ( mpImpl
->mbClickedInSelection
)
1806 SolarMutexGuard aVclGuard
;
1808 SAL_WARN_IF( !mpImpl
->maSelection
.HasRange(), "vcl", "TextView::dragGestureRecognized: mpImpl->mbClickedInSelection, but no selection?" );
1810 mpImpl
->mpDDInfo
.reset(new TextDDInfo
);
1811 mpImpl
->mpDDInfo
->mbStarterOfDD
= true;
1813 TETextDataObject
* pDataObj
= new TETextDataObject( GetSelected() );
1815 mpImpl
->mpCursor
->Hide();
1817 sal_Int8 nActions
= css::datatransfer::dnd::DNDConstants::ACTION_COPY
;
1818 if ( !IsReadOnly() )
1819 nActions
|= css::datatransfer::dnd::DNDConstants::ACTION_MOVE
;
1820 rDGE
.DragSource
->startDrag( rDGE
, nActions
, 0 /*cursor*/, 0 /*image*/, pDataObj
, mpImpl
->mxDnDListener
);
1824 void TextView::dragDropEnd( const css::datatransfer::dnd::DragSourceDropEvent
& )
1827 mpImpl
->mpDDInfo
.reset();
1830 void TextView::drop( const css::datatransfer::dnd::DropTargetDropEvent
& rDTDE
)
1832 SolarMutexGuard aVclGuard
;
1834 if ( !mpImpl
->mbReadOnly
&& mpImpl
->mpDDInfo
)
1838 // Data for deleting after DROP_MOVE:
1839 TextSelection
aPrevSel( mpImpl
->maSelection
);
1841 const sal_uInt32 nPrevParaCount
= mpImpl
->mpTextEngine
->GetParagraphCount();
1842 const sal_Int32 nPrevStartParaLen
= mpImpl
->mpTextEngine
->GetTextLen( aPrevSel
.GetStart().GetPara() );
1844 bool bStarterOfDD
= false;
1845 for ( sal_uInt16 nView
= mpImpl
->mpTextEngine
->GetViewCount(); nView
&& !bStarterOfDD
; )
1846 bStarterOfDD
= mpImpl
->mpTextEngine
->GetView( --nView
)->mpImpl
->mpDDInfo
&& mpImpl
->mpTextEngine
->GetView( nView
)->mpImpl
->mpDDInfo
->mbStarterOfDD
;
1849 ImpSetSelection( mpImpl
->mpDDInfo
->maDropPos
);
1851 mpImpl
->mpTextEngine
->UndoActionStart();
1854 css::uno::Reference
< css::datatransfer::XTransferable
> xDataObj
= rDTDE
.Transferable
;
1855 if ( xDataObj
.is() )
1857 css::datatransfer::DataFlavor aFlavor
;
1858 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING
, aFlavor
);
1859 if ( xDataObj
->isDataFlavorSupported( aFlavor
) )
1861 css::uno::Any aData
= xDataObj
->getTransferData( aFlavor
);
1863 aData
>>= aOUString
;
1864 aText
= convertLineEnd(aOUString
, LINEEND_LF
);
1868 if ( !aText
.isEmpty() && ( aText
[ aText
.getLength()-1 ] == LINE_SEP
) )
1869 aText
= aText
.copy(0, aText
.getLength()-1);
1871 if ( ImplCheckTextLen( aText
) )
1872 ImpSetSelection( mpImpl
->mpTextEngine
->ImpInsertText( mpImpl
->mpDDInfo
->maDropPos
, aText
) );
1874 if ( aPrevSel
.HasRange() &&
1875 (( rDTDE
.DropAction
& css::datatransfer::dnd::DNDConstants::ACTION_MOVE
) || !bStarterOfDD
) )
1877 // adjust selection if necessary
1878 if ( ( mpImpl
->mpDDInfo
->maDropPos
.GetPara() < aPrevSel
.GetStart().GetPara() ) ||
1879 ( ( mpImpl
->mpDDInfo
->maDropPos
.GetPara() == aPrevSel
.GetStart().GetPara() )
1880 && ( mpImpl
->mpDDInfo
->maDropPos
.GetIndex() < aPrevSel
.GetStart().GetIndex() ) ) )
1882 const sal_uInt32 nNewParasBeforeSelection
=
1883 mpImpl
->mpTextEngine
->GetParagraphCount() - nPrevParaCount
;
1885 aPrevSel
.GetStart().GetPara() += nNewParasBeforeSelection
;
1886 aPrevSel
.GetEnd().GetPara() += nNewParasBeforeSelection
;
1888 if ( mpImpl
->mpDDInfo
->maDropPos
.GetPara() == aPrevSel
.GetStart().GetPara() )
1890 const sal_Int32 nNewChars
=
1891 mpImpl
->mpTextEngine
->GetTextLen( aPrevSel
.GetStart().GetPara() ) - nPrevStartParaLen
;
1893 aPrevSel
.GetStart().GetIndex() += nNewChars
;
1894 if ( aPrevSel
.GetStart().GetPara() == aPrevSel
.GetEnd().GetPara() )
1895 aPrevSel
.GetEnd().GetIndex() += nNewChars
;
1900 // adjust current selection
1901 TextPaM aPaM
= mpImpl
->maSelection
.GetStart();
1902 aPaM
.GetPara() -= ( aPrevSel
.GetEnd().GetPara() - aPrevSel
.GetStart().GetPara() );
1903 if ( aPrevSel
.GetEnd().GetPara() == mpImpl
->mpDDInfo
->maDropPos
.GetPara() )
1905 aPaM
.GetIndex() -= aPrevSel
.GetEnd().GetIndex();
1906 if ( aPrevSel
.GetStart().GetPara() == mpImpl
->mpDDInfo
->maDropPos
.GetPara() )
1907 aPaM
.GetIndex() += aPrevSel
.GetStart().GetIndex();
1909 ImpSetSelection( aPaM
);
1912 mpImpl
->mpTextEngine
->ImpDeleteText( aPrevSel
);
1915 mpImpl
->mpTextEngine
->UndoActionEnd();
1917 mpImpl
->mpDDInfo
.reset();
1919 mpImpl
->mpTextEngine
->FormatAndUpdate( this );
1921 mpImpl
->mpTextEngine
->Broadcast( TextHint( SfxHintId::TextModified
) );
1923 rDTDE
.Context
->dropComplete( false/*bChanges*/ );
1926 void TextView::dragEnter( const css::datatransfer::dnd::DropTargetDragEnterEvent
& )
1930 void TextView::dragExit( const css::datatransfer::dnd::DropTargetEvent
& )
1932 SolarMutexGuard aVclGuard
;
1936 void TextView::dragOver( const css::datatransfer::dnd::DropTargetDragEvent
& rDTDE
)
1938 SolarMutexGuard aVclGuard
;
1940 if (!mpImpl
->mpDDInfo
)
1941 mpImpl
->mpDDInfo
.reset(new TextDDInfo
);
1943 TextPaM aPrevDropPos
= mpImpl
->mpDDInfo
->maDropPos
;
1944 Point
aMousePos( rDTDE
.LocationX
, rDTDE
.LocationY
);
1945 Point aDocPos
= GetDocPos( aMousePos
);
1946 mpImpl
->mpDDInfo
->maDropPos
= mpImpl
->mpTextEngine
->GetPaM( aDocPos
);
1948 // Don't drop in selection or in read only engine
1949 if ( IsReadOnly() || IsInSelection( mpImpl
->mpDDInfo
->maDropPos
))
1952 rDTDE
.Context
->rejectDrag();
1956 // delete old Cursor
1957 if ( !mpImpl
->mpDDInfo
->mbVisCursor
|| ( aPrevDropPos
!= mpImpl
->mpDDInfo
->maDropPos
) )
1962 rDTDE
.Context
->acceptDrag( rDTDE
.DropAction
);
1966 Point
TextView::ImpGetOutputStartPos( const Point
& rStartDocPos
) const
1968 Point
aStartPos( -rStartDocPos
.X(), -rStartDocPos
.Y() );
1969 if ( mpImpl
->mpTextEngine
->IsRightToLeft() )
1971 Size aSz
= mpImpl
->mpWindow
->GetOutputSizePixel();
1972 aStartPos
.setX( rStartDocPos
.X() + aSz
.Width() - 1 ); // -1: Start is 0
1977 Point
TextView::GetDocPos( const Point
& rWindowPos
) const
1979 // Window Position => Document Position
1983 aPoint
.setY( rWindowPos
.Y() + mpImpl
->maStartDocPos
.Y() );
1985 if ( !mpImpl
->mpTextEngine
->IsRightToLeft() )
1987 aPoint
.setX( rWindowPos
.X() + mpImpl
->maStartDocPos
.X() );
1991 Size aSz
= mpImpl
->mpWindow
->GetOutputSizePixel();
1992 aPoint
.setX( ( aSz
.Width() - 1 ) - rWindowPos
.X() + mpImpl
->maStartDocPos
.X() );
1998 Point
TextView::GetWindowPos( const Point
& rDocPos
) const
2000 // Document Position => Window Position
2004 aPoint
.setY( rDocPos
.Y() - mpImpl
->maStartDocPos
.Y() );
2006 if ( !mpImpl
->mpTextEngine
->IsRightToLeft() )
2008 aPoint
.setX( rDocPos
.X() - mpImpl
->maStartDocPos
.X() );
2012 Size aSz
= mpImpl
->mpWindow
->GetOutputSizePixel();
2013 aPoint
.setX( ( aSz
.Width() - 1 ) - ( rDocPos
.X() - mpImpl
->maStartDocPos
.X() ) );
2019 sal_Int32
TextView::GetLineNumberOfCursorInSelection() const
2022 sal_Int32 nLineNo
= -1;
2023 if( mpImpl
->mbCursorEnabled
)
2025 TextPaM aPaM
= GetSelection().GetEnd();
2026 TEParaPortion
* pPPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( aPaM
.GetPara() );
2027 nLineNo
= pPPortion
->GetLineNumber( aPaM
.GetIndex(), false );
2028 //TODO: std::vector<TextLine>::size_type -> sal_Int32!
2029 if( mpImpl
->mbCursorAtEndOfLine
)
2035 // (+) class TextSelFunctionSet
2037 TextSelFunctionSet::TextSelFunctionSet( TextView
* pView
)
2042 void TextSelFunctionSet::BeginDrag()
2046 void TextSelFunctionSet::CreateAnchor()
2048 // TextSelection aSel( mpView->GetSelection() );
2049 // aSel.GetStart() = aSel.GetEnd();
2050 // mpView->SetSelection( aSel );
2052 // may not be followed by ShowCursor
2053 mpView
->HideSelection();
2054 mpView
->ImpSetSelection( mpView
->mpImpl
->maSelection
.GetEnd() );
2057 void TextSelFunctionSet::SetCursorAtPoint( const Point
& rPointPixel
, bool )
2059 mpView
->SetCursorAtPoint( rPointPixel
);
2062 bool TextSelFunctionSet::IsSelectionAtPoint( const Point
& rPointPixel
)
2064 return mpView
->IsSelectionAtPoint( rPointPixel
);
2067 void TextSelFunctionSet::DeselectAll()
2072 void TextSelFunctionSet::DeselectAtPoint( const Point
& )
2074 // only for multiple selection
2077 void TextSelFunctionSet::DestroyAnchor()
2079 // only for multiple selection
2081 TextEngine
* TextView::GetTextEngine() const
2082 { return mpImpl
->mpTextEngine
; }
2083 vcl::Window
* TextView::GetWindow() const
2084 { return mpImpl
->mpWindow
; }
2085 void TextView::EnableCursor( bool bEnable
)
2086 { mpImpl
->mbCursorEnabled
= bEnable
; }
2087 bool TextView::IsCursorEnabled() const
2088 { return mpImpl
->mbCursorEnabled
; }
2089 void TextView::SetStartDocPos( const Point
& rPos
)
2090 { mpImpl
->maStartDocPos
= rPos
; }
2091 const Point
& TextView::GetStartDocPos() const
2092 { return mpImpl
->maStartDocPos
; }
2093 void TextView::SetAutoIndentMode( bool bAutoIndent
)
2094 { mpImpl
->mbAutoIndent
= bAutoIndent
; }
2095 bool TextView::IsReadOnly() const
2096 { return mpImpl
->mbReadOnly
; }
2097 void TextView::SetAutoScroll( bool bAutoScroll
)
2098 { mpImpl
->mbAutoScroll
= bAutoScroll
; }
2099 bool TextView::IsAutoScroll() const
2100 { return mpImpl
->mbAutoScroll
; }
2101 bool TextView::HasSelection() const
2102 { return mpImpl
->maSelection
.HasRange(); }
2103 bool TextView::IsInsertMode() const
2104 { return mpImpl
->mbInsertMode
; }
2106 void TextView::MatchGroup()
2108 TextSelection
aTmpSel( GetSelection() );
2110 if ( ( aTmpSel
.GetStart().GetPara() != aTmpSel
.GetEnd().GetPara() ) ||
2111 ( ( aTmpSel
.GetEnd().GetIndex() - aTmpSel
.GetStart().GetIndex() ) > 1 ) )
2116 TextSelection aMatchSel
= static_cast<ExtTextEngine
*>(GetTextEngine())->MatchGroup( aTmpSel
.GetStart() );
2117 if ( aMatchSel
.HasRange() )
2118 SetSelection( aMatchSel
);
2121 void TextView::CenterPaM( const TextPaM
& rPaM
)
2123 // Get textview size and the corresponding y-coordinates
2124 Size aOutSz
= mpImpl
->mpWindow
->GetOutputSizePixel();
2125 long nVisStartY
= mpImpl
->maStartDocPos
.Y();
2126 long nVisEndY
= mpImpl
->maStartDocPos
.Y() + aOutSz
.Height();
2128 // Retrieve the coordinates of the PaM
2129 tools::Rectangle aRect
= mpImpl
->mpTextEngine
->PaMtoEditCursor(rPaM
);
2131 // Recalculate the offset of the center y-coordinates and scroll
2132 Scroll(0, (nVisStartY
+ nVisEndY
) / 2 - aRect
.TopLeft().getY());
2135 bool TextView::Search( const i18nutil::SearchOptions
& rSearchOptions
, bool bForward
)
2137 bool bFound
= false;
2138 TextSelection
aSel( GetSelection() );
2139 if ( static_cast<ExtTextEngine
*>(GetTextEngine())->Search( aSel
, rSearchOptions
, bForward
) )
2142 // First add the beginning of the word to the selection,
2143 // so that the whole word is in the visible region.
2144 SetSelection( aSel
.GetStart() );
2145 ShowCursor( true, false );
2149 aSel
= GetSelection().GetEnd();
2152 SetSelection( aSel
);
2153 // tdf#49482: Move the start of the selection to the center of the textview
2156 CenterPaM( aSel
.GetStart() );
2163 sal_uInt16
TextView::Replace( const i18nutil::SearchOptions
& rSearchOptions
, bool bAll
, bool bForward
)
2165 sal_uInt16 nFound
= 0;
2169 if ( GetSelection().HasRange() )
2171 InsertText( rSearchOptions
.replaceString
);
2173 Search( rSearchOptions
, bForward
); // right away to the next
2177 if( Search( rSearchOptions
, bForward
) )
2183 // the writer replaces all, from beginning to end
2185 ExtTextEngine
* pTextEngine
= static_cast<ExtTextEngine
*>(GetTextEngine());
2190 bool bSearchInSelection
= (0 != (rSearchOptions
.searchFlag
& css::util::SearchFlags::REG_NOT_BEGINOFLINE
) );
2191 if ( bSearchInSelection
)
2193 aSel
= GetSelection();
2197 TextSelection
aSearchSel( aSel
);
2199 bool bFound
= pTextEngine
->Search( aSel
, rSearchOptions
);
2201 pTextEngine
->UndoActionStart();
2206 TextPaM aNewStart
= pTextEngine
->ImpInsertText( aSel
, rSearchOptions
.replaceString
);
2208 aSel
.GetStart() = aNewStart
;
2209 bFound
= pTextEngine
->Search( aSel
, rSearchOptions
);
2213 SetSelection( aSel
.GetStart() );
2214 pTextEngine
->FormatAndUpdate( this );
2215 pTextEngine
->UndoActionEnd();
2221 bool TextView::ImpIndentBlock( bool bRight
)
2225 TextSelection aSel
= GetSelection();
2229 GetTextEngine()->UndoActionStart();
2231 const sal_uInt32 nStartPara
= aSel
.GetStart().GetPara();
2232 sal_uInt32 nEndPara
= aSel
.GetEnd().GetPara();
2233 if ( aSel
.HasRange() && !aSel
.GetEnd().GetIndex() )
2235 nEndPara
--; // do not indent
2238 for ( sal_uInt32 nPara
= nStartPara
; nPara
<= nEndPara
; ++nPara
)
2243 GetTextEngine()->ImpInsertText( TextPaM( nPara
, 0 ), '\t' );
2248 // remove Tabs/Blanks
2249 OUString aText
= GetTextEngine()->GetText( nPara
);
2250 if ( !aText
.isEmpty() && (
2251 ( aText
[ 0 ] == '\t' ) ||
2252 ( aText
[ 0 ] == ' ' ) ) )
2254 GetTextEngine()->ImpDeleteText( TextSelection( TextPaM( nPara
, 0 ), TextPaM( nPara
, 1 ) ) );
2260 GetTextEngine()->UndoActionEnd();
2262 bool bRange
= aSel
.HasRange();
2265 ++aSel
.GetStart().GetIndex();
2266 if ( bRange
&& ( aSel
.GetEnd().GetPara() == nEndPara
) )
2267 ++aSel
.GetEnd().GetIndex();
2271 if ( aSel
.GetStart().GetIndex() )
2272 --aSel
.GetStart().GetIndex();
2273 if ( bRange
&& aSel
.GetEnd().GetIndex() )
2274 --aSel
.GetEnd().GetIndex();
2277 ImpSetSelection( aSel
);
2278 GetTextEngine()->FormatAndUpdate( this );
2283 bool TextView::IndentBlock()
2285 return ImpIndentBlock( true );
2288 bool TextView::UnindentBlock()
2290 return ImpIndentBlock( false );
2294 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */