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