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/transfer.hxx>
29 #include <vcl/xtextedt.hxx>
30 #include "textdat2.hxx"
31 #include <vcl/commandevent.hxx>
32 #include <vcl/inputctx.hxx>
34 #include <svl/undo.hxx>
35 #include <vcl/cursor.hxx>
36 #include <vcl/weld.hxx>
37 #include <vcl/window.hxx>
38 #include <vcl/svapp.hxx>
39 #include <tools/stream.hxx>
41 #include <sal/log.hxx>
42 #include <sot/formats.hxx>
44 #include <cppuhelper/weak.hxx>
45 #include <cppuhelper/queryinterface.hxx>
46 #include <com/sun/star/i18n/XBreakIterator.hpp>
47 #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
48 #include <com/sun/star/i18n/WordType.hpp>
49 #include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp>
50 #include <com/sun/star/datatransfer/XTransferable.hpp>
51 #include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
52 #include <com/sun/star/datatransfer/clipboard/XFlushableClipboard.hpp>
53 #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
54 #include <com/sun/star/datatransfer/dnd/XDragGestureRecognizer.hpp>
55 #include <com/sun/star/datatransfer/dnd/XDropTarget.hpp>
56 #include <com/sun/star/util/SearchFlags.hpp>
58 #include <vcl/toolkit/edit.hxx>
60 #include <sot/exchange.hxx>
65 TETextDataObject::TETextDataObject( const OUString
& rText
) : maText( rText
)
69 // css::uno::XInterface
70 css::uno::Any
TETextDataObject::queryInterface( const css::uno::Type
& rType
)
72 css::uno::Any aRet
= ::cppu::queryInterface( rType
, static_cast< css::datatransfer::XTransferable
* >(this) );
73 return (aRet
.hasValue() ? aRet
: OWeakObject::queryInterface( rType
));
76 // css::datatransfer::XTransferable
77 css::uno::Any
TETextDataObject::getTransferData( const css::datatransfer::DataFlavor
& rFlavor
)
81 SotClipboardFormatId nT
= SotExchange::GetFormat( rFlavor
);
82 if ( nT
== SotClipboardFormatId::STRING
)
86 else if ( nT
== SotClipboardFormatId::HTML
)
88 sal_uLong nLen
= GetHTMLStream().TellEnd();
89 GetHTMLStream().Seek(0);
91 css::uno::Sequence
< sal_Int8
> aSeq( nLen
);
92 memcpy( aSeq
.getArray(), GetHTMLStream().GetData(), nLen
);
97 throw css::datatransfer::UnsupportedFlavorException();
102 css::uno::Sequence
< css::datatransfer::DataFlavor
> TETextDataObject::getTransferDataFlavors( )
104 GetHTMLStream().Seek( STREAM_SEEK_TO_END
);
105 bool bHTML
= GetHTMLStream().Tell() > 0;
106 css::uno::Sequence
< css::datatransfer::DataFlavor
> aDataFlavors( bHTML
? 2 : 1 );
107 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING
, aDataFlavors
.getArray()[0] );
109 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::HTML
, aDataFlavors
.getArray()[1] );
113 sal_Bool
TETextDataObject::isDataFlavorSupported( const css::datatransfer::DataFlavor
& rFlavor
)
115 SotClipboardFormatId nT
= SotExchange::GetFormat( rFlavor
);
116 return ( nT
== SotClipboardFormatId::STRING
);
121 ExtTextEngine
* mpTextEngine
;
123 VclPtr
<vcl::Window
> mpWindow
;
124 TextSelection maSelection
;
127 std::unique_ptr
<vcl::Cursor
, o3tl::default_delete
<vcl::Cursor
>> mpCursor
;
129 std::unique_ptr
<TextDDInfo
, o3tl::default_delete
<TextDDInfo
>> mpDDInfo
;
131 std::unique_ptr
<SelectionEngine
> mpSelEngine
;
132 std::unique_ptr
<TextSelFunctionSet
> mpSelFuncSet
;
134 css::uno::Reference
< css::datatransfer::dnd::XDragSourceListener
> mxDnDListener
;
136 sal_uInt16 mnTravelXPos
;
138 bool mbAutoScroll
: 1;
139 bool mbInsertMode
: 1;
141 bool mbPaintSelection
: 1;
142 bool mbAutoIndent
: 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
->mbAutoIndent
= false;
161 mpImpl
->mbCursorEnabled
= true;
162 mpImpl
->mbClickedInSelection
= false;
163 // mbInSelection = false;
165 mpImpl
->mnTravelXPos
= TRAVEL_X_DONTKNOW
;
167 mpImpl
->mpSelFuncSet
= std::make_unique
<TextSelFunctionSet
>( this );
168 mpImpl
->mpSelEngine
= std::make_unique
<SelectionEngine
>( mpImpl
->mpWindow
, mpImpl
->mpSelFuncSet
.get() );
169 mpImpl
->mpSelEngine
->SetSelectionMode( SelectionMode::Range
);
170 mpImpl
->mpSelEngine
->EnableDrag( true );
172 mpImpl
->mpCursor
.reset(new vcl::Cursor
);
173 mpImpl
->mpCursor
->Show();
174 pWindow
->SetCursor( mpImpl
->mpCursor
.get() );
175 pWindow
->SetInputContext( InputContext( pEng
->GetFont(), InputContextFlags::Text
|InputContextFlags::ExtText
) );
177 pWindow
->GetOutDev()->SetLineColor();
179 if ( pWindow
->GetDragGestureRecognizer().is() )
181 mpImpl
->mxDnDListener
= new vcl::unohelper::DragAndDropWrapper( this );
183 css::uno::Reference
< css::datatransfer::dnd::XDragGestureListener
> xDGL( mpImpl
->mxDnDListener
, css::uno::UNO_QUERY
);
184 pWindow
->GetDragGestureRecognizer()->addDragGestureListener( xDGL
);
185 css::uno::Reference
< css::datatransfer::dnd::XDropTargetListener
> xDTL( xDGL
, css::uno::UNO_QUERY
);
186 pWindow
->GetDropTarget()->addDropTargetListener( xDTL
);
187 pWindow
->GetDropTarget()->setActive( true );
188 pWindow
->GetDropTarget()->setDefaultActions( css::datatransfer::dnd::DNDConstants::ACTION_COPY_OR_MOVE
);
192 TextView::~TextView()
194 mpImpl
->mpSelEngine
.reset();
195 mpImpl
->mpSelFuncSet
.reset();
197 if ( mpImpl
->mpWindow
->GetCursor() == mpImpl
->mpCursor
.get() )
198 mpImpl
->mpWindow
->SetCursor( nullptr );
200 mpImpl
->mpCursor
.reset();
201 mpImpl
->mpDDInfo
.reset();
204 void TextView::Invalidate()
206 mpImpl
->mpWindow
->Invalidate();
209 void TextView::SetSelection( const TextSelection
& rTextSel
, bool bGotoCursor
)
211 // if someone left an empty attribute and then the Outliner manipulated the selection
212 if ( !mpImpl
->maSelection
.HasRange() )
213 mpImpl
->mpTextEngine
->CursorMoved( mpImpl
->maSelection
.GetStart().GetPara() );
215 // if the selection is manipulated after a KeyInput
216 mpImpl
->mpTextEngine
->CheckIdleFormatter();
219 TextSelection
aNewSel( rTextSel
);
220 mpImpl
->mpTextEngine
->ValidateSelection( aNewSel
);
221 ImpSetSelection( aNewSel
);
223 ShowCursor( bGotoCursor
);
226 void TextView::SetSelection( const TextSelection
& rTextSel
)
228 SetSelection( rTextSel
, mpImpl
->mbAutoScroll
);
231 const TextSelection
& TextView::GetSelection() const
233 return mpImpl
->maSelection
;
235 TextSelection
& TextView::GetSelection()
237 return mpImpl
->maSelection
;
240 void TextView::DeleteSelected()
244 mpImpl
->mpTextEngine
->UndoActionStart();
245 TextPaM aPaM
= mpImpl
->mpTextEngine
->ImpDeleteText( mpImpl
->maSelection
);
246 mpImpl
->mpTextEngine
->UndoActionEnd();
248 ImpSetSelection( aPaM
);
249 mpImpl
->mpTextEngine
->FormatAndUpdate( this );
253 void TextView::ImpPaint(vcl::RenderContext
& rRenderContext
, const Point
& rStartPos
, tools::Rectangle
const* pPaintArea
, TextSelection
const* pSelection
)
255 if (!mpImpl
->mbPaintSelection
)
257 pSelection
= nullptr;
261 // set correct background color;
262 // unfortunately we cannot detect if it has changed
263 vcl::Font aFont
= mpImpl
->mpTextEngine
->GetFont();
264 Color aColor
= rRenderContext
.GetBackground().GetColor();
265 aColor
.SetAlpha(255);
266 if (aColor
!= aFont
.GetFillColor())
268 if (aFont
.IsTransparent())
269 aColor
= COL_TRANSPARENT
;
270 aFont
.SetFillColor(aColor
);
271 mpImpl
->mpTextEngine
->maFont
= aFont
;
275 mpImpl
->mpTextEngine
->ImpPaint(&rRenderContext
, rStartPos
, pPaintArea
, pSelection
);
278 void TextView::Paint(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& rRect
)
280 ImpPaint(rRenderContext
, rRect
);
283 void TextView::ImpPaint(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& rRect
)
285 if ( !mpImpl
->mpTextEngine
->GetUpdateMode() || mpImpl
->mpTextEngine
->IsInUndo() )
288 TextSelection
*pDrawSelection
= nullptr;
289 if (mpImpl
->maSelection
.HasRange())
290 pDrawSelection
= &mpImpl
->maSelection
;
292 Point aStartPos
= ImpGetOutputStartPos(mpImpl
->maStartDocPos
);
293 ImpPaint(rRenderContext
, aStartPos
, &rRect
, pDrawSelection
);
296 void TextView::ImpSetSelection( const TextSelection
& rSelection
)
298 if (rSelection
== mpImpl
->maSelection
)
301 bool bCaret
= false, bSelection
= false;
302 const TextPaM
&rEnd
= rSelection
.GetEnd();
303 const TextPaM
&rOldEnd
= mpImpl
->maSelection
.GetEnd();
304 bool bGap
= rSelection
.HasRange(), bOldGap
= mpImpl
->maSelection
.HasRange();
310 mpImpl
->maSelection
= rSelection
;
313 mpImpl
->mpTextEngine
->Broadcast(TextHint(SfxHintId::TextViewSelectionChanged
));
316 mpImpl
->mpTextEngine
->Broadcast(TextHint(SfxHintId::TextViewCaretChanged
));
319 void TextView::ShowSelection()
321 ImpShowHideSelection();
324 void TextView::HideSelection()
326 ImpShowHideSelection();
329 void TextView::ShowSelection( const TextSelection
& rRange
)
331 ImpShowHideSelection( &rRange
);
334 void TextView::ImpShowHideSelection(const TextSelection
* pRange
)
336 const TextSelection
* pRangeOrSelection
= pRange
? pRange
: &mpImpl
->maSelection
;
338 if ( !pRangeOrSelection
->HasRange() )
341 if( mpImpl
->mpWindow
->IsPaintTransparent() )
342 mpImpl
->mpWindow
->Invalidate();
345 TextSelection
aRange( *pRangeOrSelection
);
347 bool bVisCursor
= mpImpl
->mpCursor
->IsVisible();
348 mpImpl
->mpCursor
->Hide();
351 mpImpl
->mpCursor
->Show();
355 bool TextView::KeyInput( const KeyEvent
& rKeyEvent
)
358 bool bModified
= false;
360 bool bEndKey
= false; // special CursorPosition
361 bool bAllowIdle
= true;
364 // the local bModified is not set e.g. by Cut/Paste, as here
365 // the update happens somewhere else
366 bool bWasModified
= mpImpl
->mpTextEngine
->IsModified();
367 mpImpl
->mpTextEngine
->SetModified( false );
369 TextSelection
aCurSel( mpImpl
->maSelection
);
370 TextSelection
aOldSel( aCurSel
);
372 sal_uInt16 nCode
= rKeyEvent
.GetKeyCode().GetCode();
373 KeyFuncType eFunc
= rKeyEvent
.GetKeyCode().GetFunction();
374 if ( eFunc
!= KeyFuncType::DONTKNOW
)
378 case KeyFuncType::CUT
:
380 if ( !mpImpl
->mbReadOnly
)
384 case KeyFuncType::COPY
:
389 case KeyFuncType::PASTE
:
391 if ( !mpImpl
->mbReadOnly
)
395 case KeyFuncType::UNDO
:
397 if ( !mpImpl
->mbReadOnly
)
401 case KeyFuncType::REDO
:
403 if ( !mpImpl
->mbReadOnly
)
408 default: // might get processed below
409 eFunc
= KeyFuncType::DONTKNOW
;
412 if ( eFunc
== KeyFuncType::DONTKNOW
)
424 case css::awt::Key::MOVE_WORD_FORWARD
:
425 case css::awt::Key::SELECT_WORD_FORWARD
:
426 case css::awt::Key::MOVE_WORD_BACKWARD
:
427 case css::awt::Key::SELECT_WORD_BACKWARD
:
428 case css::awt::Key::MOVE_TO_BEGIN_OF_LINE
:
429 case css::awt::Key::MOVE_TO_END_OF_LINE
:
430 case css::awt::Key::SELECT_TO_BEGIN_OF_LINE
:
431 case css::awt::Key::SELECT_TO_END_OF_LINE
:
432 case css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH
:
433 case css::awt::Key::MOVE_TO_END_OF_PARAGRAPH
:
434 case css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH
:
435 case css::awt::Key::SELECT_TO_END_OF_PARAGRAPH
:
436 case css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT
:
437 case css::awt::Key::MOVE_TO_END_OF_DOCUMENT
:
438 case css::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT
:
439 case css::awt::Key::SELECT_TO_END_OF_DOCUMENT
:
441 if ( ( !rKeyEvent
.GetKeyCode().IsMod2() || ( nCode
== KEY_LEFT
) || ( nCode
== KEY_RIGHT
) )
442 && !( rKeyEvent
.GetKeyCode().IsMod1() && ( nCode
== KEY_PAGEUP
|| nCode
== KEY_PAGEDOWN
) ) )
444 aCurSel
= ImpMoveCursor( rKeyEvent
);
445 if ( aCurSel
.HasRange() ) {
446 css::uno::Reference
<css::datatransfer::clipboard::XClipboard
> aSelection(GetSystemPrimarySelection());
450 if ( nCode
== KEY_END
)
459 case css::awt::Key::DELETE_WORD_BACKWARD
:
460 case css::awt::Key::DELETE_WORD_FORWARD
:
461 case css::awt::Key::DELETE_TO_BEGIN_OF_LINE
:
462 case css::awt::Key::DELETE_TO_END_OF_LINE
:
464 if ( !mpImpl
->mbReadOnly
&& !rKeyEvent
.GetKeyCode().IsMod2() )
466 sal_uInt8 nDel
= ( nCode
== KEY_DELETE
) ? DEL_RIGHT
: DEL_LEFT
;
467 sal_uInt8 nMode
= rKeyEvent
.GetKeyCode().IsMod1() ? DELMODE_RESTOFWORD
: DELMODE_SIMPLE
;
468 if ( ( nMode
== DELMODE_RESTOFWORD
) && rKeyEvent
.GetKeyCode().IsShift() )
469 nMode
= DELMODE_RESTOFCONTENT
;
473 case css::awt::Key::DELETE_WORD_BACKWARD
:
475 nMode
= DELMODE_RESTOFWORD
;
477 case css::awt::Key::DELETE_WORD_FORWARD
:
479 nMode
= DELMODE_RESTOFWORD
;
481 case css::awt::Key::DELETE_TO_BEGIN_OF_LINE
:
483 nMode
= DELMODE_RESTOFCONTENT
;
485 case css::awt::Key::DELETE_TO_END_OF_LINE
:
487 nMode
= DELMODE_RESTOFCONTENT
;
492 mpImpl
->mpTextEngine
->UndoActionStart();
493 aCurSel
= ImpDelete( nDel
, nMode
);
494 mpImpl
->mpTextEngine
->UndoActionEnd();
504 if ( !mpImpl
->mbReadOnly
&& !rKeyEvent
.GetKeyCode().IsShift() &&
505 !rKeyEvent
.GetKeyCode().IsMod1() && !rKeyEvent
.GetKeyCode().IsMod2() &&
506 ImplCheckTextLen( OUString('x') ) )
508 aCurSel
= mpImpl
->mpTextEngine
->ImpInsertText( aCurSel
, '\t', !IsInsertMode() );
517 // do not swallow Shift-RETURN, as this would disable multi-line entries
518 // in dialogs & property editors
519 if ( !mpImpl
->mbReadOnly
&& !rKeyEvent
.GetKeyCode().IsMod1() &&
520 !rKeyEvent
.GetKeyCode().IsMod2() && ImplCheckTextLen( OUString('x') ) )
522 mpImpl
->mpTextEngine
->UndoActionStart();
523 aCurSel
= mpImpl
->mpTextEngine
->ImpInsertParaBreak( aCurSel
);
524 if ( mpImpl
->mbAutoIndent
)
526 TextNode
* pPrev
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aCurSel
.GetEnd().GetPara() - 1 ].get();
528 while ( ( n
< pPrev
->GetText().getLength() ) && (
529 ( pPrev
->GetText()[ n
] == ' ' ) ||
530 ( pPrev
->GetText()[ n
] == '\t' ) ) )
535 aCurSel
= mpImpl
->mpTextEngine
->ImpInsertText( aCurSel
, pPrev
->GetText().copy( 0, n
) );
537 mpImpl
->mpTextEngine
->UndoActionEnd();
546 if ( !mpImpl
->mbReadOnly
)
547 SetInsertMode( !IsInsertMode() );
552 if ( TextEngine::IsSimpleCharInput( rKeyEvent
) )
554 sal_Unicode nCharCode
= rKeyEvent
.GetCharCode();
555 if ( !mpImpl
->mbReadOnly
&& ImplCheckTextLen( OUString(nCharCode
) ) ) // otherwise swallow the character anyway
557 aCurSel
= mpImpl
->mpTextEngine
->ImpInsertText( nCharCode
, aCurSel
, !IsInsertMode(), true );
567 if ( aCurSel
!= aOldSel
) // Check if changed, maybe other method already changed mpImpl->maSelection, don't overwrite that!
568 ImpSetSelection( aCurSel
);
570 if ( ( nCode
!= KEY_UP
) && ( nCode
!= KEY_DOWN
) )
571 mpImpl
->mnTravelXPos
= TRAVEL_X_DONTKNOW
;
575 // Idle-Formatter only if AnyInput
576 if ( bAllowIdle
&& Application::AnyInput( VclInputFlags::KEYBOARD
) )
577 mpImpl
->mpTextEngine
->IdleFormatAndUpdate( this );
579 mpImpl
->mpTextEngine
->FormatAndUpdate( this);
583 // selection is painted now in ImpMoveCursor
584 ImpShowCursor( mpImpl
->mbAutoScroll
, true, bEndKey
);
587 if ( mpImpl
->mpTextEngine
->IsModified() )
588 mpImpl
->mpTextEngine
->Broadcast( TextHint( SfxHintId::TextModified
) );
589 else if ( bWasModified
)
590 mpImpl
->mpTextEngine
->SetModified( true );
595 void TextView::MouseButtonUp( const MouseEvent
& rMouseEvent
)
597 mpImpl
->mbClickedInSelection
= false;
598 mpImpl
->mnTravelXPos
= TRAVEL_X_DONTKNOW
;
599 mpImpl
->mpSelEngine
->SelMouseButtonUp( rMouseEvent
);
600 if ( rMouseEvent
.IsMiddle() && !IsReadOnly() &&
601 ( GetWindow()->GetSettings().GetMouseSettings().GetMiddleButtonAction() == MouseMiddleButtonAction::PasteSelection
) )
603 css::uno::Reference
<css::datatransfer::clipboard::XClipboard
> aSelection(GetSystemPrimarySelection());
605 if ( mpImpl
->mpTextEngine
->IsModified() )
606 mpImpl
->mpTextEngine
->Broadcast( TextHint( SfxHintId::TextModified
) );
608 else if ( rMouseEvent
.IsLeft() && GetSelection().HasRange() )
610 css::uno::Reference
<css::datatransfer::clipboard::XClipboard
> aSelection(GetSystemPrimarySelection());
615 void TextView::MouseButtonDown( const MouseEvent
& rMouseEvent
)
617 mpImpl
->mpTextEngine
->CheckIdleFormatter(); // for fast typing and MouseButtonDown
618 mpImpl
->mnTravelXPos
= TRAVEL_X_DONTKNOW
;
619 mpImpl
->mbClickedInSelection
= IsSelectionAtPoint( rMouseEvent
.GetPosPixel() );
621 mpImpl
->mpTextEngine
->SetActiveView( this );
623 mpImpl
->mpSelEngine
->SelMouseButtonDown( rMouseEvent
);
625 // mbu 20.01.2005 - SelMouseButtonDown() possibly triggers a 'selection changed'
626 // notification. The appropriate handler could change the current selection,
627 // which is the case in the MailMerge address block control. To enable select'n'drag
628 // we need to reevaluate the selection after the notification has been fired.
629 mpImpl
->mbClickedInSelection
= IsSelectionAtPoint( rMouseEvent
.GetPosPixel() );
632 if ( rMouseEvent
.IsShift() || ( rMouseEvent
.GetClicks() < 2 ))
635 if ( rMouseEvent
.IsMod2() )
638 ImpSetSelection( mpImpl
->maSelection
.GetEnd() );
639 SetCursorAtPoint( rMouseEvent
.GetPosPixel() ); // not set by SelectionEngine for MOD2
642 if ( rMouseEvent
.GetClicks() == 2 )
645 if ( mpImpl
->maSelection
.GetEnd().GetIndex() < mpImpl
->mpTextEngine
->GetTextLen( mpImpl
->maSelection
.GetEnd().GetPara() ) )
648 // tdf#57879 - expand selection to include connector punctuations
649 TextSelection aNewSel
;
650 mpImpl
->mpTextEngine
->GetWord( mpImpl
->maSelection
.GetEnd(), &aNewSel
.GetStart(), &aNewSel
.GetEnd() );
651 ImpSetSelection( aNewSel
);
656 else if ( rMouseEvent
.GetClicks() == 3 )
659 if ( mpImpl
->maSelection
.GetStart().GetIndex() || ( mpImpl
->maSelection
.GetEnd().GetIndex() < mpImpl
->mpTextEngine
->GetTextLen( mpImpl
->maSelection
.GetEnd().GetPara() ) ) )
662 TextSelection
aNewSel( mpImpl
->maSelection
);
663 aNewSel
.GetStart().GetIndex() = 0;
664 aNewSel
.GetEnd().GetIndex() = mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ mpImpl
->maSelection
.GetEnd().GetPara() ]->GetText().getLength();
665 ImpSetSelection( aNewSel
);
672 void TextView::MouseMove( const MouseEvent
& rMouseEvent
)
674 mpImpl
->mnTravelXPos
= TRAVEL_X_DONTKNOW
;
675 mpImpl
->mpSelEngine
->SelMouseMove( rMouseEvent
);
678 void TextView::Command( const CommandEvent
& rCEvt
)
680 mpImpl
->mpTextEngine
->CheckIdleFormatter(); // for fast typing and MouseButtonDown
681 mpImpl
->mpTextEngine
->SetActiveView( this );
683 if ( rCEvt
.GetCommand() == CommandEventId::StartExtTextInput
)
686 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ GetSelection().GetEnd().GetPara() ].get();
687 mpImpl
->mpTextEngine
->mpIMEInfos
= std::make_unique
<TEIMEInfos
>( GetSelection().GetEnd(), pNode
->GetText().copy( GetSelection().GetEnd().GetIndex() ) );
688 mpImpl
->mpTextEngine
->mpIMEInfos
->bWasCursorOverwrite
= !IsInsertMode();
690 else if ( rCEvt
.GetCommand() == CommandEventId::EndExtTextInput
)
692 SAL_WARN_IF( !mpImpl
->mpTextEngine
->mpIMEInfos
, "vcl", "CommandEventId::EndExtTextInput => No Start ?" );
693 if( mpImpl
->mpTextEngine
->mpIMEInfos
)
695 TEParaPortion
* pPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
.GetPara() );
696 pPortion
->MarkSelectionInvalid( mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
.GetIndex() );
698 bool bInsertMode
= !mpImpl
->mpTextEngine
->mpIMEInfos
->bWasCursorOverwrite
;
700 mpImpl
->mpTextEngine
->mpIMEInfos
.reset();
702 mpImpl
->mpTextEngine
->TextModified();
703 mpImpl
->mpTextEngine
->FormatAndUpdate( this );
705 SetInsertMode( bInsertMode
);
707 if ( mpImpl
->mpTextEngine
->IsModified() )
708 mpImpl
->mpTextEngine
->Broadcast( TextHint( SfxHintId::TextModified
) );
711 else if ( rCEvt
.GetCommand() == CommandEventId::ExtTextInput
)
713 SAL_WARN_IF( !mpImpl
->mpTextEngine
->mpIMEInfos
, "vcl", "CommandEventId::ExtTextInput => No Start ?" );
714 if( mpImpl
->mpTextEngine
->mpIMEInfos
)
716 const CommandExtTextInputData
* pData
= rCEvt
.GetExtTextInputData();
718 if ( !pData
->IsOnlyCursorChanged() )
720 TextSelection
aSelect( mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
);
721 aSelect
.GetEnd().GetIndex() += mpImpl
->mpTextEngine
->mpIMEInfos
->nLen
;
722 aSelect
= mpImpl
->mpTextEngine
->ImpDeleteText( aSelect
);
723 aSelect
= mpImpl
->mpTextEngine
->ImpInsertText( aSelect
, pData
->GetText() );
725 if ( mpImpl
->mpTextEngine
->mpIMEInfos
->bWasCursorOverwrite
)
727 const sal_Int32 nOldIMETextLen
= mpImpl
->mpTextEngine
->mpIMEInfos
->nLen
;
728 const sal_Int32 nNewIMETextLen
= pData
->GetText().getLength();
730 if ( ( nOldIMETextLen
> nNewIMETextLen
) &&
731 ( nNewIMETextLen
< mpImpl
->mpTextEngine
->mpIMEInfos
->aOldTextAfterStartPos
.getLength() ) )
733 // restore old characters
734 sal_Int32 nRestore
= nOldIMETextLen
- nNewIMETextLen
;
735 TextPaM
aPaM( mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
);
736 aPaM
.GetIndex() += nNewIMETextLen
;
737 mpImpl
->mpTextEngine
->ImpInsertText( aPaM
, mpImpl
->mpTextEngine
->mpIMEInfos
->aOldTextAfterStartPos
.copy( nNewIMETextLen
, nRestore
) );
739 else if ( ( nOldIMETextLen
< nNewIMETextLen
) &&
740 ( nOldIMETextLen
< mpImpl
->mpTextEngine
->mpIMEInfos
->aOldTextAfterStartPos
.getLength() ) )
743 const sal_Int32 nOverwrite
= std::min( nNewIMETextLen
, mpImpl
->mpTextEngine
->mpIMEInfos
->aOldTextAfterStartPos
.getLength() ) - nOldIMETextLen
;
744 SAL_WARN_IF( !nOverwrite
|| (nOverwrite
>= 0xFF00), "vcl", "IME Overwrite?!" );
745 TextPaM
aPaM( mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
);
746 aPaM
.GetIndex() += nNewIMETextLen
;
747 TextSelection
aSel( aPaM
);
748 aSel
.GetEnd().GetIndex() += nOverwrite
;
749 mpImpl
->mpTextEngine
->ImpDeleteText( aSel
);
753 if ( pData
->GetTextAttr() )
755 mpImpl
->mpTextEngine
->mpIMEInfos
->CopyAttribs( pData
->GetTextAttr(), pData
->GetText().getLength() );
759 mpImpl
->mpTextEngine
->mpIMEInfos
->DestroyAttribs();
762 TEParaPortion
* pPPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
.GetPara() );
763 pPPortion
->MarkSelectionInvalid( mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
.GetIndex() );
764 mpImpl
->mpTextEngine
->FormatAndUpdate( this );
767 TextSelection aNewSel
= TextPaM( mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
.GetPara(), mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
.GetIndex()+pData
->GetCursorPos() );
768 SetSelection( aNewSel
);
769 SetInsertMode( !pData
->IsCursorOverwrite() );
771 if ( pData
->IsCursorVisible() )
777 else if ( rCEvt
.GetCommand() == CommandEventId::CursorPos
)
779 if ( mpImpl
->mpTextEngine
->mpIMEInfos
&& mpImpl
->mpTextEngine
->mpIMEInfos
->nLen
)
781 TextPaM
aPaM( GetSelection().GetEnd() );
782 tools::Rectangle aR1
= mpImpl
->mpTextEngine
->PaMtoEditCursor( aPaM
);
784 sal_Int32 nInputEnd
= mpImpl
->mpTextEngine
->mpIMEInfos
->aPos
.GetIndex() + mpImpl
->mpTextEngine
->mpIMEInfos
->nLen
;
786 if ( !mpImpl
->mpTextEngine
->IsFormatted() )
787 mpImpl
->mpTextEngine
->FormatDoc();
789 TEParaPortion
* pParaPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( aPaM
.GetPara() );
790 std::vector
<TextLine
>::size_type nLine
= pParaPortion
->GetLineNumber( aPaM
.GetIndex(), true );
791 TextLine
& rLine
= pParaPortion
->GetLines()[ nLine
];
792 if ( nInputEnd
> rLine
.GetEnd() )
793 nInputEnd
= rLine
.GetEnd();
794 tools::Rectangle aR2
= mpImpl
->mpTextEngine
->PaMtoEditCursor( TextPaM( aPaM
.GetPara(), nInputEnd
) );
796 tools::Long nWidth
= aR2
.Left()-aR1
.Right();
797 aR1
.Move( -GetStartDocPos().X(), -GetStartDocPos().Y() );
798 GetWindow()->SetCursorRect( &aR1
, nWidth
);
802 GetWindow()->SetCursorRect();
807 mpImpl
->mpSelEngine
->Command( rCEvt
);
811 void TextView::ShowCursor( bool bGotoCursor
, bool bForceVisCursor
)
813 // this setting has more weight
814 if ( !mpImpl
->mbAutoScroll
)
816 ImpShowCursor( bGotoCursor
, bForceVisCursor
, false );
819 void TextView::HideCursor()
821 mpImpl
->mpCursor
->Hide();
824 void TextView::Scroll( tools::Long ndX
, tools::Long ndY
)
826 SAL_WARN_IF( !mpImpl
->mpTextEngine
->IsFormatted(), "vcl", "Scroll: Not formatted!" );
831 Point
aNewStartPos( mpImpl
->maStartDocPos
);
834 aNewStartPos
.AdjustY( -ndY
);
835 if ( aNewStartPos
.Y() < 0 )
836 aNewStartPos
.setY( 0 );
839 aNewStartPos
.AdjustX( -ndX
);
840 if ( aNewStartPos
.X() < 0 )
841 aNewStartPos
.setX( 0 );
843 tools::Long nDiffX
= mpImpl
->maStartDocPos
.X() - aNewStartPos
.X();
844 tools::Long nDiffY
= mpImpl
->maStartDocPos
.Y() - aNewStartPos
.Y();
846 if ( nDiffX
|| nDiffY
)
848 bool bVisCursor
= mpImpl
->mpCursor
->IsVisible();
849 mpImpl
->mpCursor
->Hide();
850 mpImpl
->mpWindow
->PaintImmediately();
851 mpImpl
->maStartDocPos
= aNewStartPos
;
853 if ( mpImpl
->mpTextEngine
->IsRightToLeft() )
855 mpImpl
->mpWindow
->Scroll( nDiffX
, nDiffY
);
856 mpImpl
->mpWindow
->PaintImmediately();
857 mpImpl
->mpCursor
->SetPos( mpImpl
->mpCursor
->GetPos() + Point( nDiffX
, nDiffY
) );
858 if ( bVisCursor
&& !mpImpl
->mbReadOnly
)
859 mpImpl
->mpCursor
->Show();
862 mpImpl
->mpTextEngine
->Broadcast( TextHint( SfxHintId::TextViewScrolled
) );
865 void TextView::Undo()
867 mpImpl
->mpTextEngine
->SetActiveView( this );
868 mpImpl
->mpTextEngine
->GetUndoManager().Undo();
871 void TextView::Redo()
873 mpImpl
->mpTextEngine
->SetActiveView( this );
874 mpImpl
->mpTextEngine
->GetUndoManager().Redo();
879 mpImpl
->mpTextEngine
->UndoActionStart();
882 mpImpl
->mpTextEngine
->UndoActionEnd();
885 void TextView::Copy( css::uno::Reference
< css::datatransfer::clipboard::XClipboard
> const & rxClipboard
)
887 if ( !rxClipboard
.is() )
890 rtl::Reference
<TETextDataObject
> pDataObj
= new TETextDataObject( GetSelected() );
892 SolarMutexReleaser aReleaser
;
896 rxClipboard
->setContents( pDataObj
, nullptr );
898 css::uno::Reference
< css::datatransfer::clipboard::XFlushableClipboard
> xFlushableClipboard( rxClipboard
, css::uno::UNO_QUERY
);
899 if( xFlushableClipboard
.is() )
900 xFlushableClipboard
->flushClipboard();
902 catch( const css::uno::Exception
& )
907 void TextView::Copy()
909 css::uno::Reference
<css::datatransfer::clipboard::XClipboard
> aClipboard(GetWindow()->GetClipboard());
913 void TextView::Paste( css::uno::Reference
< css::datatransfer::clipboard::XClipboard
> const & rxClipboard
)
915 if ( !rxClipboard
.is() )
918 css::uno::Reference
< css::datatransfer::XTransferable
> xDataObj
;
922 SolarMutexReleaser aReleaser
;
923 xDataObj
= rxClipboard
->getContents();
925 catch( const css::uno::Exception
& )
929 if ( !xDataObj
.is() )
932 css::datatransfer::DataFlavor aFlavor
;
933 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING
, aFlavor
);
934 if ( !xDataObj
->isDataFlavorSupported( aFlavor
) )
939 css::uno::Any aData
= xDataObj
->getTransferData( aFlavor
);
942 bool bWasTruncated
= false;
943 if( mpImpl
->mpTextEngine
->GetMaxTextLen() != 0 )
944 bWasTruncated
= ImplTruncateNewText( aText
);
946 mpImpl
->mpTextEngine
->Broadcast( TextHint( SfxHintId::TextModified
) );
949 Edit::ShowTruncationWarning(mpImpl
->mpWindow
->GetFrameWeld());
951 catch( const css::datatransfer::UnsupportedFlavorException
& )
956 void TextView::Paste()
958 css::uno::Reference
<css::datatransfer::clipboard::XClipboard
> aClipboard(GetWindow()->GetClipboard());
962 OUString
TextView::GetSelected()
964 return GetSelected( GetSystemLineEnd() );
967 OUString
TextView::GetSelected( LineEnd aSeparator
)
969 return mpImpl
->mpTextEngine
->GetText( mpImpl
->maSelection
, aSeparator
);
972 void TextView::SetInsertMode( bool bInsert
)
974 if ( mpImpl
->mbInsertMode
!= bInsert
)
976 mpImpl
->mbInsertMode
= bInsert
;
977 ShowCursor( mpImpl
->mbAutoScroll
, false );
981 void TextView::SetReadOnly( bool bReadOnly
)
983 if ( mpImpl
->mbReadOnly
!= bReadOnly
)
985 mpImpl
->mbReadOnly
= bReadOnly
;
986 if ( !mpImpl
->mbReadOnly
)
987 ShowCursor( mpImpl
->mbAutoScroll
, false );
991 GetWindow()->SetInputContext( InputContext( mpImpl
->mpTextEngine
->GetFont(), bReadOnly
? InputContextFlags::Text
|InputContextFlags::ExtText
: InputContextFlags::NONE
) );
995 TextSelection
const & TextView::ImpMoveCursor( const KeyEvent
& rKeyEvent
)
997 // normally only needed for Up/Down; but who cares
998 mpImpl
->mpTextEngine
->CheckIdleFormatter();
1000 TextPaM
aPaM( mpImpl
->maSelection
.GetEnd() );
1001 TextPaM
aOldEnd( aPaM
);
1003 TextDirectionality eTextDirection
= TextDirectionality::LeftToRight_TopToBottom
;
1004 if ( mpImpl
->mpTextEngine
->IsRightToLeft() )
1005 eTextDirection
= TextDirectionality::RightToLeft_TopToBottom
;
1007 KeyEvent aTranslatedKeyEvent
= rKeyEvent
.LogicalTextDirectionality( eTextDirection
);
1009 bool bCtrl
= aTranslatedKeyEvent
.GetKeyCode().IsMod1();
1010 sal_uInt16 nCode
= aTranslatedKeyEvent
.GetKeyCode().GetCode();
1012 bool bSelect
= aTranslatedKeyEvent
.GetKeyCode().IsShift();
1015 case KEY_UP
: aPaM
= CursorUp( aPaM
);
1017 case KEY_DOWN
: aPaM
= CursorDown( aPaM
);
1019 case KEY_HOME
: aPaM
= bCtrl
? CursorStartOfDoc() : CursorStartOfLine( aPaM
);
1021 case KEY_END
: aPaM
= bCtrl
? CursorEndOfDoc() : CursorEndOfLine( aPaM
);
1023 case KEY_PAGEUP
: aPaM
= bCtrl
? CursorStartOfDoc() : PageUp( aPaM
);
1025 case KEY_PAGEDOWN
: aPaM
= bCtrl
? CursorEndOfDoc() : PageDown( aPaM
);
1027 case KEY_LEFT
: aPaM
= bCtrl
? CursorWordLeft( aPaM
) : CursorLeft( aPaM
, aTranslatedKeyEvent
.GetKeyCode().IsMod2() ? sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCHARACTER
) : sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCELL
) );
1029 case KEY_RIGHT
: aPaM
= bCtrl
? CursorWordRight( aPaM
) : CursorRight( aPaM
, aTranslatedKeyEvent
.GetKeyCode().IsMod2() ? sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCHARACTER
) : sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCELL
) );
1031 case css::awt::Key::SELECT_WORD_FORWARD
:
1034 case css::awt::Key::MOVE_WORD_FORWARD
:
1035 aPaM
= CursorWordRight( aPaM
);
1037 case css::awt::Key::SELECT_WORD_BACKWARD
:
1040 case css::awt::Key::MOVE_WORD_BACKWARD
:
1041 aPaM
= CursorWordLeft( aPaM
);
1043 case css::awt::Key::SELECT_TO_BEGIN_OF_LINE
:
1046 case css::awt::Key::MOVE_TO_BEGIN_OF_LINE
:
1047 aPaM
= CursorStartOfLine( aPaM
);
1049 case css::awt::Key::SELECT_TO_END_OF_LINE
:
1052 case css::awt::Key::MOVE_TO_END_OF_LINE
:
1053 aPaM
= CursorEndOfLine( aPaM
);
1055 case css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH
:
1058 case css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH
:
1059 aPaM
= CursorStartOfParagraph( aPaM
);
1061 case css::awt::Key::SELECT_TO_END_OF_PARAGRAPH
:
1064 case css::awt::Key::MOVE_TO_END_OF_PARAGRAPH
:
1065 aPaM
= CursorEndOfParagraph( aPaM
);
1067 case css::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT
:
1070 case css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT
:
1071 aPaM
= CursorStartOfDoc();
1073 case css::awt::Key::SELECT_TO_END_OF_DOCUMENT
:
1076 case css::awt::Key::MOVE_TO_END_OF_DOCUMENT
:
1077 aPaM
= CursorEndOfDoc();
1081 // might cause a CreateAnchor or Deselection all
1082 mpImpl
->mpSelEngine
->CursorPosChanging( bSelect
, aTranslatedKeyEvent
.GetKeyCode().IsMod1() );
1084 if ( aOldEnd
!= aPaM
)
1086 mpImpl
->mpTextEngine
->CursorMoved( aOldEnd
.GetPara() );
1088 TextSelection
aNewSelection( mpImpl
->maSelection
);
1089 aNewSelection
.GetEnd() = aPaM
;
1092 // extend the selection
1093 ImpSetSelection( aNewSelection
);
1094 ShowSelection( TextSelection( aOldEnd
, aPaM
) );
1098 aNewSelection
.GetStart() = aPaM
;
1099 ImpSetSelection( aNewSelection
);
1103 return mpImpl
->maSelection
;
1106 void TextView::InsertText( const OUString
& rStr
)
1108 mpImpl
->mpTextEngine
->UndoActionStart();
1110 TextSelection aNewSel
= mpImpl
->mpTextEngine
->ImpInsertText( mpImpl
->maSelection
, rStr
);
1112 ImpSetSelection( aNewSel
);
1114 mpImpl
->mpTextEngine
->UndoActionEnd();
1116 mpImpl
->mpTextEngine
->FormatAndUpdate( this );
1119 TextPaM
TextView::CursorLeft( const TextPaM
& rPaM
, sal_uInt16 nCharacterIteratorMode
)
1121 TextPaM
aPaM( rPaM
);
1123 if ( aPaM
.GetIndex() )
1125 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aPaM
.GetPara() ].get();
1126 css::uno::Reference
< css::i18n::XBreakIterator
> xBI
= mpImpl
->mpTextEngine
->GetBreakIterator();
1127 sal_Int32 nCount
= 1;
1128 aPaM
.GetIndex() = xBI
->previousCharacters( pNode
->GetText(), aPaM
.GetIndex(), mpImpl
->mpTextEngine
->GetLocale(), nCharacterIteratorMode
, nCount
, nCount
);
1130 else if ( aPaM
.GetPara() )
1133 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aPaM
.GetPara() ].get();
1134 aPaM
.GetIndex() = pNode
->GetText().getLength();
1139 TextPaM
TextView::CursorRight( const TextPaM
& rPaM
, sal_uInt16 nCharacterIteratorMode
)
1141 TextPaM
aPaM( rPaM
);
1143 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aPaM
.GetPara() ].get();
1144 if ( aPaM
.GetIndex() < pNode
->GetText().getLength() )
1146 css::uno::Reference
< css::i18n::XBreakIterator
> xBI
= mpImpl
->mpTextEngine
->GetBreakIterator();
1147 sal_Int32 nCount
= 1;
1148 aPaM
.GetIndex() = xBI
->nextCharacters( pNode
->GetText(), aPaM
.GetIndex(), mpImpl
->mpTextEngine
->GetLocale(), nCharacterIteratorMode
, nCount
, nCount
);
1150 else if ( aPaM
.GetPara() < ( mpImpl
->mpTextEngine
->mpDoc
->GetNodes().size()-1) )
1153 aPaM
.GetIndex() = 0;
1159 TextPaM
TextView::CursorWordLeft( const TextPaM
& rPaM
)
1161 TextPaM
aPaM( rPaM
);
1163 if ( aPaM
.GetIndex() )
1165 // tdf#57879 - expand selection to the left to include connector punctuations
1166 mpImpl
->mpTextEngine
->GetWord( rPaM
, &aPaM
);
1167 if ( aPaM
.GetIndex() >= rPaM
.GetIndex() )
1169 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aPaM
.GetPara() ].get();
1170 css::uno::Reference
< css::i18n::XBreakIterator
> xBI
= mpImpl
->mpTextEngine
->GetBreakIterator();
1171 aPaM
.GetIndex() = xBI
->previousWord( pNode
->GetText(), rPaM
.GetIndex(), mpImpl
->mpTextEngine
->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES
).startPos
;
1172 if ( aPaM
.GetIndex() > 0 )
1173 mpImpl
->mpTextEngine
->GetWord( aPaM
, &aPaM
);
1175 aPaM
.GetIndex() = 0;
1178 else if ( aPaM
.GetPara() )
1181 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aPaM
.GetPara() ].get();
1182 aPaM
.GetIndex() = pNode
->GetText().getLength();
1187 TextPaM
TextView::CursorWordRight( const TextPaM
& rPaM
)
1189 TextPaM
aPaM( rPaM
);
1191 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aPaM
.GetPara() ].get();
1192 if ( aPaM
.GetIndex() < pNode
->GetText().getLength() )
1194 css::uno::Reference
< css::i18n::XBreakIterator
> xBI
= mpImpl
->mpTextEngine
->GetBreakIterator();
1195 aPaM
.GetIndex() = xBI
->nextWord( pNode
->GetText(), aPaM
.GetIndex(), mpImpl
->mpTextEngine
->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES
).endPos
;
1196 mpImpl
->mpTextEngine
->GetWord( aPaM
, nullptr, &aPaM
);
1198 else if ( aPaM
.GetPara() < ( mpImpl
->mpTextEngine
->mpDoc
->GetNodes().size()-1) )
1201 aPaM
.GetIndex() = 0;
1207 TextPaM
TextView::ImpDelete( sal_uInt8 nMode
, sal_uInt8 nDelMode
)
1209 if ( mpImpl
->maSelection
.HasRange() ) // only delete selection
1210 return mpImpl
->mpTextEngine
->ImpDeleteText( mpImpl
->maSelection
);
1212 TextPaM aStartPaM
= mpImpl
->maSelection
.GetStart();
1213 TextPaM aEndPaM
= aStartPaM
;
1214 if ( nMode
== DEL_LEFT
)
1216 if ( nDelMode
== DELMODE_SIMPLE
)
1218 aEndPaM
= CursorLeft( aEndPaM
, sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCHARACTER
) );
1220 else if ( nDelMode
== DELMODE_RESTOFWORD
)
1222 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aEndPaM
.GetPara() ].get();
1223 css::uno::Reference
< css::i18n::XBreakIterator
> xBI
= mpImpl
->mpTextEngine
->GetBreakIterator();
1224 css::i18n::Boundary aBoundary
= xBI
->getWordBoundary( pNode
->GetText(), mpImpl
->maSelection
.GetEnd().GetIndex(), mpImpl
->mpTextEngine
->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES
, true );
1225 if ( aBoundary
.startPos
== mpImpl
->maSelection
.GetEnd().GetIndex() )
1226 aBoundary
= xBI
->previousWord( pNode
->GetText(), mpImpl
->maSelection
.GetEnd().GetIndex(), mpImpl
->mpTextEngine
->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES
);
1227 // #i63506# startPos is -1 when the paragraph starts with a tab
1228 aEndPaM
.GetIndex() = std::max
<sal_Int32
>(aBoundary
.startPos
, 0);
1230 else // DELMODE_RESTOFCONTENT
1232 if ( aEndPaM
.GetIndex() != 0 )
1233 aEndPaM
.GetIndex() = 0;
1234 else if ( aEndPaM
.GetPara() )
1236 // previous paragraph
1237 aEndPaM
.GetPara()--;
1238 aEndPaM
.GetIndex() = 0;
1244 if ( nDelMode
== DELMODE_SIMPLE
)
1246 aEndPaM
= CursorRight( aEndPaM
, sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCELL
) );
1248 else if ( nDelMode
== DELMODE_RESTOFWORD
)
1250 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aEndPaM
.GetPara() ].get();
1251 css::uno::Reference
< css::i18n::XBreakIterator
> xBI
= mpImpl
->mpTextEngine
->GetBreakIterator();
1252 css::i18n::Boundary aBoundary
= xBI
->nextWord( pNode
->GetText(), mpImpl
->maSelection
.GetEnd().GetIndex(), mpImpl
->mpTextEngine
->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES
);
1253 aEndPaM
.GetIndex() = aBoundary
.startPos
;
1255 else // DELMODE_RESTOFCONTENT
1257 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aEndPaM
.GetPara() ].get();
1258 if ( aEndPaM
.GetIndex() < pNode
->GetText().getLength() )
1259 aEndPaM
.GetIndex() = pNode
->GetText().getLength();
1260 else if ( aEndPaM
.GetPara() < ( mpImpl
->mpTextEngine
->mpDoc
->GetNodes().size() - 1 ) )
1263 aEndPaM
.GetPara()++;
1264 TextNode
* pNextNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aEndPaM
.GetPara() ].get();
1265 aEndPaM
.GetIndex() = pNextNode
->GetText().getLength();
1270 return mpImpl
->mpTextEngine
->ImpDeleteText( TextSelection( aStartPaM
, aEndPaM
) );
1273 TextPaM
TextView::CursorUp( const TextPaM
& rPaM
)
1275 TextPaM
aPaM( rPaM
);
1278 if ( mpImpl
->mnTravelXPos
== TRAVEL_X_DONTKNOW
)
1280 nX
= mpImpl
->mpTextEngine
->GetEditCursor( rPaM
, false ).Left();
1281 mpImpl
->mnTravelXPos
= static_cast<sal_uInt16
>(nX
)+1;
1284 nX
= mpImpl
->mnTravelXPos
;
1286 TEParaPortion
* pPPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( rPaM
.GetPara() );
1287 std::vector
<TextLine
>::size_type nLine
= pPPortion
->GetLineNumber( rPaM
.GetIndex(), false );
1288 if ( nLine
) // same paragraph
1290 aPaM
.GetIndex() = mpImpl
->mpTextEngine
->GetCharPos( rPaM
.GetPara(), nLine
-1, nX
);
1291 // If we need to go to the end of a line that was wrapped automatically,
1292 // the cursor ends up at the beginning of the 2nd line
1293 // Problem: Last character of an automatically wrapped line = Cursor
1294 TextLine
& rLine
= pPPortion
->GetLines()[ nLine
- 1 ];
1295 if ( aPaM
.GetIndex() && ( aPaM
.GetIndex() == rLine
.GetEnd() ) )
1298 else if ( rPaM
.GetPara() ) // previous paragraph
1301 pPPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( aPaM
.GetPara() );
1302 std::vector
<TextLine
>::size_type nL
= pPPortion
->GetLines().size() - 1;
1303 aPaM
.GetIndex() = mpImpl
->mpTextEngine
->GetCharPos( aPaM
.GetPara(), nL
, nX
+1 );
1309 TextPaM
TextView::CursorDown( const TextPaM
& rPaM
)
1311 TextPaM
aPaM( rPaM
);
1314 if ( mpImpl
->mnTravelXPos
== TRAVEL_X_DONTKNOW
)
1316 nX
= mpImpl
->mpTextEngine
->GetEditCursor( rPaM
, false ).Left();
1317 mpImpl
->mnTravelXPos
= static_cast<sal_uInt16
>(nX
)+1;
1320 nX
= mpImpl
->mnTravelXPos
;
1322 TEParaPortion
* pPPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( rPaM
.GetPara() );
1323 std::vector
<TextLine
>::size_type nLine
= pPPortion
->GetLineNumber( rPaM
.GetIndex(), false );
1324 if ( nLine
< ( pPPortion
->GetLines().size() - 1 ) )
1326 aPaM
.GetIndex() = mpImpl
->mpTextEngine
->GetCharPos( rPaM
.GetPara(), nLine
+1, nX
);
1328 // special case CursorUp
1329 TextLine
& rLine
= pPPortion
->GetLines()[ nLine
+ 1 ];
1330 if ( ( aPaM
.GetIndex() == rLine
.GetEnd() ) && ( aPaM
.GetIndex() > rLine
.GetStart() ) && aPaM
.GetIndex() < pPPortion
->GetNode()->GetText().getLength() )
1333 else if ( rPaM
.GetPara() < ( mpImpl
->mpTextEngine
->mpDoc
->GetNodes().size() - 1 ) ) // next paragraph
1336 pPPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( aPaM
.GetPara() );
1337 aPaM
.GetIndex() = mpImpl
->mpTextEngine
->GetCharPos( aPaM
.GetPara(), 0, nX
+1 );
1338 TextLine
& rLine
= pPPortion
->GetLines().front();
1339 if ( ( aPaM
.GetIndex() == rLine
.GetEnd() ) && ( aPaM
.GetIndex() > rLine
.GetStart() ) && ( pPPortion
->GetLines().size() > 1 ) )
1346 TextPaM
TextView::CursorStartOfLine( const TextPaM
& rPaM
)
1348 TextPaM
aPaM( rPaM
);
1350 TEParaPortion
* pPPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( rPaM
.GetPara() );
1351 std::vector
<TextLine
>::size_type nLine
= pPPortion
->GetLineNumber( aPaM
.GetIndex(), false );
1352 TextLine
& rLine
= pPPortion
->GetLines()[ nLine
];
1353 aPaM
.GetIndex() = rLine
.GetStart();
1358 TextPaM
TextView::CursorEndOfLine( const TextPaM
& rPaM
)
1360 TextPaM
aPaM( rPaM
);
1362 TEParaPortion
* pPPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( rPaM
.GetPara() );
1363 std::vector
<TextLine
>::size_type nLine
= pPPortion
->GetLineNumber( aPaM
.GetIndex(), false );
1364 TextLine
& rLine
= pPPortion
->GetLines()[ nLine
];
1365 aPaM
.GetIndex() = rLine
.GetEnd();
1367 if ( rLine
.GetEnd() > rLine
.GetStart() ) // empty line
1369 sal_Unicode cLastChar
= pPPortion
->GetNode()->GetText()[ aPaM
.GetIndex()-1 ];
1370 if ( ( cLastChar
== ' ' ) && ( aPaM
.GetIndex() != pPPortion
->GetNode()->GetText().getLength() ) )
1372 // for a blank in an automatically-wrapped line it is better to stand before it,
1373 // as the user will intend to stand behind the prior word.
1374 // If there is a change, special case for Pos1 after End!
1381 TextPaM
TextView::CursorStartOfParagraph( const TextPaM
& rPaM
)
1383 TextPaM
aPaM( rPaM
);
1384 aPaM
.GetIndex() = 0;
1388 TextPaM
TextView::CursorEndOfParagraph( const TextPaM
& rPaM
)
1390 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ rPaM
.GetPara() ].get();
1391 TextPaM
aPaM( rPaM
);
1392 aPaM
.GetIndex() = pNode
->GetText().getLength();
1396 TextPaM
TextView::CursorStartOfDoc()
1398 TextPaM
aPaM( 0, 0 );
1402 TextPaM
TextView::CursorEndOfDoc()
1404 const sal_uInt32 nNode
= static_cast<sal_uInt32
>(mpImpl
->mpTextEngine
->mpDoc
->GetNodes().size() - 1);
1405 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ nNode
].get();
1406 TextPaM
aPaM( nNode
, pNode
->GetText().getLength() );
1410 TextPaM
TextView::PageUp( const TextPaM
& rPaM
)
1412 tools::Rectangle aRect
= mpImpl
->mpTextEngine
->PaMtoEditCursor( rPaM
);
1413 Point aTopLeft
= aRect
.TopLeft();
1414 aTopLeft
.AdjustY( -(mpImpl
->mpWindow
->GetOutputSizePixel().Height() * 9/10) );
1415 aTopLeft
.AdjustX(1 );
1416 if ( aTopLeft
.Y() < 0 )
1419 TextPaM aPaM
= mpImpl
->mpTextEngine
->GetPaM( aTopLeft
);
1423 TextPaM
TextView::PageDown( const TextPaM
& rPaM
)
1425 tools::Rectangle aRect
= mpImpl
->mpTextEngine
->PaMtoEditCursor( rPaM
);
1426 Point aBottomRight
= aRect
.BottomRight();
1427 aBottomRight
.AdjustY(mpImpl
->mpWindow
->GetOutputSizePixel().Height() * 9/10 );
1428 aBottomRight
.AdjustX(1 );
1429 tools::Long nHeight
= mpImpl
->mpTextEngine
->GetTextHeight();
1430 if ( aBottomRight
.Y() > nHeight
)
1431 aBottomRight
.setY( nHeight
-1 );
1433 TextPaM aPaM
= mpImpl
->mpTextEngine
->GetPaM( aBottomRight
);
1437 void TextView::ImpShowCursor( bool bGotoCursor
, bool bForceVisCursor
, bool bSpecial
)
1439 if ( mpImpl
->mpTextEngine
->IsFormatting() )
1441 if ( !mpImpl
->mpTextEngine
->GetUpdateMode() )
1443 if ( mpImpl
->mpTextEngine
->IsInUndo() )
1446 mpImpl
->mpTextEngine
->CheckIdleFormatter();
1447 if ( !mpImpl
->mpTextEngine
->IsFormatted() )
1448 mpImpl
->mpTextEngine
->FormatAndUpdate( this );
1450 TextPaM
aPaM( mpImpl
->maSelection
.GetEnd() );
1451 tools::Rectangle aEditCursor
= mpImpl
->mpTextEngine
->PaMtoEditCursor( aPaM
, bSpecial
);
1453 // Remember that we placed the cursor behind the last character of a line
1454 mpImpl
->mbCursorAtEndOfLine
= false;
1457 TEParaPortion
* pParaPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( aPaM
.GetPara() );
1458 mpImpl
->mbCursorAtEndOfLine
=
1459 pParaPortion
->GetLineNumber( aPaM
.GetIndex(), true ) != pParaPortion
->GetLineNumber( aPaM
.GetIndex(), false );
1462 if ( !IsInsertMode() && !mpImpl
->maSelection
.HasRange() )
1464 TextNode
* pNode
= mpImpl
->mpTextEngine
->mpDoc
->GetNodes()[ aPaM
.GetPara() ].get();
1465 if ( !pNode
->GetText().isEmpty() && ( aPaM
.GetIndex() < pNode
->GetText().getLength() ) )
1467 // If we are behind a portion, and the next portion has other direction, we must change position...
1468 aEditCursor
.SetLeft( mpImpl
->mpTextEngine
->GetEditCursor( aPaM
, false, true ).Left() );
1469 aEditCursor
.SetRight( aEditCursor
.Left() );
1471 TEParaPortion
* pParaPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( aPaM
.GetPara() );
1473 sal_Int32 nTextPortionStart
= 0;
1474 std::size_t nTextPortion
= pParaPortion
->GetTextPortions().FindPortion( aPaM
.GetIndex(), nTextPortionStart
, true );
1475 TETextPortion
& rTextPortion
= pParaPortion
->GetTextPortions()[ nTextPortion
];
1476 if ( rTextPortion
.GetKind() == PORTIONKIND_TAB
)
1478 aEditCursor
.AdjustRight(rTextPortion
.GetWidth() );
1482 TextPaM aNext
= CursorRight( TextPaM( aPaM
.GetPara(), aPaM
.GetIndex() ), sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCELL
) );
1483 aEditCursor
.SetRight( mpImpl
->mpTextEngine
->GetEditCursor( aNext
, true ).Left() );
1488 Size aOutSz
= mpImpl
->mpWindow
->GetOutputSizePixel();
1489 if ( aEditCursor
.GetHeight() > aOutSz
.Height() )
1490 aEditCursor
.SetBottom( aEditCursor
.Top() + aOutSz
.Height() - 1 );
1492 aEditCursor
.AdjustLeft( -1 );
1495 // #i81283# protect maStartDocPos against initialization problems
1496 && aOutSz
.Width() && aOutSz
.Height()
1499 tools::Long nVisStartY
= mpImpl
->maStartDocPos
.Y();
1500 tools::Long nVisEndY
= mpImpl
->maStartDocPos
.Y() + aOutSz
.Height();
1501 tools::Long nVisStartX
= mpImpl
->maStartDocPos
.X();
1502 tools::Long nVisEndX
= mpImpl
->maStartDocPos
.X() + aOutSz
.Width();
1503 tools::Long nMoreX
= aOutSz
.Width() / 4;
1505 Point
aNewStartPos( mpImpl
->maStartDocPos
);
1507 if ( aEditCursor
.Bottom() > nVisEndY
)
1509 aNewStartPos
.AdjustY( aEditCursor
.Bottom() - nVisEndY
);
1511 else if ( aEditCursor
.Top() < nVisStartY
)
1513 aNewStartPos
.AdjustY( -( nVisStartY
- aEditCursor
.Top() ) );
1516 if ( aEditCursor
.Right() >= nVisEndX
)
1518 aNewStartPos
.AdjustX( aEditCursor
.Right() - nVisEndX
);
1520 // do you want some more?
1521 aNewStartPos
.AdjustX(nMoreX
);
1523 else if ( aEditCursor
.Left() <= nVisStartX
)
1525 aNewStartPos
.AdjustX( -( nVisStartX
- aEditCursor
.Left() ) );
1527 // do you want some more?
1528 aNewStartPos
.AdjustX( -nMoreX
);
1531 // X can be wrong for the 'some more' above:
1532 // sal_uLong nMaxTextWidth = mpImpl->mpTextEngine->GetMaxTextWidth();
1533 // if ( !nMaxTextWidth || ( nMaxTextWidth > 0x7FFFFFFF ) )
1534 // nMaxTextWidth = 0x7FFFFFFF;
1535 // long nMaxX = (long)nMaxTextWidth - aOutSz.Width();
1536 tools::Long nMaxX
= mpImpl
->mpTextEngine
->CalcTextWidth() - aOutSz
.Width();
1540 if ( aNewStartPos
.X() < 0 )
1541 aNewStartPos
.setX( 0 );
1542 else if ( aNewStartPos
.X() > nMaxX
)
1543 aNewStartPos
.setX( nMaxX
);
1545 // Y should not be further down than needed
1546 tools::Long nYMax
= mpImpl
->mpTextEngine
->GetTextHeight() - aOutSz
.Height();
1549 if ( aNewStartPos
.Y() > nYMax
)
1550 aNewStartPos
.setY( nYMax
);
1552 if ( aNewStartPos
!= mpImpl
->maStartDocPos
)
1553 Scroll( -(aNewStartPos
.X() - mpImpl
->maStartDocPos
.X()), -(aNewStartPos
.Y() - mpImpl
->maStartDocPos
.Y()) );
1556 if ( aEditCursor
.Right() < aEditCursor
.Left() )
1558 tools::Long n
= aEditCursor
.Left();
1559 aEditCursor
.SetLeft( aEditCursor
.Right() );
1560 aEditCursor
.SetRight( n
);
1563 Point
aPoint( GetWindowPos( !mpImpl
->mpTextEngine
->IsRightToLeft() ? aEditCursor
.TopLeft() : aEditCursor
.TopRight() ) );
1564 mpImpl
->mpCursor
->SetPos( aPoint
);
1565 mpImpl
->mpCursor
->SetSize( aEditCursor
.GetSize() );
1566 if ( bForceVisCursor
&& mpImpl
->mbCursorEnabled
)
1567 mpImpl
->mpCursor
->Show();
1570 void TextView::SetCursorAtPoint( const Point
& rPosPixel
)
1572 mpImpl
->mpTextEngine
->CheckIdleFormatter();
1574 Point aDocPos
= GetDocPos( rPosPixel
);
1576 TextPaM aPaM
= mpImpl
->mpTextEngine
->GetPaM( aDocPos
);
1578 // aTmpNewSel: Diff between old and new; not the new selection
1579 TextSelection
aTmpNewSel( mpImpl
->maSelection
.GetEnd(), aPaM
);
1580 TextSelection
aNewSel( mpImpl
->maSelection
);
1581 aNewSel
.GetEnd() = aPaM
;
1583 if ( !mpImpl
->mpSelEngine
->HasAnchor() )
1585 if ( mpImpl
->maSelection
.GetStart() != aPaM
)
1586 mpImpl
->mpTextEngine
->CursorMoved( mpImpl
->maSelection
.GetStart().GetPara() );
1587 aNewSel
.GetStart() = aPaM
;
1588 ImpSetSelection( aNewSel
);
1592 ImpSetSelection( aNewSel
);
1593 ShowSelection( aTmpNewSel
);
1596 bool bForceCursor
= !mpImpl
->mpDDInfo
; // && !mbInSelection
1597 ImpShowCursor( mpImpl
->mbAutoScroll
, bForceCursor
, false );
1600 bool TextView::IsSelectionAtPoint( const Point
& rPosPixel
)
1602 Point aDocPos
= GetDocPos( rPosPixel
);
1603 TextPaM aPaM
= mpImpl
->mpTextEngine
->GetPaM( aDocPos
);
1604 // BeginDrag is only called, however, if IsSelectionAtPoint()
1605 // Problem: IsSelectionAtPoint is not called by Command()
1606 // if before MBDown returned false.
1607 return IsInSelection( aPaM
);
1610 bool TextView::IsInSelection( const TextPaM
& rPaM
)
1612 TextSelection aSel
= mpImpl
->maSelection
;
1615 const sal_uInt32 nStartNode
= aSel
.GetStart().GetPara();
1616 const sal_uInt32 nEndNode
= aSel
.GetEnd().GetPara();
1617 const sal_uInt32 nCurNode
= rPaM
.GetPara();
1619 if ( ( nCurNode
> nStartNode
) && ( nCurNode
< nEndNode
) )
1622 if ( nStartNode
== nEndNode
)
1624 if ( nCurNode
== nStartNode
)
1625 if ( ( rPaM
.GetIndex() >= aSel
.GetStart().GetIndex() ) && ( rPaM
.GetIndex() < aSel
.GetEnd().GetIndex() ) )
1628 else if ( ( nCurNode
== nStartNode
) && ( rPaM
.GetIndex() >= aSel
.GetStart().GetIndex() ) )
1630 else if ( ( nCurNode
== nEndNode
) && ( rPaM
.GetIndex() < aSel
.GetEnd().GetIndex() ) )
1636 void TextView::ImpHideDDCursor()
1638 if ( mpImpl
->mpDDInfo
&& mpImpl
->mpDDInfo
->mbVisCursor
)
1640 mpImpl
->mpDDInfo
->maCursor
.Hide();
1641 mpImpl
->mpDDInfo
->mbVisCursor
= false;
1645 void TextView::ImpShowDDCursor()
1647 if ( !mpImpl
->mpDDInfo
->mbVisCursor
)
1649 tools::Rectangle aCursor
= mpImpl
->mpTextEngine
->PaMtoEditCursor( mpImpl
->mpDDInfo
->maDropPos
, true );
1650 aCursor
.AdjustRight( 1 );
1651 aCursor
.SetPos( GetWindowPos( aCursor
.TopLeft() ) );
1653 mpImpl
->mpDDInfo
->maCursor
.SetWindow( mpImpl
->mpWindow
);
1654 mpImpl
->mpDDInfo
->maCursor
.SetPos( aCursor
.TopLeft() );
1655 mpImpl
->mpDDInfo
->maCursor
.SetSize( aCursor
.GetSize() );
1656 mpImpl
->mpDDInfo
->maCursor
.Show();
1657 mpImpl
->mpDDInfo
->mbVisCursor
= true;
1661 void TextView::SetPaintSelection( bool bPaint
)
1663 if ( bPaint
!= mpImpl
->mbPaintSelection
)
1665 mpImpl
->mbPaintSelection
= bPaint
;
1666 ShowSelection( mpImpl
->maSelection
);
1670 void TextView::Read( SvStream
& rInput
)
1672 mpImpl
->mpTextEngine
->Read( rInput
, &mpImpl
->maSelection
);
1676 bool TextView::ImplTruncateNewText( OUString
& rNewText
) const
1678 bool bTruncated
= false;
1680 const sal_Int32 nMaxLen
= mpImpl
->mpTextEngine
->GetMaxTextLen();
1681 // 0 means unlimited
1684 const sal_Int32 nCurLen
= mpImpl
->mpTextEngine
->GetTextLen();
1686 const sal_Int32 nNewLen
= rNewText
.getLength();
1687 if ( nCurLen
+ nNewLen
> nMaxLen
)
1689 // see how much text will be replaced
1690 const sal_Int32 nSelLen
= mpImpl
->mpTextEngine
->GetTextLen( mpImpl
->maSelection
);
1691 if ( nCurLen
+ nNewLen
- nSelLen
> nMaxLen
)
1693 const sal_Int32 nTruncatedLen
= nMaxLen
- (nCurLen
- nSelLen
);
1694 rNewText
= rNewText
.copy( 0, nTruncatedLen
);
1702 bool TextView::ImplCheckTextLen( const OUString
& rNewText
)
1705 if ( mpImpl
->mpTextEngine
->GetMaxTextLen() )
1707 sal_Int32 n
= mpImpl
->mpTextEngine
->GetTextLen() + rNewText
.getLength();
1708 if ( n
> mpImpl
->mpTextEngine
->GetMaxTextLen() )
1710 // calculate how much text is being deleted
1711 n
-= mpImpl
->mpTextEngine
->GetTextLen( mpImpl
->maSelection
);
1712 if ( n
> mpImpl
->mpTextEngine
->GetMaxTextLen() )
1719 void TextView::dragGestureRecognized( const css::datatransfer::dnd::DragGestureEvent
& rDGE
)
1721 if ( !mpImpl
->mbClickedInSelection
)
1724 SolarMutexGuard aVclGuard
;
1726 SAL_WARN_IF( !mpImpl
->maSelection
.HasRange(), "vcl", "TextView::dragGestureRecognized: mpImpl->mbClickedInSelection, but no selection?" );
1728 mpImpl
->mpDDInfo
.reset(new TextDDInfo
);
1729 mpImpl
->mpDDInfo
->mbStarterOfDD
= true;
1731 rtl::Reference
<TETextDataObject
> pDataObj
= new TETextDataObject( GetSelected() );
1733 mpImpl
->mpCursor
->Hide();
1735 sal_Int8 nActions
= css::datatransfer::dnd::DNDConstants::ACTION_COPY
;
1736 if ( !IsReadOnly() )
1737 nActions
|= css::datatransfer::dnd::DNDConstants::ACTION_MOVE
;
1738 rDGE
.DragSource
->startDrag( rDGE
, nActions
, 0 /*cursor*/, 0 /*image*/, pDataObj
, mpImpl
->mxDnDListener
);
1741 void TextView::dragDropEnd( const css::datatransfer::dnd::DragSourceDropEvent
& )
1744 mpImpl
->mpDDInfo
.reset();
1747 void TextView::drop( const css::datatransfer::dnd::DropTargetDropEvent
& rDTDE
)
1749 SolarMutexGuard aVclGuard
;
1751 if ( !mpImpl
->mbReadOnly
&& mpImpl
->mpDDInfo
)
1755 // Data for deleting after DROP_MOVE:
1756 TextSelection
aPrevSel( mpImpl
->maSelection
);
1758 const sal_uInt32 nPrevParaCount
= mpImpl
->mpTextEngine
->GetParagraphCount();
1759 const sal_Int32 nPrevStartParaLen
= mpImpl
->mpTextEngine
->GetTextLen( aPrevSel
.GetStart().GetPara() );
1761 bool bStarterOfDD
= false;
1762 for ( sal_uInt16 nView
= mpImpl
->mpTextEngine
->GetViewCount(); nView
&& !bStarterOfDD
; )
1763 bStarterOfDD
= mpImpl
->mpTextEngine
->GetView( --nView
)->mpImpl
->mpDDInfo
&& mpImpl
->mpTextEngine
->GetView( nView
)->mpImpl
->mpDDInfo
->mbStarterOfDD
;
1766 ImpSetSelection( mpImpl
->mpDDInfo
->maDropPos
);
1768 mpImpl
->mpTextEngine
->UndoActionStart();
1771 css::uno::Reference
< css::datatransfer::XTransferable
> xDataObj
= rDTDE
.Transferable
;
1772 if ( xDataObj
.is() )
1774 css::datatransfer::DataFlavor aFlavor
;
1775 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING
, aFlavor
);
1776 if ( xDataObj
->isDataFlavorSupported( aFlavor
) )
1778 css::uno::Any aData
= xDataObj
->getTransferData( aFlavor
);
1780 aData
>>= aOUString
;
1781 aText
= convertLineEnd(aOUString
, LINEEND_LF
);
1785 if ( !aText
.isEmpty() && ( aText
[ aText
.getLength()-1 ] == LINE_SEP
) )
1786 aText
= aText
.copy(0, aText
.getLength()-1);
1788 if ( ImplCheckTextLen( aText
) )
1789 ImpSetSelection( mpImpl
->mpTextEngine
->ImpInsertText( mpImpl
->mpDDInfo
->maDropPos
, aText
) );
1791 if ( aPrevSel
.HasRange() &&
1792 (( rDTDE
.DropAction
& css::datatransfer::dnd::DNDConstants::ACTION_MOVE
) || !bStarterOfDD
) )
1794 // adjust selection if necessary
1795 if ( ( mpImpl
->mpDDInfo
->maDropPos
.GetPara() < aPrevSel
.GetStart().GetPara() ) ||
1796 ( ( mpImpl
->mpDDInfo
->maDropPos
.GetPara() == aPrevSel
.GetStart().GetPara() )
1797 && ( mpImpl
->mpDDInfo
->maDropPos
.GetIndex() < aPrevSel
.GetStart().GetIndex() ) ) )
1799 const sal_uInt32 nNewParasBeforeSelection
=
1800 mpImpl
->mpTextEngine
->GetParagraphCount() - nPrevParaCount
;
1802 aPrevSel
.GetStart().GetPara() += nNewParasBeforeSelection
;
1803 aPrevSel
.GetEnd().GetPara() += nNewParasBeforeSelection
;
1805 if ( mpImpl
->mpDDInfo
->maDropPos
.GetPara() == aPrevSel
.GetStart().GetPara() )
1807 const sal_Int32 nNewChars
=
1808 mpImpl
->mpTextEngine
->GetTextLen( aPrevSel
.GetStart().GetPara() ) - nPrevStartParaLen
;
1810 aPrevSel
.GetStart().GetIndex() += nNewChars
;
1811 if ( aPrevSel
.GetStart().GetPara() == aPrevSel
.GetEnd().GetPara() )
1812 aPrevSel
.GetEnd().GetIndex() += nNewChars
;
1817 // adjust current selection
1818 TextPaM aPaM
= mpImpl
->maSelection
.GetStart();
1819 aPaM
.GetPara() -= ( aPrevSel
.GetEnd().GetPara() - aPrevSel
.GetStart().GetPara() );
1820 if ( aPrevSel
.GetEnd().GetPara() == mpImpl
->mpDDInfo
->maDropPos
.GetPara() )
1822 aPaM
.GetIndex() -= aPrevSel
.GetEnd().GetIndex();
1823 if ( aPrevSel
.GetStart().GetPara() == mpImpl
->mpDDInfo
->maDropPos
.GetPara() )
1824 aPaM
.GetIndex() += aPrevSel
.GetStart().GetIndex();
1826 ImpSetSelection( aPaM
);
1829 mpImpl
->mpTextEngine
->ImpDeleteText( aPrevSel
);
1832 mpImpl
->mpTextEngine
->UndoActionEnd();
1834 mpImpl
->mpDDInfo
.reset();
1836 mpImpl
->mpTextEngine
->FormatAndUpdate( this );
1838 mpImpl
->mpTextEngine
->Broadcast( TextHint( SfxHintId::TextModified
) );
1840 rDTDE
.Context
->dropComplete( false/*bChanges*/ );
1843 void TextView::dragEnter( const css::datatransfer::dnd::DropTargetDragEnterEvent
& )
1847 void TextView::dragExit( const css::datatransfer::dnd::DropTargetEvent
& )
1849 SolarMutexGuard aVclGuard
;
1853 void TextView::dragOver( const css::datatransfer::dnd::DropTargetDragEvent
& rDTDE
)
1855 SolarMutexGuard aVclGuard
;
1857 if (!mpImpl
->mpDDInfo
)
1858 mpImpl
->mpDDInfo
.reset(new TextDDInfo
);
1860 TextPaM aPrevDropPos
= mpImpl
->mpDDInfo
->maDropPos
;
1861 Point
aMousePos( rDTDE
.LocationX
, rDTDE
.LocationY
);
1862 Point aDocPos
= GetDocPos( aMousePos
);
1863 mpImpl
->mpDDInfo
->maDropPos
= mpImpl
->mpTextEngine
->GetPaM( aDocPos
);
1865 // Don't drop in selection or in read only engine
1866 if ( IsReadOnly() || IsInSelection( mpImpl
->mpDDInfo
->maDropPos
))
1869 rDTDE
.Context
->rejectDrag();
1873 // delete old Cursor
1874 if ( !mpImpl
->mpDDInfo
->mbVisCursor
|| ( aPrevDropPos
!= mpImpl
->mpDDInfo
->maDropPos
) )
1879 rDTDE
.Context
->acceptDrag( rDTDE
.DropAction
);
1883 Point
TextView::ImpGetOutputStartPos( const Point
& rStartDocPos
) const
1885 Point
aStartPos( -rStartDocPos
.X(), -rStartDocPos
.Y() );
1886 if ( mpImpl
->mpTextEngine
->IsRightToLeft() )
1888 Size aSz
= mpImpl
->mpWindow
->GetOutputSizePixel();
1889 aStartPos
.setX( rStartDocPos
.X() + aSz
.Width() - 1 ); // -1: Start is 0
1894 Point
TextView::GetDocPos( const Point
& rWindowPos
) const
1896 // Window Position => Document Position
1900 aPoint
.setY( rWindowPos
.Y() + mpImpl
->maStartDocPos
.Y() );
1902 if ( !mpImpl
->mpTextEngine
->IsRightToLeft() )
1904 aPoint
.setX( rWindowPos
.X() + mpImpl
->maStartDocPos
.X() );
1908 Size aSz
= mpImpl
->mpWindow
->GetOutputSizePixel();
1909 aPoint
.setX( ( aSz
.Width() - 1 ) - rWindowPos
.X() + mpImpl
->maStartDocPos
.X() );
1915 Point
TextView::GetWindowPos( const Point
& rDocPos
) const
1917 // Document Position => Window Position
1921 aPoint
.setY( rDocPos
.Y() - mpImpl
->maStartDocPos
.Y() );
1923 if ( !mpImpl
->mpTextEngine
->IsRightToLeft() )
1925 aPoint
.setX( rDocPos
.X() - mpImpl
->maStartDocPos
.X() );
1929 Size aSz
= mpImpl
->mpWindow
->GetOutputSizePixel();
1930 aPoint
.setX( ( aSz
.Width() - 1 ) - ( rDocPos
.X() - mpImpl
->maStartDocPos
.X() ) );
1936 sal_Int32
TextView::GetLineNumberOfCursorInSelection() const
1939 sal_Int32 nLineNo
= -1;
1940 if( mpImpl
->mbCursorEnabled
)
1942 TextPaM aPaM
= GetSelection().GetEnd();
1943 TEParaPortion
* pPPortion
= mpImpl
->mpTextEngine
->mpTEParaPortions
->GetObject( aPaM
.GetPara() );
1944 nLineNo
= pPPortion
->GetLineNumber( aPaM
.GetIndex(), false );
1945 //TODO: std::vector<TextLine>::size_type -> sal_Int32!
1946 if( mpImpl
->mbCursorAtEndOfLine
)
1952 // (+) class TextSelFunctionSet
1954 TextSelFunctionSet::TextSelFunctionSet( TextView
* pView
)
1959 void TextSelFunctionSet::BeginDrag()
1963 void TextSelFunctionSet::CreateAnchor()
1965 // TextSelection aSel( mpView->GetSelection() );
1966 // aSel.GetStart() = aSel.GetEnd();
1967 // mpView->SetSelection( aSel );
1969 // may not be followed by ShowCursor
1970 mpView
->HideSelection();
1971 mpView
->ImpSetSelection( mpView
->mpImpl
->maSelection
.GetEnd() );
1974 void TextSelFunctionSet::SetCursorAtPoint( const Point
& rPointPixel
, bool )
1976 mpView
->SetCursorAtPoint( rPointPixel
);
1979 bool TextSelFunctionSet::IsSelectionAtPoint( const Point
& rPointPixel
)
1981 return mpView
->IsSelectionAtPoint( rPointPixel
);
1984 void TextSelFunctionSet::DeselectAll()
1989 void TextSelFunctionSet::DeselectAtPoint( const Point
& )
1991 // only for multiple selection
1994 void TextSelFunctionSet::DestroyAnchor()
1996 // only for multiple selection
1998 TextEngine
* TextView::GetTextEngine() const
1999 { return mpImpl
->mpTextEngine
; }
2000 vcl::Window
* TextView::GetWindow() const
2001 { return mpImpl
->mpWindow
; }
2002 void TextView::EnableCursor( bool bEnable
)
2003 { mpImpl
->mbCursorEnabled
= bEnable
; }
2004 bool TextView::IsCursorEnabled() const
2005 { return mpImpl
->mbCursorEnabled
; }
2006 void TextView::SetStartDocPos( const Point
& rPos
)
2007 { mpImpl
->maStartDocPos
= rPos
; }
2008 const Point
& TextView::GetStartDocPos() const
2009 { return mpImpl
->maStartDocPos
; }
2010 void TextView::SetAutoIndentMode( bool bAutoIndent
)
2011 { mpImpl
->mbAutoIndent
= bAutoIndent
; }
2012 bool TextView::IsReadOnly() const
2013 { return mpImpl
->mbReadOnly
; }
2014 void TextView::SetAutoScroll( bool bAutoScroll
)
2015 { mpImpl
->mbAutoScroll
= bAutoScroll
; }
2016 bool TextView::IsAutoScroll() const
2017 { return mpImpl
->mbAutoScroll
; }
2018 bool TextView::HasSelection() const
2019 { return mpImpl
->maSelection
.HasRange(); }
2020 bool TextView::IsInsertMode() const
2021 { return mpImpl
->mbInsertMode
; }
2023 void TextView::MatchGroup()
2025 TextSelection
aTmpSel( GetSelection() );
2027 if ( ( aTmpSel
.GetStart().GetPara() != aTmpSel
.GetEnd().GetPara() ) ||
2028 ( ( aTmpSel
.GetEnd().GetIndex() - aTmpSel
.GetStart().GetIndex() ) > 1 ) )
2033 TextSelection aMatchSel
= static_cast<ExtTextEngine
*>(GetTextEngine())->MatchGroup( aTmpSel
.GetStart() );
2034 if ( aMatchSel
.HasRange() )
2035 SetSelection( aMatchSel
);
2038 void TextView::CenterPaM( const TextPaM
& rPaM
)
2040 // Get textview size and the corresponding y-coordinates
2041 Size aOutSz
= mpImpl
->mpWindow
->GetOutputSizePixel();
2042 tools::Long nVisStartY
= mpImpl
->maStartDocPos
.Y();
2043 tools::Long nVisEndY
= mpImpl
->maStartDocPos
.Y() + aOutSz
.Height();
2045 // Retrieve the coordinates of the PaM
2046 tools::Rectangle aRect
= mpImpl
->mpTextEngine
->PaMtoEditCursor(rPaM
);
2048 // Recalculate the offset of the center y-coordinates and scroll
2049 Scroll(0, (nVisStartY
+ nVisEndY
) / 2 - aRect
.TopLeft().getY());
2052 bool TextView::Search( const i18nutil::SearchOptions
& rSearchOptions
, bool bForward
)
2054 bool bFound
= false;
2055 TextSelection
aSel( GetSelection() );
2056 if ( static_cast<ExtTextEngine
*>(GetTextEngine())->Search( aSel
, rSearchOptions
, bForward
) )
2059 // First add the beginning of the word to the selection,
2060 // so that the whole word is in the visible region.
2061 SetSelection( aSel
.GetStart() );
2062 ShowCursor( true, false );
2066 aSel
= GetSelection().GetEnd();
2069 SetSelection( aSel
);
2070 // tdf#49482: Move the start of the selection to the center of the textview
2073 CenterPaM( aSel
.GetStart() );
2080 sal_uInt16
TextView::Replace( const i18nutil::SearchOptions
& rSearchOptions
, bool bAll
, bool bForward
)
2082 sal_uInt16 nFound
= 0;
2086 if ( GetSelection().HasRange() )
2088 InsertText( rSearchOptions
.replaceString
);
2090 Search( rSearchOptions
, bForward
); // right away to the next
2094 if( Search( rSearchOptions
, bForward
) )
2100 // the writer replaces all, from beginning to end
2102 ExtTextEngine
* pTextEngine
= static_cast<ExtTextEngine
*>(GetTextEngine());
2107 bool bSearchInSelection
= (0 != (rSearchOptions
.searchFlag
& css::util::SearchFlags::REG_NOT_BEGINOFLINE
) );
2108 if ( bSearchInSelection
)
2110 aSel
= GetSelection();
2114 TextSelection
aSearchSel( aSel
);
2116 bool bFound
= pTextEngine
->Search( aSel
, rSearchOptions
);
2118 pTextEngine
->UndoActionStart();
2123 TextPaM aNewStart
= pTextEngine
->ImpInsertText( aSel
, rSearchOptions
.replaceString
);
2124 // tdf#64690 - extend selection to include inserted text portions
2125 if ( aSel
.GetEnd().GetPara() == aSearchSel
.GetEnd().GetPara() )
2127 aSearchSel
.GetEnd().GetIndex() += rSearchOptions
.replaceString
.getLength() - 1;
2130 aSel
.GetStart() = aNewStart
;
2131 bFound
= pTextEngine
->Search( aSel
, rSearchOptions
);
2135 SetSelection( aSel
.GetStart() );
2136 pTextEngine
->FormatAndUpdate( this );
2137 pTextEngine
->UndoActionEnd();
2143 bool TextView::ImpIndentBlock( bool bRight
)
2147 TextSelection aSel
= GetSelection();
2151 GetTextEngine()->UndoActionStart();
2153 const sal_uInt32 nStartPara
= aSel
.GetStart().GetPara();
2154 sal_uInt32 nEndPara
= aSel
.GetEnd().GetPara();
2155 if ( aSel
.HasRange() && !aSel
.GetEnd().GetIndex() )
2157 nEndPara
--; // do not indent
2160 for ( sal_uInt32 nPara
= nStartPara
; nPara
<= nEndPara
; ++nPara
)
2165 GetTextEngine()->ImpInsertText( TextPaM( nPara
, 0 ), '\t' );
2170 // remove Tabs/Blanks
2171 OUString aText
= GetTextEngine()->GetText( nPara
);
2172 if ( !aText
.isEmpty() && (
2173 ( aText
[ 0 ] == '\t' ) ||
2174 ( aText
[ 0 ] == ' ' ) ) )
2176 GetTextEngine()->ImpDeleteText( TextSelection( TextPaM( nPara
, 0 ), TextPaM( nPara
, 1 ) ) );
2182 GetTextEngine()->UndoActionEnd();
2184 bool bRange
= aSel
.HasRange();
2187 ++aSel
.GetStart().GetIndex();
2188 if ( bRange
&& ( aSel
.GetEnd().GetPara() == nEndPara
) )
2189 ++aSel
.GetEnd().GetIndex();
2193 if ( aSel
.GetStart().GetIndex() )
2194 --aSel
.GetStart().GetIndex();
2195 if ( bRange
&& aSel
.GetEnd().GetIndex() )
2196 --aSel
.GetEnd().GetIndex();
2199 ImpSetSelection( aSel
);
2200 GetTextEngine()->FormatAndUpdate( this );
2205 bool TextView::IndentBlock()
2207 return ImpIndentBlock( true );
2210 bool TextView::UnindentBlock()
2212 return ImpIndentBlock( false );
2216 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */