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> 44 #include <cppuhelper/queryinterface.hxx> 45 #include <com/sun/star/i18n/XBreakIterator.hpp> 46 #include <com/sun/star/i18n/CharacterIteratorMode.hpp> 47 #include <com/sun/star/i18n/WordType.hpp> 48 #include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp> 49 #include <com/sun/star/datatransfer/XTransferable.hpp> 50 #include <com/sun/star/datatransfer/clipboard/XClipboard.hpp> 51 #include <com/sun/star/datatransfer/clipboard/XFlushableClipboard.hpp> 52 #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp> 53 #include <com/sun/star/datatransfer/dnd/XDragGestureRecognizer.hpp> 54 #include <com/sun/star/datatransfer/dnd/XDropTarget.hpp> 55 #include <com/sun/star/util/SearchFlags.hpp> 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 80 SotClipboardFormatId nT = SotExchange::GetFormat( rFlavor ); 81 if ( nT == SotClipboardFormatId::STRING ) 82 { 83 aAny <<= maText; 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 { 103 GetHTMLStream().Seek( STREAM_SEEK_TO_END ); 104 bool bHTML = GetHTMLStream().Tell() > 0; 105 css::uno::Sequence< css::datatransfer::DataFlavor > aDataFlavors( bHTML ? 2 : 1 ); 106 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aDataFlavors.getArray()[0] ); 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 { 114 SotClipboardFormatId nT = SotExchange::GetFormat( rFlavor ); 115 return ( nT == SotClipboardFormatId::STRING ); 116 } 117 118 struct ImpTextView 119 { 120 ExtTextEngine* mpTextEngine; 121 122 VclPtr<vcl::Window> mpWindow; 123 TextSelection maSelection; 124 Point maStartDocPos; 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; 140 bool mbPaintSelection : 1; 141 bool mbAutoIndent : 1; 142 bool mbHighlightSelection : 1; 143 bool mbCursorEnabled : 1; 144 bool mbClickedInSelection : 1; 145 bool mbCursorAtEndOfLine; 146 }; 147 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->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() ); 176 pWindow->SetInputContext( InputContext( pEng->GetFont(), InputContextFlags::Text|InputContextFlags::ExtText ) ); 177 178 if ( pWindow->GetSettings().GetStyleSettings().GetSelectionOptions() & SelectionOptions::Invert ) 179 mpImpl->mbHighlightSelection = true; 180 181 pWindow->SetLineColor(); 182 183 if ( pWindow->GetDragGestureRecognizer().is() ) 184 { 185 vcl::unohelper::DragAndDropWrapper* pDnDWrapper = new vcl::unohelper::DragAndDropWrapper( this ); 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 197 TextView::~TextView() 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 209 void TextView::Invalidate() 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 236 const TextSelection& TextView::GetSelection() const 237 { 238 return mpImpl->maSelection; 239 } 240 TextSelection& TextView::GetSelection() 241 { 242 return mpImpl->maSelection; 243 } 244 245 void TextView::DeleteSelected() 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 303 void TextView::ImpHighlight( const TextSelection& rSel ) 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 398 void TextView::ShowSelection() 399 { 400 ImpShowHideSelection(); 401 } 402 403 void TextView::HideSelection() 404 { 405 ImpShowHideSelection(); 406 } 407 408 void TextView::ShowSelection( const TextSelection& rRange ) 409 { 410 ImpShowHideSelection( &rRange ); 411 } 412 413 void TextView::ImpShowHideSelection(const TextSelection* pRange) 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 ) 633 SetInsertMode( !IsInsertMode() ); 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() && 687 ( GetWindow()->GetSettings().GetMouseSettings().GetMiddleButtonAction() == MouseMiddleButtonAction::PasteSelection ) ) 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 // tdf#57879 - expand selection to include connector punctuations 734 TextSelection aNewSel; 735 mpImpl->mpTextEngine->GetWord( mpImpl->maSelection.GetEnd(), &aNewSel.GetStart(), &aNewSel.GetEnd() ); 736 ImpSetSelection( aNewSel ); 737 ShowSelection(); 738 ShowCursor(); 739 } 740 } 741 else if ( rMouseEvent.GetClicks() == 3 ) 742 { 743 // select paragraph 744 if ( mpImpl->maSelection.GetStart().GetIndex() || ( mpImpl->maSelection.GetEnd().GetIndex() < mpImpl->mpTextEngine->GetTextLen( mpImpl->maSelection.GetEnd().GetPara() ) ) ) 745 { 746 HideSelection(); 747 TextSelection aNewSel( mpImpl->maSelection ); 748 aNewSel.GetStart().GetIndex() = 0; 749 aNewSel.GetEnd().GetIndex() = mpImpl->mpTextEngine->mpDoc->GetNodes()[ mpImpl->maSelection.GetEnd().GetPara() ]->GetText().getLength(); 750 ImpSetSelection( aNewSel ); 751 ShowSelection(); 752 ShowCursor(); 753 } 754 } 755 } 756 } 757 758 void TextView::MouseMove( const MouseEvent& rMouseEvent ) 759 { 760 mpImpl->mnTravelXPos = TRAVEL_X_DONTKNOW; 761 mpImpl->mpSelEngine->SelMouseMove( rMouseEvent ); 762 } 763 764 void TextView::Command( const CommandEvent& rCEvt ) 765 { 766 mpImpl->mpTextEngine->CheckIdleFormatter(); // for fast typing and MouseButtonDown 767 mpImpl->mpTextEngine->SetActiveView( this ); 768 769 if ( rCEvt.GetCommand() == CommandEventId::StartExtTextInput ) 770 { 771 DeleteSelected(); 772 TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes()[ GetSelection().GetEnd().GetPara() ].get(); 773 mpImpl->mpTextEngine->mpIMEInfos = std::make_unique<TEIMEInfos>( GetSelection().GetEnd(), pNode->GetText().copy( GetSelection().GetEnd().GetIndex() ) ); 774 mpImpl->mpTextEngine->mpIMEInfos->bWasCursorOverwrite = !IsInsertMode(); 775 } 776 else if ( rCEvt.GetCommand() == CommandEventId::EndExtTextInput ) 777 { 778 SAL_WARN_IF( !mpImpl->mpTextEngine->mpIMEInfos, "vcl", "CommandEventId::EndExtTextInput => No Start ?" ); 779 if( mpImpl->mpTextEngine->mpIMEInfos ) 780 { 781 TEParaPortion* pPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( mpImpl->mpTextEngine->mpIMEInfos->aPos.GetPara() ); 782 pPortion->MarkSelectionInvalid( mpImpl->mpTextEngine->mpIMEInfos->aPos.GetIndex() ); 783 784 bool bInsertMode = !mpImpl->mpTextEngine->mpIMEInfos->bWasCursorOverwrite; 785 786 mpImpl->mpTextEngine->mpIMEInfos.reset(); 787 788 mpImpl->mpTextEngine->TextModified(); 789 mpImpl->mpTextEngine->FormatAndUpdate( this ); 790 791 SetInsertMode( bInsertMode ); 792 793 if ( mpImpl->mpTextEngine->IsModified() ) 794 mpImpl->mpTextEngine->Broadcast( TextHint( SfxHintId::TextModified ) ); 795 } 796 } 797 else if ( rCEvt.GetCommand() == CommandEventId::ExtTextInput ) 798 { 799 SAL_WARN_IF( !mpImpl->mpTextEngine->mpIMEInfos, "vcl", "CommandEventId::ExtTextInput => No Start ?" ); 800 if( mpImpl->mpTextEngine->mpIMEInfos ) 801 { 802 const CommandExtTextInputData* pData = rCEvt.GetExtTextInputData(); 803 804 if ( !pData->IsOnlyCursorChanged() ) 805 { 806 TextSelection aSelect( mpImpl->mpTextEngine->mpIMEInfos->aPos ); 807 aSelect.GetEnd().GetIndex() += mpImpl->mpTextEngine->mpIMEInfos->nLen; 808 aSelect = mpImpl->mpTextEngine->ImpDeleteText( aSelect ); 809 aSelect = mpImpl->mpTextEngine->ImpInsertText( aSelect, pData->GetText() ); 810 811 if ( mpImpl->mpTextEngine->mpIMEInfos->bWasCursorOverwrite ) 812 { 813 const sal_Int32 nOldIMETextLen = mpImpl->mpTextEngine->mpIMEInfos->nLen; 814 const sal_Int32 nNewIMETextLen = pData->GetText().getLength(); 815 816 if ( ( nOldIMETextLen > nNewIMETextLen ) && 817 ( nNewIMETextLen < mpImpl->mpTextEngine->mpIMEInfos->aOldTextAfterStartPos.getLength() ) ) 818 { 819 // restore old characters 820 sal_Int32 nRestore = nOldIMETextLen - nNewIMETextLen; 821 TextPaM aPaM( mpImpl->mpTextEngine->mpIMEInfos->aPos ); 822 aPaM.GetIndex() += nNewIMETextLen; 823 mpImpl->mpTextEngine->ImpInsertText( aPaM, mpImpl->mpTextEngine->mpIMEInfos->aOldTextAfterStartPos.copy( nNewIMETextLen, nRestore ) ); 824 } 825 else if ( ( nOldIMETextLen < nNewIMETextLen ) && 826 ( nOldIMETextLen < mpImpl->mpTextEngine->mpIMEInfos->aOldTextAfterStartPos.getLength() ) ) 827 { 828 // overwrite 829 const sal_Int32 nOverwrite = std::min( nNewIMETextLen, mpImpl->mpTextEngine->mpIMEInfos->aOldTextAfterStartPos.getLength() ) - nOldIMETextLen; 830 SAL_WARN_IF( !nOverwrite || (nOverwrite >= 0xFF00), "vcl", "IME Overwrite?!" ); 831 TextPaM aPaM( mpImpl->mpTextEngine->mpIMEInfos->aPos ); 832 aPaM.GetIndex() += nNewIMETextLen; 833 TextSelection aSel( aPaM ); 834 aSel.GetEnd().GetIndex() += nOverwrite; 835 mpImpl->mpTextEngine->ImpDeleteText( aSel ); 836 } 837 } 838 839 if ( pData->GetTextAttr() ) 840 { 841 mpImpl->mpTextEngine->mpIMEInfos->CopyAttribs( pData->GetTextAttr(), pData->GetText().getLength() ); 842 } 843 else 844 { 845 mpImpl->mpTextEngine->mpIMEInfos->DestroyAttribs(); 846 } 847 848 TEParaPortion* pPPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( mpImpl->mpTextEngine->mpIMEInfos->aPos.GetPara() ); 849 pPPortion->MarkSelectionInvalid( mpImpl->mpTextEngine->mpIMEInfos->aPos.GetIndex() ); 850 mpImpl->mpTextEngine->FormatAndUpdate( this ); 851 } 852 853 TextSelection aNewSel = TextPaM( mpImpl->mpTextEngine->mpIMEInfos->aPos.GetPara(), mpImpl->mpTextEngine->mpIMEInfos->aPos.GetIndex()+pData->GetCursorPos() ); 854 SetSelection( aNewSel ); 855 SetInsertMode( !pData->IsCursorOverwrite() ); 856 857 if ( pData->IsCursorVisible() ) 858 ShowCursor(); 859 else 860 HideCursor(); 861 } 862 } 863 else if ( rCEvt.GetCommand() == CommandEventId::CursorPos ) 864 { 865 if ( mpImpl->mpTextEngine->mpIMEInfos && mpImpl->mpTextEngine->mpIMEInfos->nLen ) 866 { 867 TextPaM aPaM( GetSelection().GetEnd() ); 868 tools::Rectangle aR1 = mpImpl->mpTextEngine->PaMtoEditCursor( aPaM ); 869 870 sal_Int32 nInputEnd = mpImpl->mpTextEngine->mpIMEInfos->aPos.GetIndex() + mpImpl->mpTextEngine->mpIMEInfos->nLen; 871 872 if ( !mpImpl->mpTextEngine->IsFormatted() ) 873 mpImpl->mpTextEngine->FormatDoc(); 874 875 TEParaPortion* pParaPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( aPaM.GetPara() ); 876 std::vector<TextLine>::size_type nLine = pParaPortion->GetLineNumber( aPaM.GetIndex(), true ); 877 TextLine& rLine = pParaPortion->GetLines()[ nLine ]; 878 if ( nInputEnd > rLine.GetEnd() ) 879 nInputEnd = rLine.GetEnd(); 880 tools::Rectangle aR2 = mpImpl->mpTextEngine->PaMtoEditCursor( TextPaM( aPaM.GetPara(), nInputEnd ) ); 881 882 long nWidth = aR2.Left()-aR1.Right(); 883 aR1.Move( -GetStartDocPos().X(), -GetStartDocPos().Y() ); 884 GetWindow()->SetCursorRect( &aR1, nWidth ); 885 } 886 else 887 { 888 GetWindow()->SetCursorRect(); 889 } 890 } 891 else 892 { 893 mpImpl->mpSelEngine->Command( rCEvt ); 894 } 895 } 896 897 void TextView::ShowCursor( bool bGotoCursor, bool bForceVisCursor ) 898 { 899 // this setting has more weight 900 if ( !mpImpl->mbAutoScroll ) 901 bGotoCursor = false; 902 ImpShowCursor( bGotoCursor, bForceVisCursor, false ); 903 } 904 905 void TextView::HideCursor() 906 { 907 mpImpl->mpCursor->Hide(); 908 } 909 910 void TextView::Scroll( long ndX, long ndY ) 911 { 912 SAL_WARN_IF( !mpImpl->mpTextEngine->IsFormatted(), "vcl", "Scroll: Not formatted!" ); 913 914 if ( !ndX && !ndY ) 915 return; 916 917 Point aNewStartPos( mpImpl->maStartDocPos ); 918 919 // Vertical: 920 aNewStartPos.AdjustY( -ndY ); 921 if ( aNewStartPos.Y() < 0 ) 922 aNewStartPos.setY( 0 ); 923 924 // Horizontal: 925 aNewStartPos.AdjustX( -ndX ); 926 if ( aNewStartPos.X() < 0 ) 927 aNewStartPos.setX( 0 ); 928 929 long nDiffX = mpImpl->maStartDocPos.X() - aNewStartPos.X(); 930 long nDiffY = mpImpl->maStartDocPos.Y() - aNewStartPos.Y(); 931 932 if ( nDiffX || nDiffY ) 933 { 934 bool bVisCursor = mpImpl->mpCursor->IsVisible(); 935 mpImpl->mpCursor->Hide(); 936 mpImpl->mpWindow->PaintImmediately(); 937 mpImpl->maStartDocPos = aNewStartPos; 938 939 if ( mpImpl->mpTextEngine->IsRightToLeft() ) 940 nDiffX = -nDiffX; 941 mpImpl->mpWindow->Scroll( nDiffX, nDiffY ); 942 mpImpl->mpWindow->PaintImmediately(); 943 mpImpl->mpCursor->SetPos( mpImpl->mpCursor->GetPos() + Point( nDiffX, nDiffY ) ); 944 if ( bVisCursor && !mpImpl->mbReadOnly ) 945 mpImpl->mpCursor->Show(); 946 } 947 948 mpImpl->mpTextEngine->Broadcast( TextHint( SfxHintId::TextViewScrolled ) ); 949 } 950 951 void TextView::Undo() 952 { 953 mpImpl->mpTextEngine->SetActiveView( this ); 954 mpImpl->mpTextEngine->GetUndoManager().Undo(); 955 } 956 957 void TextView::Redo() 958 { 959 mpImpl->mpTextEngine->SetActiveView( this ); 960 mpImpl->mpTextEngine->GetUndoManager().Redo(); 961 } 962 963 void TextView::Cut() 964 { 965 mpImpl->mpTextEngine->UndoActionStart(); 966 Copy(); 967 DeleteSelected(); 968 mpImpl->mpTextEngine->UndoActionEnd(); 969 } 970 971 void TextView::Copy( css::uno::Reference< css::datatransfer::clipboard::XClipboard > const & rxClipboard ) 972 { 973 if ( rxClipboard.is() ) 974 { 975 TETextDataObject* pDataObj = new TETextDataObject( GetSelected() ); 976 977 SolarMutexReleaser aReleaser; 978 979 try 980 { 981 rxClipboard->setContents( pDataObj, nullptr ); 982 983 css::uno::Reference< css::datatransfer::clipboard::XFlushableClipboard > xFlushableClipboard( rxClipboard, css::uno::UNO_QUERY ); 984 if( xFlushableClipboard.is() ) 985 xFlushableClipboard->flushClipboard(); 986 } 987 catch( const css::uno::Exception& ) 988 { 989 } 990 } 991 } 992 993 void TextView::Copy() 994 { 995 css::uno::Reference<css::datatransfer::clipboard::XClipboard> aClipboard(GetWindow()->GetClipboard()); 996 Copy( aClipboard ); 997 } 998 999 void TextView::Paste( css::uno::Reference< css::datatransfer::clipboard::XClipboard > const & rxClipboard ) 1000 { 1001 if ( rxClipboard.is() ) 1002 { 1003 css::uno::Reference< css::datatransfer::XTransferable > xDataObj; 1004 1005 try 1006 { 1007 SolarMutexReleaser aReleaser; 1008 xDataObj = rxClipboard->getContents(); 1009 } 1010 catch( const css::uno::Exception& ) 1011 { 1012 } 1013 1014 if ( xDataObj.is() ) 1015 { 1016 css::datatransfer::DataFlavor aFlavor; 1017 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor ); 1018 if ( xDataObj->isDataFlavorSupported( aFlavor ) ) 1019 { 1020 try 1021 { 1022 css::uno::Any aData = xDataObj->getTransferData( aFlavor ); 1023 OUString aText; 1024 aData >>= aText; 1025 bool bWasTruncated = false; 1026 if( mpImpl->mpTextEngine->GetMaxTextLen() != 0 ) 1027 bWasTruncated = ImplTruncateNewText( aText ); 1028 InsertText( aText ); 1029 mpImpl->mpTextEngine->Broadcast( TextHint( SfxHintId::TextModified ) ); 1030 1031 if( bWasTruncated ) 1032 Edit::ShowTruncationWarning(mpImpl->mpWindow->GetFrameWeld()); 1033 } 1034 catch( const css::datatransfer::UnsupportedFlavorException& ) 1035 { 1036 } 1037 } 1038 } 1039 } 1040 } 1041 1042 void TextView::Paste() 1043 { 1044 css::uno::Reference<css::datatransfer::clipboard::XClipboard> aClipboard(GetWindow()->GetClipboard()); 1045 Paste( aClipboard ); 1046 } 1047 1048 OUString TextView::GetSelected() 1049 { 1050 return GetSelected( GetSystemLineEnd() ); 1051 } 1052 1053 OUString TextView::GetSelected( LineEnd aSeparator ) 1054 { 1055 return mpImpl->mpTextEngine->GetText( mpImpl->maSelection, aSeparator ); 1056 } 1057 1058 void TextView::SetInsertMode( bool bInsert ) 1059 { 1060 if ( mpImpl->mbInsertMode != bInsert ) 1061 { 1062 mpImpl->mbInsertMode = bInsert; 1063 ShowCursor( mpImpl->mbAutoScroll, false ); 1064 } 1065 } 1066 1067 void TextView::SetReadOnly( bool bReadOnly ) 1068 { 1069 if ( mpImpl->mbReadOnly != bReadOnly ) 1070 { 1071 mpImpl->mbReadOnly = bReadOnly; 1072 if ( !mpImpl->mbReadOnly ) 1073 ShowCursor( mpImpl->mbAutoScroll, false ); 1074 else 1075 HideCursor(); 1076 1077 GetWindow()->SetInputContext( InputContext( mpImpl->mpTextEngine->GetFont(), bReadOnly ? InputContextFlags::Text|InputContextFlags::ExtText : InputContextFlags::NONE ) ); 1078 } 1079 } 1080 1081 TextSelection const & TextView::ImpMoveCursor( const KeyEvent& rKeyEvent ) 1082 { 1083 // normally only needed for Up/Down; but who cares 1084 mpImpl->mpTextEngine->CheckIdleFormatter(); 1085 1086 TextPaM aPaM( mpImpl->maSelection.GetEnd() ); 1087 TextPaM aOldEnd( aPaM ); 1088 1089 TextDirectionality eTextDirection = TextDirectionality::LeftToRight_TopToBottom; 1090 if ( mpImpl->mpTextEngine->IsRightToLeft() ) 1091 eTextDirection = TextDirectionality::RightToLeft_TopToBottom; 1092 1093 KeyEvent aTranslatedKeyEvent = rKeyEvent.LogicalTextDirectionality( eTextDirection ); 1094 1095 bool bCtrl = aTranslatedKeyEvent.GetKeyCode().IsMod1(); 1096 sal_uInt16 nCode = aTranslatedKeyEvent.GetKeyCode().GetCode(); 1097 1098 bool bSelect = aTranslatedKeyEvent.GetKeyCode().IsShift(); 1099 switch ( nCode ) 1100 { 1101 case KEY_UP: aPaM = CursorUp( aPaM ); 1102 break; 1103 case KEY_DOWN: aPaM = CursorDown( aPaM ); 1104 break; 1105 case KEY_HOME: aPaM = bCtrl ? CursorStartOfDoc() : CursorStartOfLine( aPaM ); 1106 break; 1107 case KEY_END: aPaM = bCtrl ? CursorEndOfDoc() : CursorEndOfLine( aPaM ); 1108 break; 1109 case KEY_PAGEUP: aPaM = bCtrl ? CursorStartOfDoc() : PageUp( aPaM ); 1110 break; 1111 case KEY_PAGEDOWN: aPaM = bCtrl ? CursorEndOfDoc() : PageDown( aPaM ); 1112 break; 1113 case KEY_LEFT: aPaM = bCtrl ? CursorWordLeft( aPaM ) : CursorLeft( aPaM, aTranslatedKeyEvent.GetKeyCode().IsMod2() ? sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCHARACTER) : sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCELL) ); 1114 break; 1115 case KEY_RIGHT: aPaM = bCtrl ? CursorWordRight( aPaM ) : CursorRight( aPaM, aTranslatedKeyEvent.GetKeyCode().IsMod2() ? sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCHARACTER) : sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCELL) ); 1116 break; 1117 case css::awt::Key::SELECT_WORD_FORWARD: 1118 bSelect = true; 1119 [[fallthrough]]; 1120 case css::awt::Key::MOVE_WORD_FORWARD: 1121 aPaM = CursorWordRight( aPaM ); 1122 break; 1123 case css::awt::Key::SELECT_WORD_BACKWARD: 1124 bSelect = true; 1125 [[fallthrough]]; 1126 case css::awt::Key::MOVE_WORD_BACKWARD: 1127 aPaM = CursorWordLeft( aPaM ); 1128 break; 1129 case css::awt::Key::SELECT_TO_BEGIN_OF_LINE: 1130 bSelect = true; 1131 [[fallthrough]]; 1132 case css::awt::Key::MOVE_TO_BEGIN_OF_LINE: 1133 aPaM = CursorStartOfLine( aPaM ); 1134 break; 1135 case css::awt::Key::SELECT_TO_END_OF_LINE: 1136 bSelect = true; 1137 [[fallthrough]]; 1138 case css::awt::Key::MOVE_TO_END_OF_LINE: 1139 aPaM = CursorEndOfLine( aPaM ); 1140 break; 1141 case css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH: 1142 bSelect = true; 1143 [[fallthrough]]; 1144 case css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH: 1145 aPaM = CursorStartOfParagraph( aPaM ); 1146 break; 1147 case css::awt::Key::SELECT_TO_END_OF_PARAGRAPH: 1148 bSelect = true; 1149 [[fallthrough]]; 1150 case css::awt::Key::MOVE_TO_END_OF_PARAGRAPH: 1151 aPaM = CursorEndOfParagraph( aPaM ); 1152 break; 1153 case css::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT: 1154 bSelect = true; 1155 [[fallthrough]]; 1156 case css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT: 1157 aPaM = CursorStartOfDoc(); 1158 break; 1159 case css::awt::Key::SELECT_TO_END_OF_DOCUMENT: 1160 bSelect = true; 1161 [[fallthrough]]; 1162 case css::awt::Key::MOVE_TO_END_OF_DOCUMENT: 1163 aPaM = CursorEndOfDoc(); 1164 break; 1165 } 1166 1167 // might cause a CreateAnchor or Deselection all 1168 mpImpl->mpSelEngine->CursorPosChanging( bSelect, aTranslatedKeyEvent.GetKeyCode().IsMod1() ); 1169 1170 if ( aOldEnd != aPaM ) 1171 { 1172 mpImpl->mpTextEngine->CursorMoved( aOldEnd.GetPara() ); 1173 1174 TextSelection aNewSelection( mpImpl->maSelection ); 1175 aNewSelection.GetEnd() = aPaM; 1176 if ( bSelect ) 1177 { 1178 // extend the selection 1179 ImpSetSelection( aNewSelection ); 1180 ShowSelection( TextSelection( aOldEnd, aPaM ) ); 1181 } 1182 else 1183 { 1184 aNewSelection.GetStart() = aPaM; 1185 ImpSetSelection( aNewSelection ); 1186 } 1187 } 1188 1189 return mpImpl->maSelection; 1190 } 1191 1192 void TextView::InsertText( const OUString& rStr ) 1193 { 1194 mpImpl->mpTextEngine->UndoActionStart(); 1195 1196 TextSelection aNewSel = mpImpl->mpTextEngine->ImpInsertText( mpImpl->maSelection, rStr ); 1197 1198 ImpSetSelection( aNewSel ); 1199 1200 mpImpl->mpTextEngine->UndoActionEnd(); 1201 1202 mpImpl->mpTextEngine->FormatAndUpdate( this ); 1203 } 1204 1205 TextPaM TextView::CursorLeft( const TextPaM& rPaM, sal_uInt16 nCharacterIteratorMode ) 1206 { 1207 TextPaM aPaM( rPaM ); 1208 1209 if ( aPaM.GetIndex() ) 1210 { 1211 TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes()[ aPaM.GetPara() ].get(); 1212 css::uno::Reference < css::i18n::XBreakIterator > xBI = mpImpl->mpTextEngine->GetBreakIterator(); 1213 sal_Int32 nCount = 1; 1214 aPaM.GetIndex() = xBI->previousCharacters( pNode->GetText(), aPaM.GetIndex(), mpImpl->mpTextEngine->GetLocale(), nCharacterIteratorMode, nCount, nCount ); 1215 } 1216 else if ( aPaM.GetPara() ) 1217 { 1218 aPaM.GetPara()--; 1219 TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes()[ aPaM.GetPara() ].get(); 1220 aPaM.GetIndex() = pNode->GetText().getLength(); 1221 } 1222 return aPaM; 1223 } 1224 1225 TextPaM TextView::CursorRight( const TextPaM& rPaM, sal_uInt16 nCharacterIteratorMode ) 1226 { 1227 TextPaM aPaM( rPaM ); 1228 1229 TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes()[ aPaM.GetPara() ].get(); 1230 if ( aPaM.GetIndex() < pNode->GetText().getLength() ) 1231 { 1232 css::uno::Reference < css::i18n::XBreakIterator > xBI = mpImpl->mpTextEngine->GetBreakIterator(); 1233 sal_Int32 nCount = 1; 1234 aPaM.GetIndex() = xBI->nextCharacters( pNode->GetText(), aPaM.GetIndex(), mpImpl->mpTextEngine->GetLocale(), nCharacterIteratorMode, nCount, nCount ); 1235 } 1236 else if ( aPaM.GetPara() < ( mpImpl->mpTextEngine->mpDoc->GetNodes().size()-1) ) 1237 { 1238 aPaM.GetPara()++; 1239 aPaM.GetIndex() = 0; 1240 } 1241 1242 return aPaM; 1243 } 1244 1245 TextPaM TextView::CursorWordLeft( const TextPaM& rPaM ) 1246 { 1247 TextPaM aPaM( rPaM ); 1248 1249 if ( aPaM.GetIndex() ) 1250 { 1251 // tdf#57879 - expand selection to the left to include connector punctuations 1252 mpImpl->mpTextEngine->GetWord( rPaM, &aPaM ); 1253 if ( aPaM.GetIndex() >= rPaM.GetIndex() ) 1254 { 1255 TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes()[ aPaM.GetPara() ].get(); 1256 css::uno::Reference < css::i18n::XBreakIterator > xBI = mpImpl->mpTextEngine->GetBreakIterator(); 1257 aPaM.GetIndex() = xBI->previousWord( pNode->GetText(), rPaM.GetIndex(), mpImpl->mpTextEngine->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES ).startPos; 1258 if ( aPaM.GetIndex() > 0 ) 1259 mpImpl->mpTextEngine->GetWord( aPaM, &aPaM ); 1260 else 1261 aPaM.GetIndex() = 0; 1262 } 1263 } 1264 else if ( aPaM.GetPara() ) 1265 { 1266 aPaM.GetPara()--; 1267 TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes()[ aPaM.GetPara() ].get(); 1268 aPaM.GetIndex() = pNode->GetText().getLength(); 1269 } 1270 return aPaM; 1271 } 1272 1273 TextPaM TextView::CursorWordRight( const TextPaM& rPaM ) 1274 { 1275 TextPaM aPaM( rPaM ); 1276 1277 TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes()[ aPaM.GetPara() ].get(); 1278 if ( aPaM.GetIndex() < pNode->GetText().getLength() ) 1279 { 1280 css::uno::Reference < css::i18n::XBreakIterator > xBI = mpImpl->mpTextEngine->GetBreakIterator(); 1281 aPaM.GetIndex() = xBI->nextWord( pNode->GetText(), aPaM.GetIndex(), mpImpl->mpTextEngine->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES ).endPos; 1282 mpImpl->mpTextEngine->GetWord( aPaM, nullptr, &aPaM ); 1283 } 1284 else if ( aPaM.GetPara() < ( mpImpl->mpTextEngine->mpDoc->GetNodes().size()-1) ) 1285 { 1286 aPaM.GetPara()++; 1287 aPaM.GetIndex() = 0; 1288 } 1289 1290 return aPaM; 1291 } 1292 1293 TextPaM TextView::ImpDelete( sal_uInt8 nMode, sal_uInt8 nDelMode ) 1294 { 1295 if ( mpImpl->maSelection.HasRange() ) // only delete selection 1296 return mpImpl->mpTextEngine->ImpDeleteText( mpImpl->maSelection ); 1297 1298 TextPaM aStartPaM = mpImpl->maSelection.GetStart(); 1299 TextPaM aEndPaM = aStartPaM; 1300 if ( nMode == DEL_LEFT ) 1301 { 1302 if ( nDelMode == DELMODE_SIMPLE ) 1303 { 1304 aEndPaM = CursorLeft( aEndPaM, sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCHARACTER) ); 1305 } 1306 else if ( nDelMode == DELMODE_RESTOFWORD ) 1307 { 1308 TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes()[ aEndPaM.GetPara() ].get(); 1309 css::uno::Reference < css::i18n::XBreakIterator > xBI = mpImpl->mpTextEngine->GetBreakIterator(); 1310 css::i18n::Boundary aBoundary = xBI->getWordBoundary( pNode->GetText(), mpImpl->maSelection.GetEnd().GetIndex(), mpImpl->mpTextEngine->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES, true ); 1311 if ( aBoundary.startPos == mpImpl->maSelection.GetEnd().GetIndex() ) 1312 aBoundary = xBI->previousWord( pNode->GetText(), mpImpl->maSelection.GetEnd().GetIndex(), mpImpl->mpTextEngine->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES ); 1313 // #i63506# startPos is -1 when the paragraph starts with a tab 1314 aEndPaM.GetIndex() = std::max<sal_Int32>(aBoundary.startPos, 0); 1315 } 1316 else // DELMODE_RESTOFCONTENT 1317 { 1318 if ( aEndPaM.GetIndex() != 0 ) 1319 aEndPaM.GetIndex() = 0; 1320 else if ( aEndPaM.GetPara() ) 1321 { 1322 // previous paragraph 1323 aEndPaM.GetPara()--; 1324 aEndPaM.GetIndex() = 0; 1325 } 1326 } 1327 } 1328 else 1329 { 1330 if ( nDelMode == DELMODE_SIMPLE ) 1331 { 1332 aEndPaM = CursorRight( aEndPaM, sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCELL) ); 1333 } 1334 else if ( nDelMode == DELMODE_RESTOFWORD ) 1335 { 1336 TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes()[ aEndPaM.GetPara() ].get(); 1337 css::uno::Reference < css::i18n::XBreakIterator > xBI = mpImpl->mpTextEngine->GetBreakIterator(); 1338 css::i18n::Boundary aBoundary = xBI->nextWord( pNode->GetText(), mpImpl->maSelection.GetEnd().GetIndex(), mpImpl->mpTextEngine->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES ); 1339 aEndPaM.GetIndex() = aBoundary.startPos; 1340 } 1341 else // DELMODE_RESTOFCONTENT 1342 { 1343 TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes()[ aEndPaM.GetPara() ].get(); 1344 if ( aEndPaM.GetIndex() < pNode->GetText().getLength() ) 1345 aEndPaM.GetIndex() = pNode->GetText().getLength(); 1346 else if ( aEndPaM.GetPara() < ( mpImpl->mpTextEngine->mpDoc->GetNodes().size() - 1 ) ) 1347 { 1348 // next paragraph 1349 aEndPaM.GetPara()++; 1350 TextNode* pNextNode = mpImpl->mpTextEngine->mpDoc->GetNodes()[ aEndPaM.GetPara() ].get(); 1351 aEndPaM.GetIndex() = pNextNode->GetText().getLength(); 1352 } 1353 } 1354 } 1355 1356 return mpImpl->mpTextEngine->ImpDeleteText( TextSelection( aStartPaM, aEndPaM ) ); 1357 } 1358 1359 TextPaM TextView::CursorUp( const TextPaM& rPaM ) 1360 { 1361 TextPaM aPaM( rPaM ); 1362 1363 long nX; 1364 if ( mpImpl->mnTravelXPos == TRAVEL_X_DONTKNOW ) 1365 { 1366 nX = mpImpl->mpTextEngine->GetEditCursor( rPaM, false ).Left(); 1367 mpImpl->mnTravelXPos = static_cast<sal_uInt16>(nX)+1; 1368 } 1369 else 1370 nX = mpImpl->mnTravelXPos; 1371 1372 TEParaPortion* pPPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( rPaM.GetPara() ); 1373 std::vector<TextLine>::size_type nLine = pPPortion->GetLineNumber( rPaM.GetIndex(), false ); 1374 if ( nLine ) // same paragraph 1375 { 1376 aPaM.GetIndex() = mpImpl->mpTextEngine->GetCharPos( rPaM.GetPara(), nLine-1, nX ); 1377 // If we need to go to the end of a line that was wrapped automatically, 1378 // the cursor ends up at the beginning of the 2nd line 1379 // Problem: Last character of an automatically wrapped line = Cursor 1380 TextLine& rLine = pPPortion->GetLines()[ nLine - 1 ]; 1381 if ( aPaM.GetIndex() && ( aPaM.GetIndex() == rLine.GetEnd() ) ) 1382 --aPaM.GetIndex(); 1383 } 1384 else if ( rPaM.GetPara() ) // previous paragraph 1385 { 1386 aPaM.GetPara()--; 1387 pPPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( aPaM.GetPara() ); 1388 std::vector<TextLine>::size_type nL = pPPortion->GetLines().size() - 1; 1389 aPaM.GetIndex() = mpImpl->mpTextEngine->GetCharPos( aPaM.GetPara(), nL, nX+1 ); 1390 } 1391 1392 return aPaM; 1393 } 1394 1395 TextPaM TextView::CursorDown( const TextPaM& rPaM ) 1396 { 1397 TextPaM aPaM( rPaM ); 1398 1399 long nX; 1400 if ( mpImpl->mnTravelXPos == TRAVEL_X_DONTKNOW ) 1401 { 1402 nX = mpImpl->mpTextEngine->GetEditCursor( rPaM, false ).Left(); 1403 mpImpl->mnTravelXPos = static_cast<sal_uInt16>(nX)+1; 1404 } 1405 else 1406 nX = mpImpl->mnTravelXPos; 1407 1408 TEParaPortion* pPPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( rPaM.GetPara() ); 1409 std::vector<TextLine>::size_type nLine = pPPortion->GetLineNumber( rPaM.GetIndex(), false ); 1410 if ( nLine < ( pPPortion->GetLines().size() - 1 ) ) 1411 { 1412 aPaM.GetIndex() = mpImpl->mpTextEngine->GetCharPos( rPaM.GetPara(), nLine+1, nX ); 1413 1414 // special case CursorUp 1415 TextLine& rLine = pPPortion->GetLines()[ nLine + 1 ]; 1416 if ( ( aPaM.GetIndex() == rLine.GetEnd() ) && ( aPaM.GetIndex() > rLine.GetStart() ) && aPaM.GetIndex() < pPPortion->GetNode()->GetText().getLength() ) 1417 --aPaM.GetIndex(); 1418 } 1419 else if ( rPaM.GetPara() < ( mpImpl->mpTextEngine->mpDoc->GetNodes().size() - 1 ) ) // next paragraph 1420 { 1421 aPaM.GetPara()++; 1422 pPPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( aPaM.GetPara() ); 1423 aPaM.GetIndex() = mpImpl->mpTextEngine->GetCharPos( aPaM.GetPara(), 0, nX+1 ); 1424 TextLine& rLine = pPPortion->GetLines().front(); 1425 if ( ( aPaM.GetIndex() == rLine.GetEnd() ) && ( aPaM.GetIndex() > rLine.GetStart() ) && ( pPPortion->GetLines().size() > 1 ) ) 1426 --aPaM.GetIndex(); 1427 } 1428 1429 return aPaM; 1430 } 1431 1432 TextPaM TextView::CursorStartOfLine( const TextPaM& rPaM ) 1433 { 1434 TextPaM aPaM( rPaM ); 1435 1436 TEParaPortion* pPPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( rPaM.GetPara() ); 1437 std::vector<TextLine>::size_type nLine = pPPortion->GetLineNumber( aPaM.GetIndex(), false ); 1438 TextLine& rLine = pPPortion->GetLines()[ nLine ]; 1439 aPaM.GetIndex() = rLine.GetStart(); 1440 1441 return aPaM; 1442 } 1443 1444 TextPaM TextView::CursorEndOfLine( const TextPaM& rPaM ) 1445 { 1446 TextPaM aPaM( rPaM ); 1447 1448 TEParaPortion* pPPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( rPaM.GetPara() ); 1449 std::vector<TextLine>::size_type nLine = pPPortion->GetLineNumber( aPaM.GetIndex(), false ); 1450 TextLine& rLine = pPPortion->GetLines()[ nLine ]; 1451 aPaM.GetIndex() = rLine.GetEnd(); 1452 1453 if ( rLine.GetEnd() > rLine.GetStart() ) // empty line 1454 { 1455 sal_Unicode cLastChar = pPPortion->GetNode()->GetText()[ aPaM.GetIndex()-1 ]; 1456 if ( ( cLastChar == ' ' ) && ( aPaM.GetIndex() != pPPortion->GetNode()->GetText().getLength() ) ) 1457 { 1458 // for a blank in an automatically-wrapped line it is better to stand before it, 1459 // as the user will intend to stand behind the prior word. 1460 // If there is a change, special case for Pos1 after End! 1461 --aPaM.GetIndex(); 1462 } 1463 } 1464 return aPaM; 1465 } 1466 1467 TextPaM TextView::CursorStartOfParagraph( const TextPaM& rPaM ) 1468 { 1469 TextPaM aPaM( rPaM ); 1470 aPaM.GetIndex() = 0; 1471 return aPaM; 1472 } 1473 1474 TextPaM TextView::CursorEndOfParagraph( const TextPaM& rPaM ) 1475 { 1476 TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes()[ rPaM.GetPara() ].get(); 1477 TextPaM aPaM( rPaM ); 1478 aPaM.GetIndex() = pNode->GetText().getLength(); 1479 return aPaM; 1480 } 1481 1482 TextPaM TextView::CursorStartOfDoc() 1483 { 1484 TextPaM aPaM( 0, 0 ); 1485 return aPaM; 1486 } 1487 1488 TextPaM TextView::CursorEndOfDoc() 1489 { 1490 const sal_uInt32 nNode = static_cast<sal_uInt32>(mpImpl->mpTextEngine->mpDoc->GetNodes().size() - 1); 1491 TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes()[ nNode ].get(); 1492 TextPaM aPaM( nNode, pNode->GetText().getLength() ); 1493 return aPaM; 1494 } 1495 1496 TextPaM TextView::PageUp( const TextPaM& rPaM ) 1497 { 1498 tools::Rectangle aRect = mpImpl->mpTextEngine->PaMtoEditCursor( rPaM ); 1499 Point aTopLeft = aRect.TopLeft(); 1500 aTopLeft.AdjustY( -(mpImpl->mpWindow->GetOutputSizePixel().Height() * 9/10) ); 1501 aTopLeft.AdjustX(1 ); 1502 if ( aTopLeft.Y() < 0 ) 1503 aTopLeft.setY( 0 ); 1504 1505 TextPaM aPaM = mpImpl->mpTextEngine->GetPaM( aTopLeft ); 1506 return aPaM; 1507 } 1508 1509 TextPaM TextView::PageDown( const TextPaM& rPaM ) 1510 { 1511 tools::Rectangle aRect = mpImpl->mpTextEngine->PaMtoEditCursor( rPaM ); 1512 Point aBottomRight = aRect.BottomRight(); 1513 aBottomRight.AdjustY(mpImpl->mpWindow->GetOutputSizePixel().Height() * 9/10 ); 1514 aBottomRight.AdjustX(1 ); 1515 long nHeight = mpImpl->mpTextEngine->GetTextHeight(); 1516 if ( aBottomRight.Y() > nHeight ) 1517 aBottomRight.setY( nHeight-1 ); 1518 1519 TextPaM aPaM = mpImpl->mpTextEngine->GetPaM( aBottomRight ); 1520 return aPaM; 1521 } 1522 1523 void TextView::ImpShowCursor( bool bGotoCursor, bool bForceVisCursor, bool bSpecial ) 1524 { 1525 if ( mpImpl->mpTextEngine->IsFormatting() ) 1526 return; 1527 if ( !mpImpl->mpTextEngine->GetUpdateMode() ) 1528 return; 1529 if ( mpImpl->mpTextEngine->IsInUndo() ) 1530 return; 1531 1532 mpImpl->mpTextEngine->CheckIdleFormatter(); 1533 if ( !mpImpl->mpTextEngine->IsFormatted() ) 1534 mpImpl->mpTextEngine->FormatAndUpdate( this ); 1535 1536 TextPaM aPaM( mpImpl->maSelection.GetEnd() ); 1537 tools::Rectangle aEditCursor = mpImpl->mpTextEngine->PaMtoEditCursor( aPaM, bSpecial ); 1538 1539 // Remember that we placed the cursor behind the last character of a line 1540 mpImpl->mbCursorAtEndOfLine = false; 1541 if( bSpecial ) 1542 { 1543 TEParaPortion* pParaPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( aPaM.GetPara() ); 1544 mpImpl->mbCursorAtEndOfLine = 1545 pParaPortion->GetLineNumber( aPaM.GetIndex(), true ) != pParaPortion->GetLineNumber( aPaM.GetIndex(), false ); 1546 } 1547 1548 if ( !IsInsertMode() && !mpImpl->maSelection.HasRange() ) 1549 { 1550 TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes()[ aPaM.GetPara() ].get(); 1551 if ( !pNode->GetText().isEmpty() && ( aPaM.GetIndex() < pNode->GetText().getLength() ) ) 1552 { 1553 // If we are behind a portion, and the next portion has other direction, we must change position... 1554 aEditCursor.SetLeft( mpImpl->mpTextEngine->GetEditCursor( aPaM, false, true ).Left() ); 1555 aEditCursor.SetRight( aEditCursor.Left() ); 1556 1557 TEParaPortion* pParaPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( aPaM.GetPara() ); 1558 1559 sal_Int32 nTextPortionStart = 0; 1560 std::size_t nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nTextPortionStart, true ); 1561 TETextPortion* pTextPortion = pParaPortion->GetTextPortions()[ nTextPortion ]; 1562 if ( pTextPortion->GetKind() == PORTIONKIND_TAB ) 1563 { 1564 aEditCursor.AdjustRight(pTextPortion->GetWidth() ); 1565 } 1566 else 1567 { 1568 TextPaM aNext = CursorRight( TextPaM( aPaM.GetPara(), aPaM.GetIndex() ), sal_uInt16(css::i18n::CharacterIteratorMode::SKIPCELL) ); 1569 aEditCursor.SetRight( mpImpl->mpTextEngine->GetEditCursor( aNext, true ).Left() ); 1570 } 1571 } 1572 } 1573 1574 Size aOutSz = mpImpl->mpWindow->GetOutputSizePixel(); 1575 if ( aEditCursor.GetHeight() > aOutSz.Height() ) 1576 aEditCursor.SetBottom( aEditCursor.Top() + aOutSz.Height() - 1 ); 1577 1578 aEditCursor.AdjustLeft( -1 ); 1579 1580 if ( bGotoCursor 1581 // #i81283# protect maStartDocPos against initialization problems 1582 && aOutSz.Width() && aOutSz.Height() 1583 ) 1584 { 1585 long nVisStartY = mpImpl->maStartDocPos.Y(); 1586 long nVisEndY = mpImpl->maStartDocPos.Y() + aOutSz.Height(); 1587 long nVisStartX = mpImpl->maStartDocPos.X(); 1588 long nVisEndX = mpImpl->maStartDocPos.X() + aOutSz.Width(); 1589 long nMoreX = aOutSz.Width() / 4; 1590 1591 Point aNewStartPos( mpImpl->maStartDocPos ); 1592 1593 if ( aEditCursor.Bottom() > nVisEndY ) 1594 { 1595 aNewStartPos.AdjustY( aEditCursor.Bottom() - nVisEndY); 1596 } 1597 else if ( aEditCursor.Top() < nVisStartY ) 1598 { 1599 aNewStartPos.AdjustY( -( nVisStartY - aEditCursor.Top() ) ); 1600 } 1601 1602 if ( aEditCursor.Right() >= nVisEndX ) 1603 { 1604 aNewStartPos.AdjustX( aEditCursor.Right() - nVisEndX ); 1605 1606 // do you want some more? 1607 aNewStartPos.AdjustX(nMoreX ); 1608 } 1609 else if ( aEditCursor.Left() <= nVisStartX ) 1610 { 1611 aNewStartPos.AdjustX( -( nVisStartX - aEditCursor.Left() ) ); 1612 1613 // do you want some more? 1614 aNewStartPos.AdjustX( -nMoreX ); 1615 } 1616 1617 // X can be wrong for the 'some more' above: 1618 // sal_uLong nMaxTextWidth = mpImpl->mpTextEngine->GetMaxTextWidth(); 1619 // if ( !nMaxTextWidth || ( nMaxTextWidth > 0x7FFFFFFF ) ) 1620 // nMaxTextWidth = 0x7FFFFFFF; 1621 // long nMaxX = (long)nMaxTextWidth - aOutSz.Width(); 1622 long nMaxX = mpImpl->mpTextEngine->CalcTextWidth() - aOutSz.Width(); 1623 if ( nMaxX < 0 ) 1624 nMaxX = 0; 1625 1626 if ( aNewStartPos.X() < 0 ) 1627 aNewStartPos.setX( 0 ); 1628 else if ( aNewStartPos.X() > nMaxX ) 1629 aNewStartPos.setX( nMaxX ); 1630 1631 // Y should not be further down than needed 1632 long nYMax = mpImpl->mpTextEngine->GetTextHeight() - aOutSz.Height(); 1633 if ( nYMax < 0 ) 1634 nYMax = 0; 1635 if ( aNewStartPos.Y() > nYMax ) 1636 aNewStartPos.setY( nYMax ); 1637 1638 if ( aNewStartPos != mpImpl->maStartDocPos ) 1639 Scroll( -(aNewStartPos.X() - mpImpl->maStartDocPos.X()), -(aNewStartPos.Y() - mpImpl->maStartDocPos.Y()) ); 1640 } 1641 1642 if ( aEditCursor.Right() < aEditCursor.Left() ) 1643 { 1644 long n = aEditCursor.Left(); 1645 aEditCursor.SetLeft( aEditCursor.Right() ); 1646 aEditCursor.SetRight( n ); 1647 } 1648 1649 Point aPoint( GetWindowPos( !mpImpl->mpTextEngine->IsRightToLeft() ? aEditCursor.TopLeft() : aEditCursor.TopRight() ) ); 1650 mpImpl->mpCursor->SetPos( aPoint ); 1651 mpImpl->mpCursor->SetSize( aEditCursor.GetSize() ); 1652 if ( bForceVisCursor && mpImpl->mbCursorEnabled ) 1653 mpImpl->mpCursor->Show(); 1654 } 1655 1656 void TextView::SetCursorAtPoint( const Point& rPosPixel ) 1657 { 1658 mpImpl->mpTextEngine->CheckIdleFormatter(); 1659 1660 Point aDocPos = GetDocPos( rPosPixel ); 1661 1662 TextPaM aPaM = mpImpl->mpTextEngine->GetPaM( aDocPos ); 1663 1664 // aTmpNewSel: Diff between old and new; not the new selection 1665 TextSelection aTmpNewSel( mpImpl->maSelection.GetEnd(), aPaM ); 1666 TextSelection aNewSel( mpImpl->maSelection ); 1667 aNewSel.GetEnd() = aPaM; 1668 1669 if ( !mpImpl->mpSelEngine->HasAnchor() ) 1670 { 1671 if ( mpImpl->maSelection.GetStart() != aPaM ) 1672 mpImpl->mpTextEngine->CursorMoved( mpImpl->maSelection.GetStart().GetPara() ); 1673 aNewSel.GetStart() = aPaM; 1674 ImpSetSelection( aNewSel ); 1675 } 1676 else 1677 { 1678 ImpSetSelection( aNewSel ); 1679 ShowSelection( aTmpNewSel ); 1680 } 1681 1682 bool bForceCursor = !mpImpl->mpDDInfo; // && !mbInSelection 1683 ImpShowCursor( mpImpl->mbAutoScroll, bForceCursor, false ); 1684 } 1685 1686 bool TextView::IsSelectionAtPoint( const Point& rPosPixel ) 1687 { 1688 Point aDocPos = GetDocPos( rPosPixel ); 1689 TextPaM aPaM = mpImpl->mpTextEngine->GetPaM( aDocPos ); 1690 // BeginDrag is only called, however, if IsSelectionAtPoint() 1691 // Problem: IsSelectionAtPoint is not called by Command() 1692 // if before MBDown returned false. 1693 return IsInSelection( aPaM ); 1694 } 1695 1696 bool TextView::IsInSelection( const TextPaM& rPaM ) 1697 { 1698 TextSelection aSel = mpImpl->maSelection; 1699 aSel.Justify(); 1700 1701 const sal_uInt32 nStartNode = aSel.GetStart().GetPara(); 1702 const sal_uInt32 nEndNode = aSel.GetEnd().GetPara(); 1703 const sal_uInt32 nCurNode = rPaM.GetPara(); 1704 1705 if ( ( nCurNode > nStartNode ) && ( nCurNode < nEndNode ) ) 1706 return true; 1707 1708 if ( nStartNode == nEndNode ) 1709 { 1710 if ( nCurNode == nStartNode ) 1711 if ( ( rPaM.GetIndex() >= aSel.GetStart().GetIndex() ) && ( rPaM.GetIndex() < aSel.GetEnd().GetIndex() ) ) 1712 return true; 1713 } 1714 else if ( ( nCurNode == nStartNode ) && ( rPaM.GetIndex() >= aSel.GetStart().GetIndex() ) ) 1715 return true; 1716 else if ( ( nCurNode == nEndNode ) && ( rPaM.GetIndex() < aSel.GetEnd().GetIndex() ) ) 1717 return true; 1718 1719 return false; 1720 } 1721 1722 void TextView::ImpHideDDCursor() 1723 { 1724 if ( mpImpl->mpDDInfo && mpImpl->mpDDInfo->mbVisCursor ) 1725 { 1726 mpImpl->mpDDInfo->maCursor.Hide(); 1727 mpImpl->mpDDInfo->mbVisCursor = false; 1728 } 1729 } 1730 1731 void TextView::ImpShowDDCursor() 1732 { 1733 if ( !mpImpl->mpDDInfo->mbVisCursor ) 1734 { 1735 tools::Rectangle aCursor = mpImpl->mpTextEngine->PaMtoEditCursor( mpImpl->mpDDInfo->maDropPos, true ); 1736 aCursor.AdjustRight( 1 ); 1737 aCursor.SetPos( GetWindowPos( aCursor.TopLeft() ) ); 1738 1739 mpImpl->mpDDInfo->maCursor.SetWindow( mpImpl->mpWindow ); 1740 mpImpl->mpDDInfo->maCursor.SetPos( aCursor.TopLeft() ); 1741 mpImpl->mpDDInfo->maCursor.SetSize( aCursor.GetSize() ); 1742 mpImpl->mpDDInfo->maCursor.Show(); 1743 mpImpl->mpDDInfo->mbVisCursor = true; 1744 } 1745 } 1746 1747 void TextView::SetPaintSelection( bool bPaint ) 1748 { 1749 if ( bPaint != mpImpl->mbPaintSelection ) 1750 { 1751 mpImpl->mbPaintSelection = bPaint; 1752 ShowSelection( mpImpl->maSelection ); 1753 } 1754 } 1755 1756 void TextView::Read( SvStream& rInput ) 1757 { 1758 mpImpl->mpTextEngine->Read( rInput, &mpImpl->maSelection ); 1759 ShowCursor(); 1760 } 1761 1762 bool TextView::ImplTruncateNewText( OUString& rNewText ) const 1763 { 1764 bool bTruncated = false; 1765 1766 const sal_Int32 nMaxLen = mpImpl->mpTextEngine->GetMaxTextLen(); 1767 // 0 means unlimited 1768 if( nMaxLen != 0 ) 1769 { 1770 const sal_Int32 nCurLen = mpImpl->mpTextEngine->GetTextLen(); 1771 1772 const sal_Int32 nNewLen = rNewText.getLength(); 1773 if ( nCurLen + nNewLen > nMaxLen ) 1774 { 1775 // see how much text will be replaced 1776 const sal_Int32 nSelLen = mpImpl->mpTextEngine->GetTextLen( mpImpl->maSelection ); 1777 if ( nCurLen + nNewLen - nSelLen > nMaxLen ) 1778 { 1779 const sal_Int32 nTruncatedLen = nMaxLen - (nCurLen - nSelLen); 1780 rNewText = rNewText.copy( 0, nTruncatedLen ); 1781 bTruncated = true; 1782 } 1783 } 1784 } 1785 return bTruncated; 1786 } 1787 1788 bool TextView::ImplCheckTextLen( const OUString& rNewText ) 1789 { 1790 bool bOK = true; 1791 if ( mpImpl->mpTextEngine->GetMaxTextLen() ) 1792 { 1793 sal_Int32 n = mpImpl->mpTextEngine->GetTextLen() + rNewText.getLength(); 1794 if ( n > mpImpl->mpTextEngine->GetMaxTextLen() ) 1795 { 1796 // calculate how much text is being deleted 1797 n -= mpImpl->mpTextEngine->GetTextLen( mpImpl->maSelection ); 1798 if ( n > mpImpl->mpTextEngine->GetMaxTextLen() ) 1799 bOK = false; 1800 } 1801 } 1802 return bOK; 1803 } 1804 1805 void TextView::dragGestureRecognized( const css::datatransfer::dnd::DragGestureEvent& rDGE ) 1806 { 1807 if ( mpImpl->mbClickedInSelection ) 1808 { 1809 SolarMutexGuard aVclGuard; 1810 1811 SAL_WARN_IF( !mpImpl->maSelection.HasRange(), "vcl", "TextView::dragGestureRecognized: mpImpl->mbClickedInSelection, but no selection?" ); 1812 1813 mpImpl->mpDDInfo.reset(new TextDDInfo); 1814 mpImpl->mpDDInfo->mbStarterOfDD = true; 1815 1816 TETextDataObject* pDataObj = new TETextDataObject( GetSelected() ); 1817 1818 mpImpl->mpCursor->Hide(); 1819 1820 sal_Int8 nActions = css::datatransfer::dnd::DNDConstants::ACTION_COPY; 1821 if ( !IsReadOnly() ) 1822 nActions |= css::datatransfer::dnd::DNDConstants::ACTION_MOVE; 1823 rDGE.DragSource->startDrag( rDGE, nActions, 0 /*cursor*/, 0 /*image*/, pDataObj, mpImpl->mxDnDListener ); 1824 } 1825 } 1826 1827 void TextView::dragDropEnd( const css::datatransfer::dnd::DragSourceDropEvent& ) 1828 { 1829 ImpHideDDCursor(); 1830 mpImpl->mpDDInfo.reset(); 1831 } 1832 1833 void TextView::drop( const css::datatransfer::dnd::DropTargetDropEvent& rDTDE ) 1834 { 1835 SolarMutexGuard aVclGuard; 1836 1837 if ( !mpImpl->mbReadOnly && mpImpl->mpDDInfo ) 1838 { 1839 ImpHideDDCursor(); 1840 1841 // Data for deleting after DROP_MOVE: 1842 TextSelection aPrevSel( mpImpl->maSelection ); 1843 aPrevSel.Justify(); 1844 const sal_uInt32 nPrevParaCount = mpImpl->mpTextEngine->GetParagraphCount(); 1845 const sal_Int32 nPrevStartParaLen = mpImpl->mpTextEngine->GetTextLen( aPrevSel.GetStart().GetPara() ); 1846 1847 bool bStarterOfDD = false; 1848 for ( sal_uInt16 nView = mpImpl->mpTextEngine->GetViewCount(); nView && !bStarterOfDD; ) 1849 bStarterOfDD = mpImpl->mpTextEngine->GetView( --nView )->mpImpl->mpDDInfo && mpImpl->mpTextEngine->GetView( nView )->mpImpl->mpDDInfo->mbStarterOfDD; 1850 1851 HideSelection(); 1852 ImpSetSelection( mpImpl->mpDDInfo->maDropPos ); 1853 1854 mpImpl->mpTextEngine->UndoActionStart(); 1855 1856 OUString aText; 1857 css::uno::Reference< css::datatransfer::XTransferable > xDataObj = rDTDE.Transferable; 1858 if ( xDataObj.is() ) 1859 { 1860 css::datatransfer::DataFlavor aFlavor; 1861 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor ); 1862 if ( xDataObj->isDataFlavorSupported( aFlavor ) ) 1863 { 1864 css::uno::Any aData = xDataObj->getTransferData( aFlavor ); 1865 OUString aOUString; 1866 aData >>= aOUString; 1867 aText = convertLineEnd(aOUString, LINEEND_LF); 1868 } 1869 } 1870 1871 if ( !aText.isEmpty() && ( aText[ aText.getLength()-1 ] == LINE_SEP ) ) 1872 aText = aText.copy(0, aText.getLength()-1); 1873 1874 if ( ImplCheckTextLen( aText ) ) 1875 ImpSetSelection( mpImpl->mpTextEngine->ImpInsertText( mpImpl->mpDDInfo->maDropPos, aText ) ); 1876 1877 if ( aPrevSel.HasRange() && 1878 (( rDTDE.DropAction & css::datatransfer::dnd::DNDConstants::ACTION_MOVE ) || !bStarterOfDD) ) 1879 { 1880 // adjust selection if necessary 1881 if ( ( mpImpl->mpDDInfo->maDropPos.GetPara() < aPrevSel.GetStart().GetPara() ) || 1882 ( ( mpImpl->mpDDInfo->maDropPos.GetPara() == aPrevSel.GetStart().GetPara() ) 1883 && ( mpImpl->mpDDInfo->maDropPos.GetIndex() < aPrevSel.GetStart().GetIndex() ) ) ) 1884 { 1885 const sal_uInt32 nNewParasBeforeSelection = 1886 mpImpl->mpTextEngine->GetParagraphCount() - nPrevParaCount; 1887 1888 aPrevSel.GetStart().GetPara() += nNewParasBeforeSelection; 1889 aPrevSel.GetEnd().GetPara() += nNewParasBeforeSelection; 1890 1891 if ( mpImpl->mpDDInfo->maDropPos.GetPara() == aPrevSel.GetStart().GetPara() ) 1892 { 1893 const sal_Int32 nNewChars = 1894 mpImpl->mpTextEngine->GetTextLen( aPrevSel.GetStart().GetPara() ) - nPrevStartParaLen; 1895 1896 aPrevSel.GetStart().GetIndex() += nNewChars; 1897 if ( aPrevSel.GetStart().GetPara() == aPrevSel.GetEnd().GetPara() ) 1898 aPrevSel.GetEnd().GetIndex() += nNewChars; 1899 } 1900 } 1901 else 1902 { 1903 // adjust current selection 1904 TextPaM aPaM = mpImpl->maSelection.GetStart(); 1905 aPaM.GetPara() -= ( aPrevSel.GetEnd().GetPara() - aPrevSel.GetStart().GetPara() ); 1906 if ( aPrevSel.GetEnd().GetPara() == mpImpl->mpDDInfo->maDropPos.GetPara() ) 1907 { 1908 aPaM.GetIndex() -= aPrevSel.GetEnd().GetIndex(); 1909 if ( aPrevSel.GetStart().GetPara() == mpImpl->mpDDInfo->maDropPos.GetPara() ) 1910 aPaM.GetIndex() += aPrevSel.GetStart().GetIndex(); 1911 } 1912 ImpSetSelection( aPaM ); 1913 1914 } 1915 mpImpl->mpTextEngine->ImpDeleteText( aPrevSel ); 1916 } 1917 1918 mpImpl->mpTextEngine->UndoActionEnd(); 1919 1920 mpImpl->mpDDInfo.reset(); 1921 1922 mpImpl->mpTextEngine->FormatAndUpdate( this ); 1923 1924 mpImpl->mpTextEngine->Broadcast( TextHint( SfxHintId::TextModified ) ); 1925 } 1926 rDTDE.Context->dropComplete( false/*bChanges*/ ); 1927 } 1928 1929 void TextView::dragEnter( const css::datatransfer::dnd::DropTargetDragEnterEvent& ) 1930 { 1931 } 1932 1933 void TextView::dragExit( const css::datatransfer::dnd::DropTargetEvent& ) 1934 { 1935 SolarMutexGuard aVclGuard; 1936 ImpHideDDCursor(); 1937 } 1938 1939 void TextView::dragOver( const css::datatransfer::dnd::DropTargetDragEvent& rDTDE ) 1940 { 1941 SolarMutexGuard aVclGuard; 1942 1943 if (!mpImpl->mpDDInfo) 1944 mpImpl->mpDDInfo.reset(new TextDDInfo); 1945 1946 TextPaM aPrevDropPos = mpImpl->mpDDInfo->maDropPos; 1947 Point aMousePos( rDTDE.LocationX, rDTDE.LocationY ); 1948 Point aDocPos = GetDocPos( aMousePos ); 1949 mpImpl->mpDDInfo->maDropPos = mpImpl->mpTextEngine->GetPaM( aDocPos ); 1950 1951 // Don't drop in selection or in read only engine 1952 if ( IsReadOnly() || IsInSelection( mpImpl->mpDDInfo->maDropPos )) 1953 { 1954 ImpHideDDCursor(); 1955 rDTDE.Context->rejectDrag(); 1956 } 1957 else 1958 { 1959 // delete old Cursor 1960 if ( !mpImpl->mpDDInfo->mbVisCursor || ( aPrevDropPos != mpImpl->mpDDInfo->maDropPos ) ) 1961 { 1962 ImpHideDDCursor(); 1963 ImpShowDDCursor(); 1964 } 1965 rDTDE.Context->acceptDrag( rDTDE.DropAction ); 1966 } 1967 } 1968 1969 Point TextView::ImpGetOutputStartPos( const Point& rStartDocPos ) const 1970 { 1971 Point aStartPos( -rStartDocPos.X(), -rStartDocPos.Y() ); 1972 if ( mpImpl->mpTextEngine->IsRightToLeft() ) 1973 { 1974 Size aSz = mpImpl->mpWindow->GetOutputSizePixel(); 1975 aStartPos.setX( rStartDocPos.X() + aSz.Width() - 1 ); // -1: Start is 0 1976 } 1977 return aStartPos; 1978 } 1979 1980 Point TextView::GetDocPos( const Point& rWindowPos ) const 1981 { 1982 // Window Position => Document Position 1983 1984 Point aPoint; 1985 1986 aPoint.setY( rWindowPos.Y() + mpImpl->maStartDocPos.Y() ); 1987 1988 if ( !mpImpl->mpTextEngine->IsRightToLeft() ) 1989 { 1990 aPoint.setX( rWindowPos.X() + mpImpl->maStartDocPos.X() ); 1991 } 1992 else 1993 { 1994 Size aSz = mpImpl->mpWindow->GetOutputSizePixel(); 1995 aPoint.setX( ( aSz.Width() - 1 ) - rWindowPos.X() + mpImpl->maStartDocPos.X() ); 1996 } 1997 1998 return aPoint; 1999 } 2000 2001 Point TextView::GetWindowPos( const Point& rDocPos ) const 2002 { 2003 // Document Position => Window Position 2004 2005 Point aPoint; 2006 2007 aPoint.setY( rDocPos.Y() - mpImpl->maStartDocPos.Y() ); 2008 2009 if ( !mpImpl->mpTextEngine->IsRightToLeft() ) 2010 { 2011 aPoint.setX( rDocPos.X() - mpImpl->maStartDocPos.X() ); 2012 } 2013 else 2014 { 2015 Size aSz = mpImpl->mpWindow->GetOutputSizePixel(); 2016 aPoint.setX( ( aSz.Width() - 1 ) - ( rDocPos.X() - mpImpl->maStartDocPos.X() ) ); 2017 } 2018 2019 return aPoint; 2020 } 2021 2022 sal_Int32 TextView::GetLineNumberOfCursorInSelection() const 2023 { 2024 // PROGRESS 2025 sal_Int32 nLineNo = -1; 2026 if( mpImpl->mbCursorEnabled ) 2027 { 2028 TextPaM aPaM = GetSelection().GetEnd(); 2029 TEParaPortion* pPPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( aPaM.GetPara() ); 2030 nLineNo = pPPortion->GetLineNumber( aPaM.GetIndex(), false ); 2031 //TODO: std::vector<TextLine>::size_type -> sal_Int32! 2032 if( mpImpl->mbCursorAtEndOfLine ) 2033 --nLineNo; 2034 } 2035 return nLineNo; 2036 } 2037 2038 // (+) class TextSelFunctionSet 2039 2040 TextSelFunctionSet::TextSelFunctionSet( TextView* pView ) 2041 { 2042 mpView = pView; 2043 } 2044 2045 void TextSelFunctionSet::BeginDrag() 2046 { 2047 } 2048 2049 void TextSelFunctionSet::CreateAnchor() 2050 { 2051 // TextSelection aSel( mpView->GetSelection() ); 2052 // aSel.GetStart() = aSel.GetEnd(); 2053 // mpView->SetSelection( aSel ); 2054 2055 // may not be followed by ShowCursor 2056 mpView->HideSelection(); 2057 mpView->ImpSetSelection( mpView->mpImpl->maSelection.GetEnd() ); 2058 } 2059 2060 void TextSelFunctionSet::SetCursorAtPoint( const Point& rPointPixel, bool ) 2061 { 2062 mpView->SetCursorAtPoint( rPointPixel ); 2063 } 2064 2065 bool TextSelFunctionSet::IsSelectionAtPoint( const Point& rPointPixel ) 2066 { 2067 return mpView->IsSelectionAtPoint( rPointPixel ); 2068 } 2069 2070 void TextSelFunctionSet::DeselectAll() 2071 { 2072 CreateAnchor(); 2073 } 2074 2075 void TextSelFunctionSet::DeselectAtPoint( const Point& ) 2076 { 2077 // only for multiple selection 2078 } 2079 2080 void TextSelFunctionSet::DestroyAnchor() 2081 { 2082 // only for multiple selection 2083 } 2084 TextEngine* TextView::GetTextEngine() const 2085 { return mpImpl->mpTextEngine; } 2086 vcl::Window* TextView::GetWindow() const 2087 { return mpImpl->mpWindow; } 2088 void TextView::EnableCursor( bool bEnable ) 2089 { mpImpl->mbCursorEnabled = bEnable; } 2090 bool TextView::IsCursorEnabled() const 2091 { return mpImpl->mbCursorEnabled; } 2092 void TextView::SetStartDocPos( const Point& rPos ) 2093 { mpImpl->maStartDocPos = rPos; } 2094 const Point& TextView::GetStartDocPos() const 2095 { return mpImpl->maStartDocPos; } 2096 void TextView::SetAutoIndentMode( bool bAutoIndent ) 2097 { mpImpl->mbAutoIndent = bAutoIndent; } 2098 bool TextView::IsReadOnly() const 2099 { return mpImpl->mbReadOnly; } 2100 void TextView::SetAutoScroll( bool bAutoScroll ) 2101 { mpImpl->mbAutoScroll = bAutoScroll; } 2102 bool TextView::IsAutoScroll() const 2103 { return mpImpl->mbAutoScroll; } 2104 bool TextView::HasSelection() const 2105 { return mpImpl->maSelection.HasRange(); } 2106 bool TextView::IsInsertMode() const 2107 { return mpImpl->mbInsertMode; } 2108 2109 void TextView::MatchGroup() 2110 { 2111 TextSelection aTmpSel( GetSelection() ); 2112 aTmpSel.Justify(); 2113 if ( ( aTmpSel.GetStart().GetPara() != aTmpSel.GetEnd().GetPara() ) || 2114 ( ( aTmpSel.GetEnd().GetIndex() - aTmpSel.GetStart().GetIndex() ) > 1 ) ) 2115 { 2116 return; 2117 } 2118 2119 TextSelection aMatchSel = static_cast<ExtTextEngine*>(GetTextEngine())->MatchGroup( aTmpSel.GetStart() ); 2120 if ( aMatchSel.HasRange() ) 2121 SetSelection( aMatchSel ); 2122 } 2123 2124 void TextView::CenterPaM( const TextPaM& rPaM ) 2125 { 2126 // Get textview size and the corresponding y-coordinates 2127 Size aOutSz = mpImpl->mpWindow->GetOutputSizePixel(); 2128 long nVisStartY = mpImpl->maStartDocPos.Y(); 2129 long nVisEndY = mpImpl->maStartDocPos.Y() + aOutSz.Height(); 2130 2131 // Retrieve the coordinates of the PaM 2132 tools::Rectangle aRect = mpImpl->mpTextEngine->PaMtoEditCursor(rPaM); 2133 2134 // Recalculate the offset of the center y-coordinates and scroll 2135 Scroll(0, (nVisStartY + nVisEndY) / 2 - aRect.TopLeft().getY()); 2136 } 2137 2138 bool TextView::Search( const i18nutil::SearchOptions& rSearchOptions, bool bForward ) 2139 { 2140 bool bFound = false; 2141 TextSelection aSel( GetSelection() ); 2142 if ( static_cast<ExtTextEngine*>(GetTextEngine())->Search( aSel, rSearchOptions, bForward ) ) 2143 { 2144 bFound = true; 2145 // First add the beginning of the word to the selection, 2146 // so that the whole word is in the visible region. 2147 SetSelection( aSel.GetStart() ); 2148 ShowCursor( true, false ); 2149 } 2150 else 2151 { 2152 aSel = GetSelection().GetEnd(); 2153 } 2154 2155 SetSelection( aSel ); 2156 // tdf#49482: Move the start of the selection to the center of the textview 2157 if (bFound) 2158 { 2159 CenterPaM( aSel.GetStart() ); 2160 } 2161 ShowCursor(); 2162 2163 return bFound; 2164 } 2165 2166 sal_uInt16 TextView::Replace( const i18nutil::SearchOptions& rSearchOptions, bool bAll, bool bForward ) 2167 { 2168 sal_uInt16 nFound = 0; 2169 2170 if ( !bAll ) 2171 { 2172 if ( GetSelection().HasRange() ) 2173 { 2174 InsertText( rSearchOptions.replaceString ); 2175 nFound = 1; 2176 Search( rSearchOptions, bForward ); // right away to the next 2177 } 2178 else 2179 { 2180 if( Search( rSearchOptions, bForward ) ) 2181 nFound = 1; 2182 } 2183 } 2184 else 2185 { 2186 // the writer replaces all, from beginning to end 2187 2188 ExtTextEngine* pTextEngine = static_cast<ExtTextEngine*>(GetTextEngine()); 2189 2190 // HideSelection(); 2191 TextSelection aSel; 2192 2193 bool bSearchInSelection = (0 != (rSearchOptions.searchFlag & css::util::SearchFlags::REG_NOT_BEGINOFLINE) ); 2194 if ( bSearchInSelection ) 2195 { 2196 aSel = GetSelection(); 2197 aSel.Justify(); 2198 } 2199 2200 TextSelection aSearchSel( aSel ); 2201 2202 bool bFound = pTextEngine->Search( aSel, rSearchOptions ); 2203 if ( bFound ) 2204 pTextEngine->UndoActionStart(); 2205 while ( bFound ) 2206 { 2207 nFound++; 2208 2209 TextPaM aNewStart = pTextEngine->ImpInsertText( aSel, rSearchOptions.replaceString ); 2210 aSel = aSearchSel; 2211 aSel.GetStart() = aNewStart; 2212 bFound = pTextEngine->Search( aSel, rSearchOptions ); 2213 } 2214 if ( nFound ) 2215 { 2216 SetSelection( aSel.GetStart() ); 2217 pTextEngine->FormatAndUpdate( this ); 2218 pTextEngine->UndoActionEnd(); 2219 } 2220 } 2221 return nFound; 2222 } 2223 2224 bool TextView::ImpIndentBlock( bool bRight ) 2225 { 2226 bool bDone = false; 2227 2228 TextSelection aSel = GetSelection(); 2229 aSel.Justify(); 2230 2231 HideSelection(); 2232 GetTextEngine()->UndoActionStart(); 2233 2234 const sal_uInt32 nStartPara = aSel.GetStart().GetPara(); 2235 sal_uInt32 nEndPara = aSel.GetEnd().GetPara(); 2236 if ( aSel.HasRange() && !aSel.GetEnd().GetIndex() ) 2237 { 2238 nEndPara--; // do not indent 2239 } 2240 2241 for ( sal_uInt32 nPara = nStartPara; nPara <= nEndPara; ++nPara ) 2242 { 2243 if ( bRight ) 2244 { 2245 // add tabs 2246 GetTextEngine()->ImpInsertText( TextPaM( nPara, 0 ), '\t' ); 2247 bDone = true; 2248 } 2249 else 2250 { 2251 // remove Tabs/Blanks 2252 OUString aText = GetTextEngine()->GetText( nPara ); 2253 if ( !aText.isEmpty() && ( 2254 ( aText[ 0 ] == '\t' ) || 2255 ( aText[ 0 ] == ' ' ) ) ) 2256 { 2257 GetTextEngine()->ImpDeleteText( TextSelection( TextPaM( nPara, 0 ), TextPaM( nPara, 1 ) ) ); 2258 bDone = true; 2259 } 2260 } 2261 } 2262 2263 GetTextEngine()->UndoActionEnd(); 2264 2265 bool bRange = aSel.HasRange(); 2266 if ( bRight ) 2267 { 2268 ++aSel.GetStart().GetIndex(); 2269 if ( bRange && ( aSel.GetEnd().GetPara() == nEndPara ) ) 2270 ++aSel.GetEnd().GetIndex(); 2271 } 2272 else 2273 { 2274 if ( aSel.GetStart().GetIndex() ) 2275 --aSel.GetStart().GetIndex(); 2276 if ( bRange && aSel.GetEnd().GetIndex() ) 2277 --aSel.GetEnd().GetIndex(); 2278 } 2279 2280 ImpSetSelection( aSel ); 2281 GetTextEngine()->FormatAndUpdate( this ); 2282 2283 return bDone; 2284 } 2285 2286 bool TextView::IndentBlock() 2287 { 2288 return ImpIndentBlock( true ); 2289 } 2290 2291 bool TextView::UnindentBlock() 2292 { 2293 return ImpIndentBlock( false ); 2294 } 2295 2296 2297 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 2298
