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>
24 #include <vcl/textview.hxx>
25 #include <vcl/texteng.hxx>
26 #include <vcl/settings.hxx>
27 #include "textdoc.hxx"
28 #include <vcl/textdata.hxx>
29 #include <vcl/transfer.hxx>
30 #include <vcl/xtextedt.hxx>
31 #include "textdat2.hxx"
32 #include <vcl/commandevent.hxx>
33 #include <vcl/inputctx.hxx>
35 #include <svl/undo.hxx>
36 #include <vcl/cursor.hxx>
37 #include <vcl/weld.hxx>
38 #include <vcl/window.hxx>
39 #include <vcl/svapp.hxx>
40 #include <tools/stream.hxx>
42 #include <sal/log.hxx>
43 #include <sot/formats.hxx>
45 #include <cppuhelper/weak.hxx>
46 #include <cppuhelper/queryinterface.hxx>
47 #include <com/sun/star/i18n/XBreakIterator.hpp>
48 #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
49 #include <com/sun/star/i18n/WordType.hpp>
50 #include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp>
51 #include <com/sun/star/datatransfer/XTransferable.hpp>
52 #include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
53 #include <com/sun/star/datatransfer/clipboard/XFlushableClipboard.hpp>
54 #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
55 #include <com/sun/star/datatransfer/dnd/XDragGestureRecognizer.hpp>
56 #include <com/sun/star/datatransfer/dnd/XDropTarget.hpp>
57 #include <com/sun/star/util/SearchFlags.hpp>
59 #include <vcl/toolkit/edit.hxx>
61 #include <sot/exchange.hxx>
66 TETextDataObject::TETextDataObject( OUString aText
) : maText(std::move( aText
))
70 // css::uno::XInterface
71 css::uno::Any
TETextDataObject::queryInterface( const css::uno::Type
& rType
)
73 css::uno::Any aRet
= ::cppu::queryInterface( rType
, static_cast< css::datatransfer::XTransferable
* >(this) );
74 return (aRet
.hasValue() ? aRet
: OWeakObject::queryInterface( rType
));
77 // css::datatransfer::XTransferable
78 css::uno::Any
TETextDataObject::getTransferData( const css::datatransfer::DataFlavor
& rFlavor
)
82 SotClipboardFormatId nT
= SotExchange::GetFormat( rFlavor
);
83 if ( nT
== SotClipboardFormatId::STRING
)
87 else if ( nT
== SotClipboardFormatId::HTML
)
89 sal_uInt64 nLen
= GetHTMLStream().TellEnd();
90 GetHTMLStream().Seek(0);
92 css::uno::Sequence
< sal_Int8
> aSeq( nLen
);
93 memcpy( aSeq
.getArray(), GetHTMLStream().GetData(), nLen
);
98 throw css::datatransfer::UnsupportedFlavorException();
103 css::uno::Sequence
< css::datatransfer::DataFlavor
> TETextDataObject::getTransferDataFlavors( )
105 GetHTMLStream().Seek( STREAM_SEEK_TO_END
);
106 bool bHTML
= GetHTMLStream().Tell() > 0;
107 css::uno::Sequence
< css::datatransfer::DataFlavor
> aDataFlavors( bHTML
? 2 : 1 );
108 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING
, aDataFlavors
.getArray()[0] );
110 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::HTML
, aDataFlavors
.getArray()[1] );
114 sal_Bool
TETextDataObject::isDataFlavorSupported( const css::datatransfer::DataFlavor
& rFlavor
)
116 SotClipboardFormatId nT
= SotExchange::GetFormat( rFlavor
);
117 return ( nT
== SotClipboardFormatId::STRING
);
122 ExtTextEngine
* mpTextEngine
;
124 VclPtr
<vcl::Window
> mpWindow
;
125 TextSelection maSelection
;
128 std::unique_ptr
<vcl::Cursor
, o3tl::default_delete
<vcl::Cursor
>> mpCursor
;
130 std::unique_ptr
<TextDDInfo
, o3tl::default_delete
<TextDDInfo
>> mpDDInfo
;
132 std::unique_ptr
<SelectionEngine
> mpSelEngine
;
133 std::unique_ptr
<TextSelFunctionSet
> mpSelFuncSet
;
135 css::uno::Reference
< css::datatransfer::dnd::XDragSourceListener
> mxDnDListener
;
137 sal_uInt16 mnTravelXPos
;
139 bool mbAutoScroll
: 1;
140 bool mbInsertMode
: 1;
142 bool mbPaintSelection
: 1;
143 bool mbAutoIndent
: 1;
144 bool mbCursorEnabled
: 1;
145 bool mbClickedInSelection
: 1;
146 bool mbCursorAtEndOfLine
;
149 TextView::TextView( ExtTextEngine
* pEng
, vcl::Window
* pWindow
) :
150 mpImpl(new ImpTextView
)
152 pWindow
->EnableRTL( false );
154 mpImpl
->mpWindow
= pWindow
;
155 mpImpl
->mpTextEngine
= pEng
;
157 mpImpl
->mbPaintSelection
= true;
158 mpImpl
->mbAutoScroll
= true;
159 mpImpl
->mbInsertMode
= true;
160 mpImpl
->mbReadOnly
= 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 pWindow
->GetOutDev()->SetLineColor();
180 if ( pWindow
->GetDragGestureRecognizer().is() )
182 mpImpl
->mxDnDListener
= new vcl::unohelper::DragAndDropWrapper( this );
184 css::uno::Reference
< css::datatransfer::dnd::XDragGestureListener
> xDGL( mpImpl
->mxDnDListener
, css::uno::UNO_QUERY
);
185 pWindow
->GetDragGestureRecognizer()->addDragGestureListener( xDGL
);
186 css::uno::Reference
< css::datatransfer::dnd::XDropTargetListener
> xDTL( xDGL
, css::uno::UNO_QUERY
);
187 pWindow
->GetDropTarget()->addDropTargetListener( xDTL
);
188 pWindow
->GetDropTarget()->setActive( true );
189 pWindow
->GetDropTarget()->setDefaultActions( css::datatransfer::dnd::DNDConstants::ACTION_COPY_OR_MOVE
);
193 TextView::~TextView()
195 mpImpl
->mpSelEngine
.reset();
196 mpImpl
->mpSelFuncSet
.reset();
198 if ( mpImpl
->mpWindow
->GetCursor() == mpImpl
->mpCursor
.get() )
199 mpImpl
->mpWindow
->SetCursor( nullptr );
201 mpImpl
->mpCursor
.reset();
202 mpImpl
->mpDDInfo
.reset();
205 void TextView::Invalidate()
207 mpImpl
->mpWindow
->Invalidate();
210 void TextView::SetSelection( const TextSelection
& rTextSel
, bool bGotoCursor
)
212 // if someone left an empty attribute and then the Outliner manipulated the selection
213 if ( !mpImpl
->maSelection
.HasRange() )
214 mpImpl
->mpTextEngine
->CursorMoved( mpImpl
->maSelection
.GetStart().GetPara() );
216 // if the selection is manipulated after a KeyInput
217 mpImpl
->mpTextEngine
->CheckIdleFormatter();
220 TextSelection
aNewSel( rTextSel
);
221 mpImpl
->mpTextEngine
->ValidateSelection( aNewSel
);
222 ImpSetSelection( aNewSel
);
224 ShowCursor( bGotoCursor
);
227 void TextView::SetSelection( const TextSelection
& rTextSel
)
229 SetSelection( rTextSel
, mpImpl
->mbAutoScroll
);
232 const TextSelection
& TextView::GetSelection() const
234 return mpImpl
->maSelection
;
236 TextSelection
& TextView::GetSelection()
238 return mpImpl
->maSelection
;
241 void TextView::DeleteSelected()
245 mpImpl
->mpTextEngine
->UndoActionStart();
246 TextPaM aPaM
= mpImpl
->mpTextEngine
->ImpDeleteText( mpImpl
->maSelection
);
247 mpImpl
->mpTextEngine
->UndoActionEnd();
249 ImpSetSelection( aPaM
);
250 mpImpl
->mpTextEngine
->FormatAndUpdate( this );
254 void TextView::ImpPaint(vcl::RenderContext
& rRenderContext
, const Point
& rStartPos
, tools::Rectangle
const* pPaintArea
, TextSelection
const* pSelection
)
256 if (!mpImpl
->mbPaintSelection
)
258 pSelection
= nullptr;
262 // set correct background color;
263 // unfortunately we cannot detect if it has changed
264 vcl::Font aFont
= mpImpl
->mpTextEngine
->GetFont();
265 Color aColor
= rRenderContext
.GetBackground().GetColor();
266 aColor
.SetAlpha(255);
267 if (aColor
!= aFont
.GetFillColor())
269 if (aFont
.IsTransparent())
270 aColor
= COL_TRANSPARENT
;
271 aFont
.SetFillColor(aColor
);
272 mpImpl
->mpTextEngine
->maFont
= aFont
;
276 mpImpl
->mpTextEngine
->ImpPaint(&rRenderContext
, rStartPos
, pPaintArea
, pSelection
);
279 void TextView::Paint(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& rRect
)
281 ImpPaint(rRenderContext
, rRect
);
284 void TextView::ImpPaint(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& rRect
)
286 if ( !mpImpl
->mpTextEngine
->GetUpdateMode() || mpImpl
->mpTextEngine
->IsInUndo() )
289 TextSelection
*pDrawSelection
= nullptr;
290 if (mpImpl
->maSelection
.HasRange())
291 pDrawSelection
= &mpImpl
->maSelection
;
293 Point aStartPos
= ImpGetOutputStartPos(mpImpl
->maStartDocPos
);
294 ImpPaint(rRenderContext
, aStartPos
, &rRect
, pDrawSelection
);
297 void TextView::ImpSetSelection( const TextSelection
& rSelection
)
299 if (rSelection
== mpImpl
->maSelection
)
302 bool bCaret
= false, bSelection
= false;
303 const TextPaM
&rEnd
= rSelection
.GetEnd();
304 const TextPaM
&rOldEnd
= mpImpl
->maSelection
.GetEnd();
305 bool bGap
= rSelection
.HasRange(), bOldGap
= mpImpl
->maSelection
.HasRange();
311 mpImpl
->maSelection
= rSelection
;
314 mpImpl
->mpTextEngine
->Broadcast(TextHint(SfxHintId::TextViewSelectionChanged
));
317 mpImpl
->mpTextEngine
->Broadcast(TextHint(SfxHintId::TextViewCaretChanged
));
320 void TextView::ShowSelection()
322 ImpShowHideSelection();
325 void TextView::HideSelection()
327 ImpShowHideSelection();
330 void TextView::ShowSelection( const TextSelection
& rRange
)
332 ImpShowHideSelection( &rRange
);
335 void TextView::ImpShowHideSelection(const TextSelection
* pRange
)
337 const TextSelection
* pRangeOrSelection
= pRange
? pRange
: &mpImpl
->maSelection
;
339 if ( !pRangeOrSelection
->HasRange() )
342 if( mpImpl
->mpWindow
->IsPaintTransparent() )
343 mpImpl
->mpWindow
->Invalidate();
346 TextSelection
aRange( *pRangeOrSelection
);
348 bool bVisCursor
= mpImpl
->mpCursor
->IsVisible();
349 mpImpl
->mpCursor
->Hide();
352 mpImpl
->mpCursor
->Show();
356 bool TextView::KeyInput( const KeyEvent
& rKeyEvent
)
359 bool bModified
= false;
361 bool bEndKey
= false; // special CursorPosition
362 bool bAllowIdle
= true;
365 // the local bModified is not set e.g. by Cut/Paste, as here
366 // the update happens somewhere else
367 bool bWasModified
= mpImpl
->mpTextEngine
->IsModified();
368 mpImpl
->mpTextEngine
->SetModified( false );
370 TextSelection
aCurSel( mpImpl
->maSelection
);
371 TextSelection
aOldSel( aCurSel
);
373 sal_uInt16 nCode
= rKeyEvent
.GetKeyCode().GetCode();
374 KeyFuncType eFunc
= rKeyEvent
.GetKeyCode().GetFunction();
375 if ( eFunc
!= KeyFuncType::DONTKNOW
)
379 case KeyFuncType::CUT
:
381 if ( !mpImpl
->mbReadOnly
)
385 case KeyFuncType::COPY
:
390 case KeyFuncType::PASTE
:
392 if ( !mpImpl
->mbReadOnly
)
396 case KeyFuncType::UNDO
:
398 if ( !mpImpl
->mbReadOnly
)
402 case KeyFuncType::REDO
:
404 if ( !mpImpl
->mbReadOnly
)
409 default: // might get processed below
410 eFunc
= KeyFuncType::DONTKNOW
;
413 if ( eFunc
== KeyFuncType::DONTKNOW
)
425 case css::awt::Key::MOVE_WORD_FORWARD
:
426 case css::awt::Key::SELECT_WORD_FORWARD
:
427 case css::awt::Key::MOVE_WORD_BACKWARD
:
428 case css::awt::Key::SELECT_WORD_BACKWARD
:
429 case css::awt::Key::MOVE_TO_BEGIN_OF_LINE
:
430 case css::awt::Key::MOVE_TO_END_OF_LINE
:
431 case css::awt::Key::SELECT_TO_BEGIN_OF_LINE
:
432 case css::awt::Key::SELECT_TO_END_OF_LINE
:
433 case css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH
:
434 case css::awt::Key::MOVE_TO_END_OF_PARAGRAPH
:
435 case css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH
:
436 case css::awt::Key::SELECT_TO_END_OF_PARAGRAPH
:
437 case css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT
:
438 case css::awt::Key::MOVE_TO_END_OF_DOCUMENT
:
439 case css::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT
:
440 case css::awt::Key::SELECT_TO_END_OF_DOCUMENT
:
442 if ( ( !rKeyEvent
.GetKeyCode().IsMod2() || ( nCode
== KEY_LEFT
) || ( nCode
== KEY_RIGHT
) )
443 && !( rKeyEvent
.GetKeyCode().IsMod1() && ( nCode
== KEY_PAGEUP
|| nCode
== KEY_PAGEDOWN
) ) )
445 aCurSel
= ImpMoveCursor( rKeyEvent
);
446 if ( aCurSel
.HasRange() ) {
447 css::uno::Reference
<css::datatransfer::clipboard::XClipboard
> aSelection(GetSystemPrimarySelection());
451 if ( nCode
== KEY_END
)
460 case css::awt::Key::DELETE_WORD_BACKWARD
:
461 case css::awt::Key::DELETE_WORD_FORWARD
:
462 case css::awt::Key::DELETE_TO_BEGIN_OF_LINE
:
463 case css::awt::Key::DELETE_TO_END_OF_LINE
:
465 if ( !mpImpl
->mbReadOnly
&& !rKeyEvent
.GetKeyCode().IsMod2() )
467 sal_uInt8 nDel
= ( nCode
== KEY_DELETE
) ? DEL_RIGHT
: DEL_LEFT
;
468 sal_uInt8 nMode
= rKeyEvent
.GetKeyCode().IsMod1() ? DELMODE_RESTOFWORD
: DELMODE_SIMPLE
;
469 if ( ( nMode
== DELMODE_RESTOFWORD
) && rKeyEvent
.GetKeyCode().IsShift() )
470 nMode
= DELMODE_RESTOFCONTENT
;
474 case css::awt::Key::DELETE_WORD_BACKWARD
:
476 nMode
= DELMODE_RESTOFWORD
;
478 case css::awt::Key::DELETE_WORD_FORWARD
:
480 nMode
= DELMODE_RESTOFWORD
;
482 case css::awt::Key::DELETE_TO_BEGIN_OF_LINE
:
484 nMode
= DELMODE_RESTOFCONTENT
;
486 case css::awt::Key::DELETE_TO_END_OF_LINE
:
488 nMode
= DELMODE_RESTOFCONTENT
;
493 mpImpl
->mpTextEngine
->UndoActionStart();
494 aCurSel
= ImpDelete( nDel
, nMode
);
495 mpImpl
->mpTextEngine
->UndoActionEnd();
505 if ( !mpImpl
->mbReadOnly
&& !rKeyEvent
.GetKeyCode().IsShift() &&
506 !rKeyEvent
.GetKeyCode().IsMod1() && !rKeyEvent
.GetKeyCode().IsMod2() &&
507 ImplCheckTextLen( u
"x" ) )
509 aCurSel
= mpImpl
->mpTextEngine
->ImpInsertText( aCurSel
, '\t', !IsInsertMode() );
518 // do not swallow Shift-RETURN, as this would disable multi-line entries
519 // in dialogs & property editors
520 if ( !mpImpl
->mbReadOnly
&& !rKeyEvent
.GetKeyCode().IsMod1() &&
521 !rKeyEvent
.GetKeyCode().IsMod2() && ImplCheckTextLen( u
"x" ) )
523 mpImpl
->mpTextEngine
->UndoActionStart();
524 aCurSel
= mpImpl
->mpTextEngine
->ImpInsertParaBreak( aCurSel
);
525 if ( mpImpl
->mbAutoIndent
)
527 TextNode
* pPrev
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aCurSel
.GetEnd().GetPara() - 1 ].get();
529 while ( ( n
< pPrev
->GetText().getLength() ) && (
530 ( pPrev
->GetText()[ n
] == ' ' ) ||
531 ( pPrev
->GetText()[ n
] == '\t' ) ) )
536 aCurSel
= mpImpl
->mpTextEngine
->ImpInsertText( aCurSel
, pPrev
->GetText().copy( 0, n
) );
538 mpImpl
->mpTextEngine
->UndoActionEnd();
547 if ( !mpImpl
->mbReadOnly
)
548 SetInsertMode( !IsInsertMode() );
553 if ( TextEngine::IsSimpleCharInput( rKeyEvent
) )
555 sal_Unicode nCharCode
= rKeyEvent
.GetCharCode();
556 if ( !mpImpl
->mbReadOnly
&& ImplCheckTextLen( OUStringChar(nCharCode
) ) ) // otherwise swallow the character anyway
558 aCurSel
= mpImpl
->mpTextEngine
->ImpInsertText( nCharCode
, aCurSel
, !IsInsertMode(), true );
568 if ( aCurSel
!= aOldSel
) // Check if changed, maybe other method already changed mpImpl->maSelection, don't overwrite that!
569 ImpSetSelection( aCurSel
);
571 if ( ( nCode
!= KEY_UP
) && ( nCode
!= KEY_DOWN
) )
572 mpImpl
->mnTravelXPos
= TRAVEL_X_DONTKNOW
;
576 // Idle-Formatter only if AnyInput
577 if ( bAllowIdle
&& Application::AnyInput( VclInputFlags::KEYBOARD
) )
578 mpImpl
->mpTextEngine
->IdleFormatAndUpdate( this );
580 mpImpl
->mpTextEngine
->FormatAndUpdate( this);
584 // selection is painted now in ImpMoveCursor
585 ImpShowCursor( mpImpl
->mbAutoScroll
, true, bEndKey
);
588 if ( mpImpl
->mpTextEngine
->IsModified() )
589 mpImpl
->mpTextEngine
->Broadcast( TextHint( SfxHintId::TextModified
) );
590 else if ( bWasModified
)
591 mpImpl
->mpTextEngine
->SetModified( true );
596 void TextView::MouseButtonUp( const MouseEvent
& rMouseEvent
)
598 mpImpl
->mbClickedInSelection
= false;
599 mpImpl
->mnTravelXPos
= TRAVEL_X_DONTKNOW
;
600 mpImpl
->mpSelEngine
->SelMouseButtonUp( rMouseEvent
);
601 if ( rMouseEvent
.IsMiddle() && !IsReadOnly() &&
602 ( GetWindow()->GetSettings().GetMouseSettings().GetMiddleButtonAction() == MouseMiddleButtonAction::PasteSelection
) )
604 css::uno::Reference
<css::datatransfer::clipboard::XClipboard
> aSelection(GetSystemPrimarySelection());
606 if ( mpImpl
->mpTextEngine
->IsModified() )
607 mpImpl
->mpTextEngine
->Broadcast( TextHint( SfxHintId::TextModified
) );
609 else if ( rMouseEvent
.IsLeft() && GetSelection().HasRange() )
611 css::uno::Reference
<css::datatransfer::clipboard::XClipboard
> aSelection(GetSystemPrimarySelection());
616 void TextView::MouseButtonDown( const MouseEvent
& rMouseEvent
)
618 mpImpl
->mpTextEngine
->CheckIdleFormatter(); // for fast typing and MouseButtonDown
619 mpImpl
->mnTravelXPos
= TRAVEL_X_DONTKNOW
;
620 mpImpl
->mbClickedInSelection
= IsSelectionAtPoint( rMouseEvent
.GetPosPixel() );
622 mpImpl
->mpTextEngine
->SetActiveView( this );
624 mpImpl
->mpSelEngine
->SelMouseButtonDown( rMouseEvent
);
626 // mbu 20.01.2005 - SelMouseButtonDown() possibly triggers a 'selection changed'
627 // notification. The appropriate handler could change the current selection,
628 // which is the case in the MailMerge address block control. To enable select'n'drag
629 // we need to reevaluate the selection after the notification has been fired.
630 mpImpl
->mbClickedInSelection
= IsSelectionAtPoint( rMouseEvent
.GetPosPixel() );
633 if ( rMouseEvent
.IsShift() || ( rMouseEvent
.GetClicks() < 2 ))
636 if ( rMouseEvent
.IsMod2() )
639 ImpSetSelection( mpImpl
->maSelection
.GetEnd() );
640 SetCursorAtPoint( rMouseEvent
.GetPosPixel() ); // not set by SelectionEngine for MOD2
643 if ( rMouseEvent
.GetClicks() == 2 )
646 if ( mpImpl
->maSelection
.GetEnd().GetIndex() < mpImpl
->mpTextEngine
->GetTextLen( mpImpl
->maSelection
.GetEnd().GetPara() ) )
649 // tdf#57879 - expand selection to include connector punctuations
650 TextSelection aNewSel
;
651 mpImpl
->mpTextEngine
->GetWord( mpImpl
->maSelection
.GetEnd(), &aNewSel
.GetStart(), &aNewSel
.GetEnd() );
652 ImpSetSelection( aNewSel
);
657 else if ( rMouseEvent
.GetClicks() == 3 )
660 if ( mpImpl
->maSelection
.GetStart().GetIndex() || ( mpImpl
->maSelection
.GetEnd().GetIndex() < mpImpl
->mpTextEngine
->GetTextLen( mpImpl
->maSelection
.GetEnd().GetPara() ) ) )
663 TextSelection
aNewSel( mpImpl
->maSelection
);
664 aNewSel
.GetStart().GetIndex() = 0;
665 aNewSel
.GetEnd().GetIndex() = mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ mpImpl
->maSelection
.GetEnd().GetPara() ]->GetText().getLength();
666 ImpSetSelection( aNewSel
);
673 void TextView::MouseMove( const MouseEvent
& rMouseEvent
)
675 mpImpl
->mnTravelXPos
= TRAVEL_X_DONTKNOW
;
676 mpImpl
->mpSelEngine
->SelMouseMove( rMouseEvent
);
679 void TextView::Command( const CommandEvent
& rCEvt
)
681 mpImpl
->mpTextEngine
->CheckIdleFormatter(); // for fast typing and MouseButtonDown
682 mpImpl
->mpTextEngine
->SetActiveView( this );
684 if ( rCEvt
.GetCommand() == CommandEventId::StartExtTextInput
)
687 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ GetSelection().GetEnd().GetPara() ].get();
688 mpImpl
->mpTextEngine
->mpIMEInfos
= std::make_unique
<TEIMEInfos
>( GetSelection().GetEnd(), pNode
->GetText().copy( GetSelection().GetEnd().GetIndex() ) );
689 mpImpl
->mpTextEngine
->mpIMEInfos
->bWasCursorOverwrite
= !IsInsertMode();
691 else if ( rCEvt
.GetCommand() == CommandEventId::EndExtTextInput
)
693 SAL_WARN_IF( !mpImpl
->mpTextEngine
->mpIMEInfos
, "vcl", "CommandEventId::EndExtTextInput => No Start ?" );
694 if( mpImpl
->mpTextEngine
->mpIMEInfos
)
696 TEParaPortion
* pPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
.GetPara() );
697 pPortion
->MarkSelectionInvalid( mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
.GetIndex() );
699 bool bInsertMode
= !mpImpl
->mpTextEngine
->mpIMEInfos
->bWasCursorOverwrite
;
701 mpImpl
->mpTextEngine
->mpIMEInfos
.reset();
703 mpImpl
->mpTextEngine
->TextModified();
704 mpImpl
->mpTextEngine
->FormatAndUpdate( this );
706 SetInsertMode( bInsertMode
);
708 if ( mpImpl
->mpTextEngine
->IsModified() )
709 mpImpl
->mpTextEngine
->Broadcast( TextHint( SfxHintId::TextModified
) );
712 else if ( rCEvt
.GetCommand() == CommandEventId::ExtTextInput
)
714 SAL_WARN_IF( !mpImpl
->mpTextEngine
->mpIMEInfos
, "vcl", "CommandEventId::ExtTextInput => No Start ?" );
715 if( mpImpl
->mpTextEngine
->mpIMEInfos
)
717 const CommandExtTextInputData
* pData
= rCEvt
.GetExtTextInputData();
719 if ( !pData
->IsOnlyCursorChanged() )
721 TextSelection
aSelect( mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
);
722 aSelect
.GetEnd().GetIndex() += mpImpl
->mpTextEngine
->mpIMEInfos
->nLen
;
723 aSelect
= mpImpl
->mpTextEngine
->ImpDeleteText( aSelect
);
724 aSelect
= mpImpl
->mpTextEngine
->ImpInsertText( aSelect
, pData
->GetText() );
726 if ( mpImpl
->mpTextEngine
->mpIMEInfos
->bWasCursorOverwrite
)
728 const sal_Int32 nOldIMETextLen
= mpImpl
->mpTextEngine
->mpIMEInfos
->nLen
;
729 const sal_Int32 nNewIMETextLen
= pData
->GetText().getLength();
731 if ( ( nOldIMETextLen
> nNewIMETextLen
) &&
732 ( nNewIMETextLen
< mpImpl
->mpTextEngine
->mpIMEInfos
->aOldTextAfterStartPos
.getLength() ) )
734 // restore old characters
735 sal_Int32 nRestore
= nOldIMETextLen
- nNewIMETextLen
;
736 TextPaM
aPaM( mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
);
737 aPaM
.GetIndex() += nNewIMETextLen
;
738 mpImpl
->mpTextEngine
->ImpInsertText( aPaM
, mpImpl
->mpTextEngine
->mpIMEInfos
->aOldTextAfterStartPos
.copy( nNewIMETextLen
, nRestore
) );
740 else if ( ( nOldIMETextLen
< nNewIMETextLen
) &&
741 ( nOldIMETextLen
< mpImpl
->mpTextEngine
->mpIMEInfos
->aOldTextAfterStartPos
.getLength() ) )
744 const sal_Int32 nOverwrite
= std::min( nNewIMETextLen
, mpImpl
->mpTextEngine
->mpIMEInfos
->aOldTextAfterStartPos
.getLength() ) - nOldIMETextLen
;
745 SAL_WARN_IF( !nOverwrite
|| (nOverwrite
>= 0xFF00), "vcl", "IME Overwrite?!" );
746 TextPaM
aPaM( mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
);
747 aPaM
.GetIndex() += nNewIMETextLen
;
748 TextSelection
aSel( aPaM
);
749 aSel
.GetEnd().GetIndex() += nOverwrite
;
750 mpImpl
->mpTextEngine
->ImpDeleteText( aSel
);
754 if ( pData
->GetTextAttr() )
756 mpImpl
->mpTextEngine
->mpIMEInfos
->CopyAttribs( pData
->GetTextAttr(), pData
->GetText().getLength() );
760 mpImpl
->mpTextEngine
->mpIMEInfos
->DestroyAttribs();
763 TEParaPortion
* pPPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
.GetPara() );
764 pPPortion
->MarkSelectionInvalid( mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
.GetIndex() );
765 mpImpl
->mpTextEngine
->FormatAndUpdate( this );
768 TextSelection aNewSel
= TextPaM( mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
.GetPara(), mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
.GetIndex()+pData
->GetCursorPos() );
769 SetSelection( aNewSel
);
770 SetInsertMode( !pData
->IsCursorOverwrite() );
772 if ( pData
->IsCursorVisible() )
778 else if ( rCEvt
.GetCommand() == CommandEventId::CursorPos
)
780 if ( mpImpl
->mpTextEngine
->mpIMEInfos
&& mpImpl
->mpTextEngine
->mpIMEInfos
->nLen
)
782 TextPaM
aPaM( GetSelection().GetEnd() );
783 tools::Rectangle aR1
= mpImpl
->mpTextEngine
->PaMtoEditCursor( aPaM
);
785 sal_Int32 nInputEnd
= mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
.GetIndex() + mpImpl
->mpTextEngine
->mpIMEInfos
->nLen
;
787 if ( !mpImpl
->mpTextEngine
->IsFormatted() )
788 mpImpl
->mpTextEngine
->FormatDoc();
790 TEParaPortion
* pParaPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( aPaM
.GetPara() );
791 std::vector
<TextLine
>::size_type nLine
= pParaPortion
->GetLineNumber( aPaM
.GetIndex(), true );
792 TextLine
& rLine
= pParaPortion
->GetLines()[ nLine
];
793 if ( nInputEnd
> rLine
.GetEnd() )
794 nInputEnd
= rLine
.GetEnd();
795 tools::Rectangle aR2
= mpImpl
->mpTextEngine
->PaMtoEditCursor( TextPaM( aPaM
.GetPara(), nInputEnd
) );
797 tools::Long nWidth
= aR2
.Left()-aR1
.Right();
798 aR1
.Move( -GetStartDocPos().X(), -GetStartDocPos().Y() );
799 GetWindow()->SetCursorRect( &aR1
, nWidth
);
803 GetWindow()->SetCursorRect();
808 mpImpl
->mpSelEngine
->Command( rCEvt
);
812 void TextView::ShowCursor( bool bGotoCursor
, bool bForceVisCursor
)
814 // this setting has more weight
815 if ( !mpImpl
->mbAutoScroll
)
817 ImpShowCursor( bGotoCursor
, bForceVisCursor
, false );
820 void TextView::HideCursor()
822 mpImpl
->mpCursor
->Hide();
825 void TextView::Scroll( tools::Long ndX
, tools::Long ndY
)
827 SAL_WARN_IF( !mpImpl
->mpTextEngine
->IsFormatted(), "vcl", "Scroll: Not formatted!" );
832 Point
aNewStartPos( mpImpl
->maStartDocPos
);
835 aNewStartPos
.AdjustY( -ndY
);
836 if ( aNewStartPos
.Y() < 0 )
837 aNewStartPos
.setY( 0 );
840 aNewStartPos
.AdjustX( -ndX
);
841 if ( aNewStartPos
.X() < 0 )
842 aNewStartPos
.setX( 0 );
844 tools::Long nDiffX
= mpImpl
->maStartDocPos
.X() - aNewStartPos
.X();
845 tools::Long nDiffY
= mpImpl
->maStartDocPos
.Y() - aNewStartPos
.Y();
847 if ( nDiffX
|| nDiffY
)
849 bool bVisCursor
= mpImpl
->mpCursor
->IsVisible();
850 mpImpl
->mpCursor
->Hide();
851 mpImpl
->mpWindow
->PaintImmediately();
852 mpImpl
->maStartDocPos
= aNewStartPos
;
854 if ( mpImpl
->mpTextEngine
->IsRightToLeft() )
856 mpImpl
->mpWindow
->Scroll( nDiffX
, nDiffY
);
857 mpImpl
->mpWindow
->PaintImmediately();
858 mpImpl
->mpCursor
->SetPos( mpImpl
->mpCursor
->GetPos() + Point( nDiffX
, nDiffY
) );
859 if ( bVisCursor
&& !mpImpl
->mbReadOnly
)
860 mpImpl
->mpCursor
->Show();
863 mpImpl
->mpTextEngine
->Broadcast( TextHint( SfxHintId::TextViewScrolled
) );
866 void TextView::Undo()
868 mpImpl
->mpTextEngine
->SetActiveView( this );
869 mpImpl
->mpTextEngine
->GetUndoManager().Undo();
872 void TextView::Redo()
874 mpImpl
->mpTextEngine
->SetActiveView( this );
875 mpImpl
->mpTextEngine
->GetUndoManager().Redo();
880 mpImpl
->mpTextEngine
->UndoActionStart();
883 mpImpl
->mpTextEngine
->UndoActionEnd();
886 void TextView::Copy( css::uno::Reference
< css::datatransfer::clipboard::XClipboard
> const & rxClipboard
)
888 if ( !rxClipboard
.is() )
891 rtl::Reference
<TETextDataObject
> pDataObj
= new TETextDataObject( GetSelected() );
893 SolarMutexReleaser aReleaser
;
897 rxClipboard
->setContents( pDataObj
, nullptr );
899 css::uno::Reference
< css::datatransfer::clipboard::XFlushableClipboard
> xFlushableClipboard( rxClipboard
, css::uno::UNO_QUERY
);
900 if( xFlushableClipboard
.is() )
901 xFlushableClipboard
->flushClipboard();
903 catch( const css::uno::Exception
& )
908 void TextView::Copy()
910 css::uno::Reference
<css::datatransfer::clipboard::XClipboard
> aClipboard(GetWindow()->GetClipboard());
914 void TextView::Paste( css::uno::Reference
< css::datatransfer::clipboard::XClipboard
> const & rxClipboard
)
916 if ( !rxClipboard
.is() )
919 css::uno::Reference
< css::datatransfer::XTransferable
> xDataObj
;
923 SolarMutexReleaser aReleaser
;
924 xDataObj
= rxClipboard
->getContents();
926 catch( const css::uno::Exception
& )
930 if ( !xDataObj
.is() )
933 css::datatransfer::DataFlavor aFlavor
;
934 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING
, aFlavor
);
935 if ( !xDataObj
->isDataFlavorSupported( aFlavor
) )
940 css::uno::Any aData
= xDataObj
->getTransferData( aFlavor
);
943 bool bWasTruncated
= false;
944 if( mpImpl
->mpTextEngine
->GetMaxTextLen() != 0 )
945 bWasTruncated
= ImplTruncateNewText( aText
);
947 mpImpl
->mpTextEngine
->Broadcast( TextHint( SfxHintId::TextModified
) );
950 Edit::ShowTruncationWarning(mpImpl
->mpWindow
->GetFrameWeld());
952 catch( const css::datatransfer::UnsupportedFlavorException
& )
957 void TextView::Paste()
959 css::uno::Reference
<css::datatransfer::clipboard::XClipboard
> aClipboard(GetWindow()->GetClipboard());
963 OUString
TextView::GetSelected() const
965 return GetSelected( GetSystemLineEnd() );
968 OUString
TextView::GetSelected( LineEnd aSeparator
) const
970 return mpImpl
->mpTextEngine
->GetText( mpImpl
->maSelection
, aSeparator
);
973 void TextView::SetInsertMode( bool bInsert
)
975 if ( mpImpl
->mbInsertMode
!= bInsert
)
977 mpImpl
->mbInsertMode
= bInsert
;
978 ShowCursor( mpImpl
->mbAutoScroll
, false );
982 void TextView::SetReadOnly( bool bReadOnly
)
984 if ( mpImpl
->mbReadOnly
!= bReadOnly
)
986 mpImpl
->mbReadOnly
= bReadOnly
;
987 if ( !mpImpl
->mbReadOnly
)
988 ShowCursor( mpImpl
->mbAutoScroll
, false );
992 GetWindow()->SetInputContext( InputContext( mpImpl
->mpTextEngine
->GetFont(), bReadOnly
? InputContextFlags::Text
|InputContextFlags::ExtText
: InputContextFlags::NONE
) );
996 TextSelection
const & TextView::ImpMoveCursor( const KeyEvent
& rKeyEvent
)
998 // normally only needed for Up/Down; but who cares
999 mpImpl
->mpTextEngine
->CheckIdleFormatter();
1001 TextPaM
aPaM( mpImpl
->maSelection
.GetEnd() );
1002 TextPaM
aOldEnd( aPaM
);
1004 TextDirectionality eTextDirection
= TextDirectionality::LeftToRight_TopToBottom
;
1005 if ( mpImpl
->mpTextEngine
->IsRightToLeft() )
1006 eTextDirection
= TextDirectionality::RightToLeft_TopToBottom
;
1008 KeyEvent aTranslatedKeyEvent
= rKeyEvent
.LogicalTextDirectionality( eTextDirection
);
1010 bool bCtrl
= aTranslatedKeyEvent
.GetKeyCode().IsMod1();
1011 sal_uInt16 nCode
= aTranslatedKeyEvent
.GetKeyCode().GetCode();
1013 bool bSelect
= aTranslatedKeyEvent
.GetKeyCode().IsShift();
1016 case KEY_UP
: aPaM
= CursorUp( aPaM
);
1018 case KEY_DOWN
: aPaM
= CursorDown( aPaM
);
1023 aPaM
= CursorStartOfDoc();
1027 // tdf#145764 - move cursor to the beginning or the first non-space character in the same line
1028 const TextPaM aFirstWordPaM
= CursorFirstWord(aPaM
);
1029 aPaM
= aPaM
.GetIndex() == aFirstWordPaM
.GetIndex() ? CursorStartOfLine(aPaM
) : aFirstWordPaM
;
1032 case KEY_END
: aPaM
= bCtrl
? CursorEndOfDoc() : CursorEndOfLine( aPaM
);
1034 case KEY_PAGEUP
: aPaM
= bCtrl
? CursorStartOfDoc() : PageUp( aPaM
);
1036 case KEY_PAGEDOWN
: aPaM
= bCtrl
? CursorEndOfDoc() : PageDown( aPaM
);
1038 case KEY_LEFT
: aPaM
= bCtrl
? CursorWordLeft( aPaM
) : CursorLeft( aPaM
, aTranslatedKeyEvent
.GetKeyCode().IsMod2() ? sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCHARACTER
) : sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCELL
) );
1040 case KEY_RIGHT
: aPaM
= bCtrl
? CursorWordRight( aPaM
) : CursorRight( aPaM
, aTranslatedKeyEvent
.GetKeyCode().IsMod2() ? sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCHARACTER
) : sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCELL
) );
1042 case css::awt::Key::SELECT_WORD_FORWARD
:
1045 case css::awt::Key::MOVE_WORD_FORWARD
:
1046 aPaM
= CursorWordRight( aPaM
);
1048 case css::awt::Key::SELECT_WORD_BACKWARD
:
1051 case css::awt::Key::MOVE_WORD_BACKWARD
:
1052 aPaM
= CursorWordLeft( aPaM
);
1054 case css::awt::Key::SELECT_TO_BEGIN_OF_LINE
:
1057 case css::awt::Key::MOVE_TO_BEGIN_OF_LINE
:
1058 aPaM
= CursorStartOfLine( aPaM
);
1060 case css::awt::Key::SELECT_TO_END_OF_LINE
:
1063 case css::awt::Key::MOVE_TO_END_OF_LINE
:
1064 aPaM
= CursorEndOfLine( aPaM
);
1066 case css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH
:
1069 case css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH
:
1070 aPaM
= CursorStartOfParagraph( aPaM
);
1072 case css::awt::Key::SELECT_TO_END_OF_PARAGRAPH
:
1075 case css::awt::Key::MOVE_TO_END_OF_PARAGRAPH
:
1076 aPaM
= CursorEndOfParagraph( aPaM
);
1078 case css::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT
:
1081 case css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT
:
1082 aPaM
= CursorStartOfDoc();
1084 case css::awt::Key::SELECT_TO_END_OF_DOCUMENT
:
1087 case css::awt::Key::MOVE_TO_END_OF_DOCUMENT
:
1088 aPaM
= CursorEndOfDoc();
1092 // might cause a CreateAnchor or Deselection all
1093 mpImpl
->mpSelEngine
->CursorPosChanging( bSelect
, aTranslatedKeyEvent
.GetKeyCode().IsMod1() );
1095 if ( aOldEnd
!= aPaM
)
1097 mpImpl
->mpTextEngine
->CursorMoved( aOldEnd
.GetPara() );
1099 TextSelection
aNewSelection( mpImpl
->maSelection
);
1100 aNewSelection
.GetEnd() = aPaM
;
1103 // extend the selection
1104 ImpSetSelection( aNewSelection
);
1105 ShowSelection( TextSelection( aOldEnd
, aPaM
) );
1109 aNewSelection
.GetStart() = aPaM
;
1110 ImpSetSelection( aNewSelection
);
1114 return mpImpl
->maSelection
;
1117 void TextView::InsertText( const OUString
& rStr
)
1119 mpImpl
->mpTextEngine
->UndoActionStart();
1121 TextSelection aNewSel
= mpImpl
->mpTextEngine
->ImpInsertText( mpImpl
->maSelection
, rStr
);
1123 ImpSetSelection( aNewSel
);
1125 mpImpl
->mpTextEngine
->UndoActionEnd();
1127 mpImpl
->mpTextEngine
->FormatAndUpdate( this );
1130 TextPaM
TextView::CursorLeft( const TextPaM
& rPaM
, sal_uInt16 nCharacterIteratorMode
)
1132 TextPaM
aPaM( rPaM
);
1134 if ( aPaM
.GetIndex() )
1136 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aPaM
.GetPara() ].get();
1137 css::uno::Reference
< css::i18n::XBreakIterator
> xBI
= mpImpl
->mpTextEngine
->GetBreakIterator();
1138 sal_Int32 nCount
= 1;
1139 aPaM
.GetIndex() = xBI
->previousCharacters( pNode
->GetText(), aPaM
.GetIndex(), mpImpl
->mpTextEngine
->GetLocale(), nCharacterIteratorMode
, nCount
, nCount
);
1141 else if ( aPaM
.GetPara() )
1144 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aPaM
.GetPara() ].get();
1145 aPaM
.GetIndex() = pNode
->GetText().getLength();
1150 TextPaM
TextView::CursorRight( const TextPaM
& rPaM
, sal_uInt16 nCharacterIteratorMode
)
1152 TextPaM
aPaM( rPaM
);
1154 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aPaM
.GetPara() ].get();
1155 if ( aPaM
.GetIndex() < pNode
->GetText().getLength() )
1157 css::uno::Reference
< css::i18n::XBreakIterator
> xBI
= mpImpl
->mpTextEngine
->GetBreakIterator();
1158 sal_Int32 nCount
= 1;
1159 aPaM
.GetIndex() = xBI
->nextCharacters( pNode
->GetText(), aPaM
.GetIndex(), mpImpl
->mpTextEngine
->GetLocale(), nCharacterIteratorMode
, nCount
, nCount
);
1161 else if ( aPaM
.GetPara() < ( mpImpl
->mpTextEngine
->mpDoc
->GetNodes().size()-1) )
1164 aPaM
.GetIndex() = 0;
1170 TextPaM
TextView::CursorFirstWord( const TextPaM
& rPaM
)
1173 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[aPaM
.GetPara()].get();
1175 css::uno::Reference
<css::i18n::XBreakIterator
> xBI
= mpImpl
->mpTextEngine
->GetBreakIterator();
1176 aPaM
.GetIndex() = xBI
->beginOfSentence(pNode
->GetText(), 0, mpImpl
->mpTextEngine
->GetLocale());
1181 TextPaM
TextView::CursorWordLeft( const TextPaM
& rPaM
)
1183 TextPaM
aPaM( rPaM
);
1185 if ( aPaM
.GetIndex() )
1187 // tdf#57879 - expand selection to the left to include connector punctuations
1188 mpImpl
->mpTextEngine
->GetWord( rPaM
, &aPaM
);
1189 if ( aPaM
.GetIndex() >= rPaM
.GetIndex() )
1191 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aPaM
.GetPara() ].get();
1192 css::uno::Reference
< css::i18n::XBreakIterator
> xBI
= mpImpl
->mpTextEngine
->GetBreakIterator();
1193 aPaM
.GetIndex() = xBI
->previousWord( pNode
->GetText(), rPaM
.GetIndex(), mpImpl
->mpTextEngine
->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES
).startPos
;
1194 if ( aPaM
.GetIndex() > 0 )
1195 mpImpl
->mpTextEngine
->GetWord( aPaM
, &aPaM
);
1197 aPaM
.GetIndex() = 0;
1200 else if ( aPaM
.GetPara() )
1203 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aPaM
.GetPara() ].get();
1204 aPaM
.GetIndex() = pNode
->GetText().getLength();
1209 TextPaM
TextView::CursorWordRight( const TextPaM
& rPaM
)
1211 TextPaM
aPaM( rPaM
);
1213 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aPaM
.GetPara() ].get();
1214 if ( aPaM
.GetIndex() < pNode
->GetText().getLength() )
1216 css::uno::Reference
< css::i18n::XBreakIterator
> xBI
= mpImpl
->mpTextEngine
->GetBreakIterator();
1217 aPaM
.GetIndex() = xBI
->nextWord( pNode
->GetText(), aPaM
.GetIndex(), mpImpl
->mpTextEngine
->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES
).endPos
;
1218 mpImpl
->mpTextEngine
->GetWord( aPaM
, nullptr, &aPaM
);
1220 else if ( aPaM
.GetPara() < ( mpImpl
->mpTextEngine
->mpDoc
->GetNodes().size()-1) )
1223 aPaM
.GetIndex() = 0;
1229 TextPaM
TextView::ImpDelete( sal_uInt8 nMode
, sal_uInt8 nDelMode
)
1231 if ( mpImpl
->maSelection
.HasRange() ) // only delete selection
1232 return mpImpl
->mpTextEngine
->ImpDeleteText( mpImpl
->maSelection
);
1234 TextPaM aStartPaM
= mpImpl
->maSelection
.GetStart();
1235 TextPaM aEndPaM
= aStartPaM
;
1236 if ( nMode
== DEL_LEFT
)
1238 if ( nDelMode
== DELMODE_SIMPLE
)
1240 aEndPaM
= CursorLeft( aEndPaM
, sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCHARACTER
) );
1242 else if ( nDelMode
== DELMODE_RESTOFWORD
)
1244 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aEndPaM
.GetPara() ].get();
1245 css::uno::Reference
< css::i18n::XBreakIterator
> xBI
= mpImpl
->mpTextEngine
->GetBreakIterator();
1246 css::i18n::Boundary aBoundary
= xBI
->getWordBoundary( pNode
->GetText(), mpImpl
->maSelection
.GetEnd().GetIndex(), mpImpl
->mpTextEngine
->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES
, true );
1247 if ( aBoundary
.startPos
== mpImpl
->maSelection
.GetEnd().GetIndex() )
1248 aBoundary
= xBI
->previousWord( pNode
->GetText(), mpImpl
->maSelection
.GetEnd().GetIndex(), mpImpl
->mpTextEngine
->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES
);
1249 // #i63506# startPos is -1 when the paragraph starts with a tab
1250 aEndPaM
.GetIndex() = std::max
<sal_Int32
>(aBoundary
.startPos
, 0);
1252 else // DELMODE_RESTOFCONTENT
1254 if ( aEndPaM
.GetIndex() != 0 )
1255 aEndPaM
.GetIndex() = 0;
1256 else if ( aEndPaM
.GetPara() )
1258 // previous paragraph
1259 aEndPaM
.GetPara()--;
1260 aEndPaM
.GetIndex() = 0;
1266 if ( nDelMode
== DELMODE_SIMPLE
)
1268 aEndPaM
= CursorRight( aEndPaM
, sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCELL
) );
1270 else if ( nDelMode
== DELMODE_RESTOFWORD
)
1272 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aEndPaM
.GetPara() ].get();
1273 css::uno::Reference
< css::i18n::XBreakIterator
> xBI
= mpImpl
->mpTextEngine
->GetBreakIterator();
1274 css::i18n::Boundary aBoundary
= xBI
->nextWord( pNode
->GetText(), mpImpl
->maSelection
.GetEnd().GetIndex(), mpImpl
->mpTextEngine
->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES
);
1275 aEndPaM
.GetIndex() = aBoundary
.startPos
;
1277 else // DELMODE_RESTOFCONTENT
1279 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aEndPaM
.GetPara() ].get();
1280 if ( aEndPaM
.GetIndex() < pNode
->GetText().getLength() )
1281 aEndPaM
.GetIndex() = pNode
->GetText().getLength();
1282 else if ( aEndPaM
.GetPara() < ( mpImpl
->mpTextEngine
->mpDoc
->GetNodes().size() - 1 ) )
1285 aEndPaM
.GetPara()++;
1286 TextNode
* pNextNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aEndPaM
.GetPara() ].get();
1287 aEndPaM
.GetIndex() = pNextNode
->GetText().getLength();
1292 return mpImpl
->mpTextEngine
->ImpDeleteText( TextSelection( aStartPaM
, aEndPaM
) );
1295 TextPaM
TextView::CursorUp( const TextPaM
& rPaM
)
1297 TextPaM
aPaM( rPaM
);
1300 if ( mpImpl
->mnTravelXPos
== TRAVEL_X_DONTKNOW
)
1302 nX
= mpImpl
->mpTextEngine
->GetEditCursor( rPaM
, false ).Left();
1303 mpImpl
->mnTravelXPos
= static_cast<sal_uInt16
>(nX
)+1;
1306 nX
= mpImpl
->mnTravelXPos
;
1308 TEParaPortion
* pPPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( rPaM
.GetPara() );
1309 std::vector
<TextLine
>::size_type nLine
= pPPortion
->GetLineNumber( rPaM
.GetIndex(), false );
1310 if ( nLine
) // same paragraph
1312 aPaM
.GetIndex() = mpImpl
->mpTextEngine
->GetCharPos( rPaM
.GetPara(), nLine
-1, nX
);
1313 // If we need to go to the end of a line that was wrapped automatically,
1314 // the cursor ends up at the beginning of the 2nd line
1315 // Problem: Last character of an automatically wrapped line = Cursor
1316 TextLine
& rLine
= pPPortion
->GetLines()[ nLine
- 1 ];
1317 if ( aPaM
.GetIndex() && ( aPaM
.GetIndex() == rLine
.GetEnd() ) )
1320 else if ( rPaM
.GetPara() ) // previous paragraph
1323 pPPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( aPaM
.GetPara() );
1324 std::vector
<TextLine
>::size_type nL
= pPPortion
->GetLines().size() - 1;
1325 aPaM
.GetIndex() = mpImpl
->mpTextEngine
->GetCharPos( aPaM
.GetPara(), nL
, nX
+1 );
1331 TextPaM
TextView::CursorDown( const TextPaM
& rPaM
)
1333 TextPaM
aPaM( rPaM
);
1336 if ( mpImpl
->mnTravelXPos
== TRAVEL_X_DONTKNOW
)
1338 nX
= mpImpl
->mpTextEngine
->GetEditCursor( rPaM
, false ).Left();
1339 mpImpl
->mnTravelXPos
= static_cast<sal_uInt16
>(nX
)+1;
1342 nX
= mpImpl
->mnTravelXPos
;
1344 TEParaPortion
* pPPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( rPaM
.GetPara() );
1345 std::vector
<TextLine
>::size_type nLine
= pPPortion
->GetLineNumber( rPaM
.GetIndex(), false );
1346 if ( nLine
< ( pPPortion
->GetLines().size() - 1 ) )
1348 aPaM
.GetIndex() = mpImpl
->mpTextEngine
->GetCharPos( rPaM
.GetPara(), nLine
+1, nX
);
1350 // special case CursorUp
1351 TextLine
& rLine
= pPPortion
->GetLines()[ nLine
+ 1 ];
1352 if ( ( aPaM
.GetIndex() == rLine
.GetEnd() ) && ( aPaM
.GetIndex() > rLine
.GetStart() ) && aPaM
.GetIndex() < pPPortion
->GetNode()->GetText().getLength() )
1355 else if ( rPaM
.GetPara() < ( mpImpl
->mpTextEngine
->mpDoc
->GetNodes().size() - 1 ) ) // next paragraph
1358 pPPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( aPaM
.GetPara() );
1359 aPaM
.GetIndex() = mpImpl
->mpTextEngine
->GetCharPos( aPaM
.GetPara(), 0, nX
+1 );
1360 TextLine
& rLine
= pPPortion
->GetLines().front();
1361 if ( ( aPaM
.GetIndex() == rLine
.GetEnd() ) && ( aPaM
.GetIndex() > rLine
.GetStart() ) && ( pPPortion
->GetLines().size() > 1 ) )
1368 TextPaM
TextView::CursorStartOfLine( const TextPaM
& rPaM
)
1370 TextPaM
aPaM( rPaM
);
1372 TEParaPortion
* pPPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( rPaM
.GetPara() );
1373 std::vector
<TextLine
>::size_type nLine
= pPPortion
->GetLineNumber( aPaM
.GetIndex(), false );
1374 TextLine
& rLine
= pPPortion
->GetLines()[ nLine
];
1375 aPaM
.GetIndex() = rLine
.GetStart();
1380 TextPaM
TextView::CursorEndOfLine( const TextPaM
& rPaM
)
1382 TextPaM
aPaM( rPaM
);
1384 TEParaPortion
* pPPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( rPaM
.GetPara() );
1385 std::vector
<TextLine
>::size_type nLine
= pPPortion
->GetLineNumber( aPaM
.GetIndex(), false );
1386 TextLine
& rLine
= pPPortion
->GetLines()[ nLine
];
1387 aPaM
.GetIndex() = rLine
.GetEnd();
1389 if ( rLine
.GetEnd() > rLine
.GetStart() ) // empty line
1391 sal_Unicode cLastChar
= pPPortion
->GetNode()->GetText()[ aPaM
.GetIndex()-1 ];
1392 if ( ( cLastChar
== ' ' ) && ( aPaM
.GetIndex() != pPPortion
->GetNode()->GetText().getLength() ) )
1394 // for a blank in an automatically-wrapped line it is better to stand before it,
1395 // as the user will intend to stand behind the prior word.
1396 // If there is a change, special case for Pos1 after End!
1403 TextPaM
TextView::CursorStartOfParagraph( const TextPaM
& rPaM
)
1405 TextPaM
aPaM( rPaM
);
1406 aPaM
.GetIndex() = 0;
1410 TextPaM
TextView::CursorEndOfParagraph( const TextPaM
& rPaM
)
1412 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ rPaM
.GetPara() ].get();
1413 TextPaM
aPaM( rPaM
);
1414 aPaM
.GetIndex() = pNode
->GetText().getLength();
1418 TextPaM
TextView::CursorStartOfDoc()
1420 TextPaM
aPaM( 0, 0 );
1424 TextPaM
TextView::CursorEndOfDoc()
1426 const sal_uInt32 nNode
= static_cast<sal_uInt32
>(mpImpl
->mpTextEngine
->mpDoc
->GetNodes().size() - 1);
1427 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ nNode
].get();
1428 TextPaM
aPaM( nNode
, pNode
->GetText().getLength() );
1432 TextPaM
TextView::PageUp( const TextPaM
& rPaM
)
1434 tools::Rectangle aRect
= mpImpl
->mpTextEngine
->PaMtoEditCursor( rPaM
);
1435 Point aTopLeft
= aRect
.TopLeft();
1436 aTopLeft
.AdjustY( -(mpImpl
->mpWindow
->GetOutputSizePixel().Height() * 9/10) );
1437 aTopLeft
.AdjustX(1 );
1438 if ( aTopLeft
.Y() < 0 )
1441 TextPaM aPaM
= mpImpl
->mpTextEngine
->GetPaM( aTopLeft
);
1445 TextPaM
TextView::PageDown( const TextPaM
& rPaM
)
1447 tools::Rectangle aRect
= mpImpl
->mpTextEngine
->PaMtoEditCursor( rPaM
);
1448 Point aBottomRight
= aRect
.BottomRight();
1449 aBottomRight
.AdjustY(mpImpl
->mpWindow
->GetOutputSizePixel().Height() * 9/10 );
1450 aBottomRight
.AdjustX(1 );
1451 tools::Long nHeight
= mpImpl
->mpTextEngine
->GetTextHeight();
1452 if ( aBottomRight
.Y() > nHeight
)
1453 aBottomRight
.setY( nHeight
-1 );
1455 TextPaM aPaM
= mpImpl
->mpTextEngine
->GetPaM( aBottomRight
);
1459 void TextView::ImpShowCursor( bool bGotoCursor
, bool bForceVisCursor
, bool bSpecial
)
1461 if ( mpImpl
->mpTextEngine
->IsFormatting() )
1463 if ( !mpImpl
->mpTextEngine
->GetUpdateMode() )
1465 if ( mpImpl
->mpTextEngine
->IsInUndo() )
1468 mpImpl
->mpTextEngine
->CheckIdleFormatter();
1469 if ( !mpImpl
->mpTextEngine
->IsFormatted() )
1470 mpImpl
->mpTextEngine
->FormatAndUpdate( this );
1472 TextPaM
aPaM( mpImpl
->maSelection
.GetEnd() );
1473 tools::Rectangle aEditCursor
= mpImpl
->mpTextEngine
->PaMtoEditCursor( aPaM
, bSpecial
);
1475 // Remember that we placed the cursor behind the last character of a line
1476 mpImpl
->mbCursorAtEndOfLine
= false;
1479 TEParaPortion
* pParaPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( aPaM
.GetPara() );
1480 mpImpl
->mbCursorAtEndOfLine
=
1481 pParaPortion
->GetLineNumber( aPaM
.GetIndex(), true ) != pParaPortion
->GetLineNumber( aPaM
.GetIndex(), false );
1484 if ( !IsInsertMode() && !mpImpl
->maSelection
.HasRange() )
1486 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aPaM
.GetPara() ].get();
1487 if ( !pNode
->GetText().isEmpty() && ( aPaM
.GetIndex() < pNode
->GetText().getLength() ) )
1489 // If we are behind a portion, and the next portion has other direction, we must change position...
1490 aEditCursor
.SetLeft( mpImpl
->mpTextEngine
->GetEditCursor( aPaM
, false, true ).Left() );
1491 aEditCursor
.SetRight( aEditCursor
.Left() );
1493 TEParaPortion
* pParaPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( aPaM
.GetPara() );
1495 sal_Int32 nTextPortionStart
= 0;
1496 std::size_t nTextPortion
= pParaPortion
->GetTextPortions().FindPortion( aPaM
.GetIndex(), nTextPortionStart
, true );
1497 TETextPortion
& rTextPortion
= pParaPortion
->GetTextPortions()[ nTextPortion
];
1498 if ( rTextPortion
.GetKind() == PORTIONKIND_TAB
)
1500 aEditCursor
.AdjustRight(rTextPortion
.GetWidth() );
1504 TextPaM aNext
= CursorRight( TextPaM( aPaM
.GetPara(), aPaM
.GetIndex() ), sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCELL
) );
1505 aEditCursor
.SetRight( mpImpl
->mpTextEngine
->GetEditCursor( aNext
, true ).Left() );
1510 Size aOutSz
= mpImpl
->mpWindow
->GetOutputSizePixel();
1511 if ( aEditCursor
.GetHeight() > aOutSz
.Height() )
1512 aEditCursor
.SetBottom( aEditCursor
.Top() + aOutSz
.Height() - 1 );
1514 aEditCursor
.AdjustLeft( -1 );
1517 // #i81283# protect maStartDocPos against initialization problems
1518 && aOutSz
.Width() && aOutSz
.Height()
1521 tools::Long nVisStartY
= mpImpl
->maStartDocPos
.Y();
1522 tools::Long nVisEndY
= mpImpl
->maStartDocPos
.Y() + aOutSz
.Height();
1523 tools::Long nVisStartX
= mpImpl
->maStartDocPos
.X();
1524 tools::Long nVisEndX
= mpImpl
->maStartDocPos
.X() + aOutSz
.Width();
1525 tools::Long nMoreX
= aOutSz
.Width() / 4;
1527 Point
aNewStartPos( mpImpl
->maStartDocPos
);
1529 if ( aEditCursor
.Bottom() > nVisEndY
)
1531 aNewStartPos
.AdjustY( aEditCursor
.Bottom() - nVisEndY
);
1533 else if ( aEditCursor
.Top() < nVisStartY
)
1535 aNewStartPos
.AdjustY( -( nVisStartY
- aEditCursor
.Top() ) );
1538 if ( aEditCursor
.Right() >= nVisEndX
)
1540 aNewStartPos
.AdjustX( aEditCursor
.Right() - nVisEndX
);
1542 // do you want some more?
1543 aNewStartPos
.AdjustX(nMoreX
);
1545 else if ( aEditCursor
.Left() <= nVisStartX
)
1547 aNewStartPos
.AdjustX( -( nVisStartX
- aEditCursor
.Left() ) );
1549 // do you want some more?
1550 aNewStartPos
.AdjustX( -nMoreX
);
1553 // X can be wrong for the 'some more' above:
1554 // sal_uLong nMaxTextWidth = mpImpl->mpTextEngine->GetMaxTextWidth();
1555 // if ( !nMaxTextWidth || ( nMaxTextWidth > 0x7FFFFFFF ) )
1556 // nMaxTextWidth = 0x7FFFFFFF;
1557 // long nMaxX = (long)nMaxTextWidth - aOutSz.Width();
1558 tools::Long nMaxX
= mpImpl
->mpTextEngine
->CalcTextWidth() - aOutSz
.Width();
1562 if ( aNewStartPos
.X() < 0 )
1563 aNewStartPos
.setX( 0 );
1564 else if ( aNewStartPos
.X() > nMaxX
)
1565 aNewStartPos
.setX( nMaxX
);
1567 // Y should not be further down than needed
1568 tools::Long nYMax
= mpImpl
->mpTextEngine
->GetTextHeight() - aOutSz
.Height();
1571 if ( aNewStartPos
.Y() > nYMax
)
1572 aNewStartPos
.setY( nYMax
);
1574 if ( aNewStartPos
!= mpImpl
->maStartDocPos
)
1575 Scroll( -(aNewStartPos
.X() - mpImpl
->maStartDocPos
.X()), -(aNewStartPos
.Y() - mpImpl
->maStartDocPos
.Y()) );
1578 if ( aEditCursor
.Right() < aEditCursor
.Left() )
1580 tools::Long n
= aEditCursor
.Left();
1581 aEditCursor
.SetLeft( aEditCursor
.Right() );
1582 aEditCursor
.SetRight( n
);
1585 Point
aPoint( GetWindowPos( !mpImpl
->mpTextEngine
->IsRightToLeft() ? aEditCursor
.TopLeft() : aEditCursor
.TopRight() ) );
1586 mpImpl
->mpCursor
->SetPos( aPoint
);
1587 mpImpl
->mpCursor
->SetSize( aEditCursor
.GetSize() );
1588 if ( bForceVisCursor
&& mpImpl
->mbCursorEnabled
)
1589 mpImpl
->mpCursor
->Show();
1592 void TextView::SetCursorAtPoint( const Point
& rPosPixel
)
1594 mpImpl
->mpTextEngine
->CheckIdleFormatter();
1596 Point aDocPos
= GetDocPos( rPosPixel
);
1598 TextPaM aPaM
= mpImpl
->mpTextEngine
->GetPaM( aDocPos
);
1600 // aTmpNewSel: Diff between old and new; not the new selection
1601 TextSelection
aTmpNewSel( mpImpl
->maSelection
.GetEnd(), aPaM
);
1602 TextSelection
aNewSel( mpImpl
->maSelection
);
1603 aNewSel
.GetEnd() = aPaM
;
1605 if ( !mpImpl
->mpSelEngine
->HasAnchor() )
1607 if ( mpImpl
->maSelection
.GetStart() != aPaM
)
1608 mpImpl
->mpTextEngine
->CursorMoved( mpImpl
->maSelection
.GetStart().GetPara() );
1609 aNewSel
.GetStart() = aPaM
;
1610 ImpSetSelection( aNewSel
);
1614 ImpSetSelection( aNewSel
);
1615 ShowSelection( aTmpNewSel
);
1618 bool bForceCursor
= !mpImpl
->mpDDInfo
; // && !mbInSelection
1619 ImpShowCursor( mpImpl
->mbAutoScroll
, bForceCursor
, false );
1622 bool TextView::IsSelectionAtPoint( const Point
& rPosPixel
)
1624 Point aDocPos
= GetDocPos( rPosPixel
);
1625 TextPaM aPaM
= mpImpl
->mpTextEngine
->GetPaM( aDocPos
);
1626 // BeginDrag is only called, however, if IsSelectionAtPoint()
1627 // Problem: IsSelectionAtPoint is not called by Command()
1628 // if before MBDown returned false.
1629 return IsInSelection( aPaM
);
1632 bool TextView::IsInSelection( const TextPaM
& rPaM
) const
1634 TextSelection aSel
= mpImpl
->maSelection
;
1637 const sal_uInt32 nStartNode
= aSel
.GetStart().GetPara();
1638 const sal_uInt32 nEndNode
= aSel
.GetEnd().GetPara();
1639 const sal_uInt32 nCurNode
= rPaM
.GetPara();
1641 if ( ( nCurNode
> nStartNode
) && ( nCurNode
< nEndNode
) )
1644 if ( nStartNode
== nEndNode
)
1646 if ( nCurNode
== nStartNode
)
1647 if ( ( rPaM
.GetIndex() >= aSel
.GetStart().GetIndex() ) && ( rPaM
.GetIndex() < aSel
.GetEnd().GetIndex() ) )
1650 else if ( ( nCurNode
== nStartNode
) && ( rPaM
.GetIndex() >= aSel
.GetStart().GetIndex() ) )
1652 else if ( ( nCurNode
== nEndNode
) && ( rPaM
.GetIndex() < aSel
.GetEnd().GetIndex() ) )
1658 void TextView::ImpHideDDCursor()
1660 if ( mpImpl
->mpDDInfo
&& mpImpl
->mpDDInfo
->mbVisCursor
)
1662 mpImpl
->mpDDInfo
->maCursor
.Hide();
1663 mpImpl
->mpDDInfo
->mbVisCursor
= false;
1667 void TextView::ImpShowDDCursor()
1669 if ( !mpImpl
->mpDDInfo
->mbVisCursor
)
1671 tools::Rectangle aCursor
= mpImpl
->mpTextEngine
->PaMtoEditCursor( mpImpl
->mpDDInfo
->maDropPos
, true );
1672 aCursor
.AdjustRight( 1 );
1673 aCursor
.SetPos( GetWindowPos( aCursor
.TopLeft() ) );
1675 mpImpl
->mpDDInfo
->maCursor
.SetWindow( mpImpl
->mpWindow
);
1676 mpImpl
->mpDDInfo
->maCursor
.SetPos( aCursor
.TopLeft() );
1677 mpImpl
->mpDDInfo
->maCursor
.SetSize( aCursor
.GetSize() );
1678 mpImpl
->mpDDInfo
->maCursor
.Show();
1679 mpImpl
->mpDDInfo
->mbVisCursor
= true;
1683 void TextView::SetPaintSelection( bool bPaint
)
1685 if ( bPaint
!= mpImpl
->mbPaintSelection
)
1687 mpImpl
->mbPaintSelection
= bPaint
;
1688 ShowSelection( mpImpl
->maSelection
);
1692 void TextView::Read( SvStream
& rInput
)
1694 mpImpl
->mpTextEngine
->Read( rInput
, &mpImpl
->maSelection
);
1698 bool TextView::ImplTruncateNewText( OUString
& rNewText
) const
1700 bool bTruncated
= false;
1702 const sal_Int32 nMaxLen
= mpImpl
->mpTextEngine
->GetMaxTextLen();
1703 // 0 means unlimited
1706 const sal_Int32 nCurLen
= mpImpl
->mpTextEngine
->GetTextLen();
1708 const sal_Int32 nNewLen
= rNewText
.getLength();
1709 if ( nCurLen
+ nNewLen
> nMaxLen
)
1711 // see how much text will be replaced
1712 const sal_Int32 nSelLen
= mpImpl
->mpTextEngine
->GetTextLen( mpImpl
->maSelection
);
1713 if ( nCurLen
+ nNewLen
- nSelLen
> nMaxLen
)
1715 const sal_Int32 nTruncatedLen
= nMaxLen
- (nCurLen
- nSelLen
);
1716 rNewText
= rNewText
.copy( 0, nTruncatedLen
);
1724 bool TextView::ImplCheckTextLen( std::u16string_view rNewText
) const
1727 if ( mpImpl
->mpTextEngine
->GetMaxTextLen() )
1729 sal_Int32 n
= mpImpl
->mpTextEngine
->GetTextLen() + rNewText
.size();
1730 if ( n
> mpImpl
->mpTextEngine
->GetMaxTextLen() )
1732 // calculate how much text is being deleted
1733 n
-= mpImpl
->mpTextEngine
->GetTextLen( mpImpl
->maSelection
);
1734 if ( n
> mpImpl
->mpTextEngine
->GetMaxTextLen() )
1741 void TextView::dragGestureRecognized( const css::datatransfer::dnd::DragGestureEvent
& rDGE
)
1743 if ( !mpImpl
->mbClickedInSelection
)
1746 SolarMutexGuard aVclGuard
;
1748 SAL_WARN_IF( !mpImpl
->maSelection
.HasRange(), "vcl", "TextView::dragGestureRecognized: mpImpl->mbClickedInSelection, but no selection?" );
1750 mpImpl
->mpDDInfo
.reset(new TextDDInfo
);
1751 mpImpl
->mpDDInfo
->mbStarterOfDD
= true;
1753 rtl::Reference
<TETextDataObject
> pDataObj
= new TETextDataObject( GetSelected() );
1755 mpImpl
->mpCursor
->Hide();
1757 sal_Int8 nActions
= css::datatransfer::dnd::DNDConstants::ACTION_COPY
;
1758 if ( !IsReadOnly() )
1759 nActions
|= css::datatransfer::dnd::DNDConstants::ACTION_MOVE
;
1760 rDGE
.DragSource
->startDrag( rDGE
, nActions
, 0 /*cursor*/, 0 /*image*/, pDataObj
, mpImpl
->mxDnDListener
);
1763 void TextView::dragDropEnd( const css::datatransfer::dnd::DragSourceDropEvent
& )
1766 mpImpl
->mpDDInfo
.reset();
1769 void TextView::drop( const css::datatransfer::dnd::DropTargetDropEvent
& rDTDE
)
1771 SolarMutexGuard aVclGuard
;
1773 if ( !mpImpl
->mbReadOnly
&& mpImpl
->mpDDInfo
)
1777 // Data for deleting after DROP_MOVE:
1778 TextSelection
aPrevSel( mpImpl
->maSelection
);
1780 const sal_uInt32 nPrevParaCount
= mpImpl
->mpTextEngine
->GetParagraphCount();
1781 const sal_Int32 nPrevStartParaLen
= mpImpl
->mpTextEngine
->GetTextLen( aPrevSel
.GetStart().GetPara() );
1783 bool bStarterOfDD
= false;
1784 for ( sal_uInt16 nView
= mpImpl
->mpTextEngine
->GetViewCount(); nView
&& !bStarterOfDD
; )
1785 bStarterOfDD
= mpImpl
->mpTextEngine
->GetView( --nView
)->mpImpl
->mpDDInfo
&& mpImpl
->mpTextEngine
->GetView( nView
)->mpImpl
->mpDDInfo
->mbStarterOfDD
;
1788 ImpSetSelection( mpImpl
->mpDDInfo
->maDropPos
);
1790 mpImpl
->mpTextEngine
->UndoActionStart();
1793 css::uno::Reference
< css::datatransfer::XTransferable
> xDataObj
= rDTDE
.Transferable
;
1794 if ( xDataObj
.is() )
1796 css::datatransfer::DataFlavor aFlavor
;
1797 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING
, aFlavor
);
1798 if ( xDataObj
->isDataFlavorSupported( aFlavor
) )
1800 css::uno::Any aData
= xDataObj
->getTransferData( aFlavor
);
1802 aData
>>= aOUString
;
1803 aText
= convertLineEnd(aOUString
, LINEEND_LF
);
1807 if ( !aText
.isEmpty() && ( aText
[ aText
.getLength()-1 ] == LINE_SEP
) )
1808 aText
= aText
.copy(0, aText
.getLength()-1);
1810 if ( ImplCheckTextLen( aText
) )
1811 ImpSetSelection( mpImpl
->mpTextEngine
->ImpInsertText( mpImpl
->mpDDInfo
->maDropPos
, aText
) );
1813 if ( aPrevSel
.HasRange() &&
1814 (( rDTDE
.DropAction
& css::datatransfer::dnd::DNDConstants::ACTION_MOVE
) || !bStarterOfDD
) )
1816 // adjust selection if necessary
1817 if ( ( mpImpl
->mpDDInfo
->maDropPos
.GetPara() < aPrevSel
.GetStart().GetPara() ) ||
1818 ( ( mpImpl
->mpDDInfo
->maDropPos
.GetPara() == aPrevSel
.GetStart().GetPara() )
1819 && ( mpImpl
->mpDDInfo
->maDropPos
.GetIndex() < aPrevSel
.GetStart().GetIndex() ) ) )
1821 const sal_uInt32 nNewParasBeforeSelection
=
1822 mpImpl
->mpTextEngine
->GetParagraphCount() - nPrevParaCount
;
1824 aPrevSel
.GetStart().GetPara() += nNewParasBeforeSelection
;
1825 aPrevSel
.GetEnd().GetPara() += nNewParasBeforeSelection
;
1827 if ( mpImpl
->mpDDInfo
->maDropPos
.GetPara() == aPrevSel
.GetStart().GetPara() )
1829 const sal_Int32 nNewChars
=
1830 mpImpl
->mpTextEngine
->GetTextLen( aPrevSel
.GetStart().GetPara() ) - nPrevStartParaLen
;
1832 aPrevSel
.GetStart().GetIndex() += nNewChars
;
1833 if ( aPrevSel
.GetStart().GetPara() == aPrevSel
.GetEnd().GetPara() )
1834 aPrevSel
.GetEnd().GetIndex() += nNewChars
;
1839 // adjust current selection
1840 TextPaM aPaM
= mpImpl
->maSelection
.GetStart();
1841 aPaM
.GetPara() -= ( aPrevSel
.GetEnd().GetPara() - aPrevSel
.GetStart().GetPara() );
1842 if ( aPrevSel
.GetEnd().GetPara() == mpImpl
->mpDDInfo
->maDropPos
.GetPara() )
1844 aPaM
.GetIndex() -= aPrevSel
.GetEnd().GetIndex();
1845 if ( aPrevSel
.GetStart().GetPara() == mpImpl
->mpDDInfo
->maDropPos
.GetPara() )
1846 aPaM
.GetIndex() += aPrevSel
.GetStart().GetIndex();
1848 ImpSetSelection( aPaM
);
1851 mpImpl
->mpTextEngine
->ImpDeleteText( aPrevSel
);
1854 mpImpl
->mpTextEngine
->UndoActionEnd();
1856 mpImpl
->mpDDInfo
.reset();
1858 mpImpl
->mpTextEngine
->FormatAndUpdate( this );
1860 mpImpl
->mpTextEngine
->Broadcast( TextHint( SfxHintId::TextModified
) );
1862 rDTDE
.Context
->dropComplete( false/*bChanges*/ );
1865 void TextView::dragEnter( const css::datatransfer::dnd::DropTargetDragEnterEvent
& )
1869 void TextView::dragExit( const css::datatransfer::dnd::DropTargetEvent
& )
1871 SolarMutexGuard aVclGuard
;
1875 void TextView::dragOver( const css::datatransfer::dnd::DropTargetDragEvent
& rDTDE
)
1877 SolarMutexGuard aVclGuard
;
1879 if (!mpImpl
->mpDDInfo
)
1880 mpImpl
->mpDDInfo
.reset(new TextDDInfo
);
1882 TextPaM aPrevDropPos
= mpImpl
->mpDDInfo
->maDropPos
;
1883 Point
aMousePos( rDTDE
.LocationX
, rDTDE
.LocationY
);
1884 Point aDocPos
= GetDocPos( aMousePos
);
1885 mpImpl
->mpDDInfo
->maDropPos
= mpImpl
->mpTextEngine
->GetPaM( aDocPos
);
1887 // Don't drop in selection or in read only engine
1888 if ( IsReadOnly() || IsInSelection( mpImpl
->mpDDInfo
->maDropPos
))
1891 rDTDE
.Context
->rejectDrag();
1895 // delete old Cursor
1896 if ( !mpImpl
->mpDDInfo
->mbVisCursor
|| ( aPrevDropPos
!= mpImpl
->mpDDInfo
->maDropPos
) )
1901 rDTDE
.Context
->acceptDrag( rDTDE
.DropAction
);
1905 Point
TextView::ImpGetOutputStartPos( const Point
& rStartDocPos
) const
1907 Point
aStartPos( -rStartDocPos
.X(), -rStartDocPos
.Y() );
1908 if ( mpImpl
->mpTextEngine
->IsRightToLeft() )
1910 Size aSz
= mpImpl
->mpWindow
->GetOutputSizePixel();
1911 aStartPos
.setX( rStartDocPos
.X() + aSz
.Width() - 1 ); // -1: Start is 0
1916 Point
TextView::GetDocPos( const Point
& rWindowPos
) const
1918 // Window Position => Document Position
1922 aPoint
.setY( rWindowPos
.Y() + mpImpl
->maStartDocPos
.Y() );
1924 if ( !mpImpl
->mpTextEngine
->IsRightToLeft() )
1926 aPoint
.setX( rWindowPos
.X() + mpImpl
->maStartDocPos
.X() );
1930 Size aSz
= mpImpl
->mpWindow
->GetOutputSizePixel();
1931 aPoint
.setX( ( aSz
.Width() - 1 ) - rWindowPos
.X() + mpImpl
->maStartDocPos
.X() );
1937 Point
TextView::GetWindowPos( const Point
& rDocPos
) const
1939 // Document Position => Window Position
1943 aPoint
.setY( rDocPos
.Y() - mpImpl
->maStartDocPos
.Y() );
1945 if ( !mpImpl
->mpTextEngine
->IsRightToLeft() )
1947 aPoint
.setX( rDocPos
.X() - mpImpl
->maStartDocPos
.X() );
1951 Size aSz
= mpImpl
->mpWindow
->GetOutputSizePixel();
1952 aPoint
.setX( ( aSz
.Width() - 1 ) - ( rDocPos
.X() - mpImpl
->maStartDocPos
.X() ) );
1958 sal_Int32
TextView::GetLineNumberOfCursorInSelection() const
1961 sal_Int32 nLineNo
= -1;
1962 if( mpImpl
->mbCursorEnabled
)
1964 TextPaM aPaM
= GetSelection().GetEnd();
1965 TEParaPortion
* pPPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( aPaM
.GetPara() );
1966 nLineNo
= pPPortion
->GetLineNumber( aPaM
.GetIndex(), false );
1967 //TODO: std::vector<TextLine>::size_type -> sal_Int32!
1968 if( mpImpl
->mbCursorAtEndOfLine
)
1974 // (+) class TextSelFunctionSet
1976 TextSelFunctionSet::TextSelFunctionSet( TextView
* pView
)
1981 void TextSelFunctionSet::BeginDrag()
1985 void TextSelFunctionSet::CreateAnchor()
1987 // TextSelection aSel( mpView->GetSelection() );
1988 // aSel.GetStart() = aSel.GetEnd();
1989 // mpView->SetSelection( aSel );
1991 // may not be followed by ShowCursor
1992 mpView
->HideSelection();
1993 mpView
->ImpSetSelection( mpView
->mpImpl
->maSelection
.GetEnd() );
1996 void TextSelFunctionSet::SetCursorAtPoint( const Point
& rPointPixel
, bool )
1998 mpView
->SetCursorAtPoint( rPointPixel
);
2001 bool TextSelFunctionSet::IsSelectionAtPoint( const Point
& rPointPixel
)
2003 return mpView
->IsSelectionAtPoint( rPointPixel
);
2006 void TextSelFunctionSet::DeselectAll()
2011 void TextSelFunctionSet::DeselectAtPoint( const Point
& )
2013 // only for multiple selection
2016 void TextSelFunctionSet::DestroyAnchor()
2018 // only for multiple selection
2020 TextEngine
* TextView::GetTextEngine() const
2021 { return mpImpl
->mpTextEngine
; }
2022 vcl::Window
* TextView::GetWindow() const
2023 { return mpImpl
->mpWindow
; }
2024 void TextView::EnableCursor( bool bEnable
)
2025 { mpImpl
->mbCursorEnabled
= bEnable
; }
2026 bool TextView::IsCursorEnabled() const
2027 { return mpImpl
->mbCursorEnabled
; }
2028 void TextView::SetStartDocPos( const Point
& rPos
)
2029 { mpImpl
->maStartDocPos
= rPos
; }
2030 const Point
& TextView::GetStartDocPos() const
2031 { return mpImpl
->maStartDocPos
; }
2032 void TextView::SetAutoIndentMode( bool bAutoIndent
)
2033 { mpImpl
->mbAutoIndent
= bAutoIndent
; }
2034 bool TextView::IsReadOnly() const
2035 { return mpImpl
->mbReadOnly
; }
2036 void TextView::SetAutoScroll( bool bAutoScroll
)
2037 { mpImpl
->mbAutoScroll
= bAutoScroll
; }
2038 bool TextView::IsAutoScroll() const
2039 { return mpImpl
->mbAutoScroll
; }
2040 bool TextView::HasSelection() const
2041 { return mpImpl
->maSelection
.HasRange(); }
2042 bool TextView::IsInsertMode() const
2043 { return mpImpl
->mbInsertMode
; }
2045 void TextView::MatchGroup()
2047 TextSelection
aTmpSel( GetSelection() );
2049 if ( ( aTmpSel
.GetStart().GetPara() != aTmpSel
.GetEnd().GetPara() ) ||
2050 ( ( aTmpSel
.GetEnd().GetIndex() - aTmpSel
.GetStart().GetIndex() ) > 1 ) )
2055 TextSelection aMatchSel
= static_cast<ExtTextEngine
*>(GetTextEngine())->MatchGroup( aTmpSel
.GetStart() );
2056 if ( aMatchSel
.HasRange() )
2057 SetSelection( aMatchSel
);
2060 void TextView::CenterPaM( const TextPaM
& rPaM
)
2062 // Get textview size and the corresponding y-coordinates
2063 Size aOutSz
= mpImpl
->mpWindow
->GetOutputSizePixel();
2064 tools::Long nVisStartY
= mpImpl
->maStartDocPos
.Y();
2065 tools::Long nVisEndY
= mpImpl
->maStartDocPos
.Y() + aOutSz
.Height();
2067 // Retrieve the coordinates of the PaM
2068 tools::Rectangle aRect
= mpImpl
->mpTextEngine
->PaMtoEditCursor(rPaM
);
2070 // Recalculate the offset of the center y-coordinates and scroll
2071 Scroll(0, (nVisStartY
+ nVisEndY
) / 2 - aRect
.TopLeft().getY());
2074 bool TextView::Search( const i18nutil::SearchOptions
& rSearchOptions
, bool bForward
)
2076 bool bFound
= false;
2077 TextSelection
aSel( GetSelection() );
2078 if ( static_cast<ExtTextEngine
*>(GetTextEngine())->Search( aSel
, rSearchOptions
, bForward
) )
2081 // First add the beginning of the word to the selection,
2082 // so that the whole word is in the visible region.
2083 SetSelection( aSel
.GetStart() );
2084 ShowCursor( true, false );
2088 aSel
= GetSelection().GetEnd();
2091 SetSelection( aSel
);
2092 // tdf#49482: Move the start of the selection to the center of the textview
2095 CenterPaM( aSel
.GetStart() );
2102 sal_uInt16
TextView::Replace( const i18nutil::SearchOptions
& rSearchOptions
, bool bAll
, bool bForward
)
2104 sal_uInt16 nFound
= 0;
2108 if ( GetSelection().HasRange() )
2110 InsertText( rSearchOptions
.replaceString
);
2112 Search( rSearchOptions
, bForward
); // right away to the next
2116 if( Search( rSearchOptions
, bForward
) )
2122 // the writer replaces all, from beginning to end
2124 ExtTextEngine
* pTextEngine
= static_cast<ExtTextEngine
*>(GetTextEngine());
2129 bool bSearchInSelection
= (0 != (rSearchOptions
.searchFlag
& css::util::SearchFlags::REG_NOT_BEGINOFLINE
) );
2130 if ( bSearchInSelection
)
2132 aSel
= GetSelection();
2136 TextSelection
aSearchSel( aSel
);
2138 bool bFound
= pTextEngine
->Search( aSel
, rSearchOptions
);
2140 pTextEngine
->UndoActionStart();
2145 TextPaM aNewStart
= pTextEngine
->ImpInsertText( aSel
, rSearchOptions
.replaceString
);
2146 // tdf#64690 - extend selection to include inserted text portions
2147 if ( aSel
.GetEnd().GetPara() == aSearchSel
.GetEnd().GetPara() )
2149 aSearchSel
.GetEnd().GetIndex() += rSearchOptions
.replaceString
.getLength() - 1;
2152 aSel
.GetStart() = aNewStart
;
2153 bFound
= pTextEngine
->Search( aSel
, rSearchOptions
);
2157 SetSelection( aSel
.GetStart() );
2158 pTextEngine
->FormatAndUpdate( this );
2159 pTextEngine
->UndoActionEnd();
2165 bool TextView::ImpIndentBlock( bool bRight
)
2169 TextSelection aSel
= GetSelection();
2173 GetTextEngine()->UndoActionStart();
2175 const sal_uInt32 nStartPara
= aSel
.GetStart().GetPara();
2176 sal_uInt32 nEndPara
= aSel
.GetEnd().GetPara();
2177 if ( aSel
.HasRange() && !aSel
.GetEnd().GetIndex() )
2179 nEndPara
--; // do not indent
2182 for ( sal_uInt32 nPara
= nStartPara
; nPara
<= nEndPara
; ++nPara
)
2187 GetTextEngine()->ImpInsertText( TextPaM( nPara
, 0 ), '\t' );
2192 // remove Tabs/Blanks
2193 OUString aText
= GetTextEngine()->GetText( nPara
);
2194 if ( !aText
.isEmpty() && (
2195 ( aText
[ 0 ] == '\t' ) ||
2196 ( aText
[ 0 ] == ' ' ) ) )
2198 GetTextEngine()->ImpDeleteText( TextSelection( TextPaM( nPara
, 0 ), TextPaM( nPara
, 1 ) ) );
2204 GetTextEngine()->UndoActionEnd();
2206 bool bRange
= aSel
.HasRange();
2209 ++aSel
.GetStart().GetIndex();
2210 if ( bRange
&& ( aSel
.GetEnd().GetPara() == nEndPara
) )
2211 ++aSel
.GetEnd().GetIndex();
2215 if ( aSel
.GetStart().GetIndex() )
2216 --aSel
.GetStart().GetIndex();
2217 if ( bRange
&& aSel
.GetEnd().GetIndex() )
2218 --aSel
.GetEnd().GetIndex();
2221 ImpSetSelection( aSel
);
2222 GetTextEngine()->FormatAndUpdate( this );
2227 bool TextView::IndentBlock()
2229 return ImpIndentBlock( true );
2232 bool TextView::UnindentBlock()
2234 return ImpIndentBlock( false );
2238 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */