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