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 <vcl/svapp.hxx> 21 #include <vcl/window.hxx> 22 #include <editeng/lspcitem.hxx> 23 #include <editeng/flditem.hxx> 24 #include "impedit.hxx" 25 #include <editeng/editeng.hxx> 26 #include <editeng/editview.hxx> 27 #include "editdbg.hxx" 28 #include <eerdll2.hxx> 29 #include <editeng/eerdll.hxx> 30 #include <edtspell.hxx> 31 #include "eeobj.hxx" 32 #include <editeng/txtrange.hxx> 33 #include <svl/urlbmk.hxx> 34 #include <sfx2/app.hxx> 35 #include <svtools/colorcfg.hxx> 36 #include <svl/ctloptions.hxx> 37 #include <editeng/acorrcfg.hxx> 38 #include <editeng/fhgtitem.hxx> 39 #include <editeng/lrspitem.hxx> 40 #include <editeng/ulspitem.hxx> 41 #include <editeng/wghtitem.hxx> 42 #include <editeng/postitem.hxx> 43 #include <editeng/udlnitem.hxx> 44 #include <editeng/adjustitem.hxx> 45 #include <editeng/scripttypeitem.hxx> 46 #include <editeng/frmdiritem.hxx> 47 #include <editeng/fontitem.hxx> 48 #include <editeng/justifyitem.hxx> 49 50 #include <com/sun/star/i18n/CharacterIteratorMode.hpp> 51 #include <com/sun/star/i18n/WordType.hpp> 52 #include <com/sun/star/i18n/ScriptType.hpp> 53 #include <com/sun/star/lang/Locale.hpp> 54 #include <com/sun/star/text/CharacterCompressionType.hpp> 55 #include <com/sun/star/i18n/InputSequenceCheckMode.hpp> 56 57 58 #include <sal/log.hxx> 59 #include <osl/diagnose.h> 60 #include <sot/exchange.hxx> 61 #include <sot/formats.hxx> 62 #include <svl/asiancfg.hxx> 63 #include <i18nutil/unicode.hxx> 64 #include <tools/diagnose_ex.h> 65 #include <comphelper/lok.hxx> 66 #include <unotools/configmgr.hxx> 67 68 #include <unicode/ubidi.h> 69 #include <algorithm> 70 #include <memory> 71 72 #include <iostream> 73 #include <fstream> 74 75 using namespace ::com::sun::star; 76 77 static sal_uInt16 lcl_CalcExtraSpace( const SvxLineSpacingItem& rLSItem ) 78 { 79 sal_uInt16 nExtra = 0; 80 if ( rLSItem.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Fix ) 81 { 82 nExtra = rLSItem.GetInterLineSpace(); 83 } 84 85 return nExtra; 86 } 87 88 ImpEditEngine::ImpEditEngine( EditEngine* pEE, SfxItemPool* pItemPool ) : 89 pSharedVCL(EditDLL::Get().GetSharedVclResources()), 90 aPaperSize( 0x7FFFFFFF, 0x7FFFFFFF ), 91 aMinAutoPaperSize( 0x0, 0x0 ), 92 aMaxAutoPaperSize( 0x7FFFFFFF, 0x7FFFFFFF ), 93 aEditDoc( pItemPool ), 94 aWordDelimiters(" .,;:-`'?!_=\"{}()[]"), 95 bKernAsianPunctuation(false), 96 bAddExtLeading(false), 97 bIsFormatting(false), 98 bFormatted(false), 99 bInSelection(false), 100 bIsInUndo(false), 101 bUpdate(true), 102 bUndoEnabled(true), 103 bDowning(false), 104 bUseAutoColor(true), 105 bForceAutoColor(false), 106 bCallParaInsertedOrDeleted(false), 107 bFirstWordCapitalization(true), 108 mbLastTryMerge(false), 109 mbReplaceLeadingSingleQuotationMark(true), 110 mbNbspRunNext(false) 111 { 112 pEditEngine = pEE; 113 pActiveView = nullptr; 114 pConvInfo = nullptr; 115 pTextObjectPool = nullptr; 116 pStylePool = nullptr; 117 pUndoManager = nullptr; 118 pUndoMarkSelection = nullptr; 119 120 nCurTextHeight = 0; 121 nCurTextHeightNTP = 0; 122 nBigTextObjectStart = 20; 123 124 nStretchX = 100; 125 nStretchY = 100; 126 127 eDefLanguage = LANGUAGE_DONTKNOW; 128 maBackgroundColor = COL_AUTO; 129 130 nAsianCompressionMode = CharCompressType::NONE; 131 132 eDefaultHorizontalTextDirection = EEHorizontalTextDirection::Default; 133 134 135 aStatus.GetControlWord() = EEControlBits::USECHARATTRIBS | EEControlBits::DOIDLEFORMAT | 136 EEControlBits::PASTESPECIAL | EEControlBits::UNDOATTRIBS | 137 EEControlBits::ALLOWBIGOBJS | EEControlBits::RTFSTYLESHEETS | 138 EEControlBits::FORMAT100; 139 140 aSelEngine.SetFunctionSet( &aSelFuncSet ); 141 142 aStatusTimer.SetTimeout( 200 ); 143 aStatusTimer.SetInvokeHandler( LINK( this, ImpEditEngine, StatusTimerHdl ) ); 144 aStatusTimer.SetDebugName( "editeng::ImpEditEngine aStatusTimer" ); 145 146 aIdleFormatter.SetPriority( TaskPriority::REPAINT ); 147 aIdleFormatter.SetInvokeHandler( LINK( this, ImpEditEngine, IdleFormatHdl ) ); 148 aIdleFormatter.SetDebugName( "editeng::ImpEditEngine aIdleFormatter" ); 149 150 aOnlineSpellTimer.SetTimeout( 100 ); 151 aOnlineSpellTimer.SetInvokeHandler( LINK( this, ImpEditEngine, OnlineSpellHdl ) ); 152 aOnlineSpellTimer.SetDebugName( "editeng::ImpEditEngine aOnlineSpellTimer" ); 153 154 // Access data already from here on! 155 SetRefDevice( nullptr ); 156 InitDoc( false ); 157 158 bCallParaInsertedOrDeleted = true; 159 160 aEditDoc.SetModifyHdl( LINK( this, ImpEditEngine, DocModified ) ); 161 StartListening(*SfxGetpApp()); 162 } 163 164 void ImpEditEngine::Dispose() 165 { 166 SolarMutexGuard g; 167 auto pApp = SfxApplication::Get(); 168 if(pApp) 169 EndListening(*pApp); 170 pVirtDev.disposeAndClear(); 171 mpOwnDev.disposeAndClear(); 172 pSharedVCL.reset(); 173 } 174 175 ImpEditEngine::~ImpEditEngine() 176 { 177 aStatusTimer.Stop(); 178 aOnlineSpellTimer.Stop(); 179 aIdleFormatter.Stop(); 180 181 // Destroying templates may otherwise cause unnecessary formatting, 182 // when a parent template is destroyed. 183 // And this after the destruction of the data! 184 bDowning = true; 185 SetUpdateMode( false ); 186 187 Dispose(); 188 // it's only legal to delete the pUndoManager if it was created by 189 // ImpEditEngine; if it was set by SetUndoManager() it must be cleared 190 // before destroying the ImpEditEngine! 191 assert(!pUndoManager || typeid(*pUndoManager) == typeid(EditUndoManager)); 192 delete pUndoManager; 193 pTextRanger.reset(); 194 mpIMEInfos.reset(); 195 pCTLOptions.reset(); 196 pSpellInfo.reset(); 197 } 198 199 void ImpEditEngine::SetRefDevice( OutputDevice* pRef ) 200 { 201 if (pRef) 202 pRefDev = pRef; 203 else 204 pRefDev = pSharedVCL->GetVirtualDevice(); 205 206 nOnePixelInRef = static_cast<sal_uInt16>(pRefDev->PixelToLogic( Size( 1, 0 ) ).Width()); 207 208 if ( IsFormatted() ) 209 { 210 FormatFullDoc(); 211 UpdateViews(); 212 } 213 } 214 215 void ImpEditEngine::SetRefMapMode( const MapMode& rMapMode ) 216 { 217 if ( GetRefDevice()->GetMapMode() == rMapMode ) 218 return; 219 220 mpOwnDev.disposeAndClear(); 221 mpOwnDev = VclPtr<VirtualDevice>::Create(); 222 pRefDev = mpOwnDev; 223 pRefDev->SetMapMode(MapMode(MapUnit::MapTwip)); 224 SetRefDevice( pRefDev ); 225 226 pRefDev->SetMapMode( rMapMode ); 227 nOnePixelInRef = static_cast<sal_uInt16>(pRefDev->PixelToLogic( Size( 1, 0 ) ).Width()); 228 if ( IsFormatted() ) 229 { 230 FormatFullDoc(); 231 UpdateViews(); 232 } 233 } 234 235 void ImpEditEngine::InitDoc(bool bKeepParaAttribs) 236 { 237 sal_Int32 nParas = aEditDoc.Count(); 238 for ( sal_Int32 n = bKeepParaAttribs ? 1 : 0; n < nParas; n++ ) 239 { 240 if ( aEditDoc[n]->GetStyleSheet() ) 241 EndListening( *aEditDoc[n]->GetStyleSheet() ); 242 } 243 244 if ( bKeepParaAttribs ) 245 aEditDoc.RemoveText(); 246 else 247 aEditDoc.Clear(); 248 249 GetParaPortions().Reset(); 250 251 GetParaPortions().Insert(0, std::make_unique<ParaPortion>( aEditDoc[0] )); 252 253 bFormatted = false; 254 255 if ( IsCallParaInsertedOrDeleted() ) 256 { 257 GetEditEnginePtr()->ParagraphDeleted( EE_PARA_ALL ); 258 GetEditEnginePtr()->ParagraphInserted( 0 ); 259 } 260 261 if ( GetStatus().DoOnlineSpelling() ) 262 aEditDoc.GetObject( 0 )->CreateWrongList(); 263 } 264 265 EditPaM ImpEditEngine::DeleteSelected(const EditSelection& rSel) 266 { 267 EditPaM aPaM (ImpDeleteSelection(rSel)); 268 return aPaM; 269 } 270 271 OUString ImpEditEngine::GetSelected( const EditSelection& rSel ) const 272 { 273 if ( !rSel.HasRange() ) 274 return OUString(); 275 276 EditSelection aSel( rSel ); 277 aSel.Adjust( aEditDoc ); 278 279 ContentNode* pStartNode = aSel.Min().GetNode(); 280 ContentNode* pEndNode = aSel.Max().GetNode(); 281 sal_Int32 nStartNode = aEditDoc.GetPos( pStartNode ); 282 sal_Int32 nEndNode = aEditDoc.GetPos( pEndNode ); 283 284 OSL_ENSURE( nStartNode <= nEndNode, "Selection not sorted ?" ); 285 286 OUStringBuffer aText; 287 const OUString aSep = EditDoc::GetSepStr( LINEEND_LF ); 288 289 // iterate over the paragraphs ... 290 for ( sal_Int32 nNode = nStartNode; nNode <= nEndNode; nNode++ ) 291 { 292 OSL_ENSURE( aEditDoc.GetObject( nNode ), "Node not found: GetSelected" ); 293 const ContentNode* pNode = aEditDoc.GetObject( nNode ); 294 295 const sal_Int32 nStartPos = nNode==nStartNode ? aSel.Min().GetIndex() : 0; 296 const sal_Int32 nEndPos = nNode==nEndNode ? aSel.Max().GetIndex() : pNode->Len(); // can also be == nStart! 297 298 aText.append(EditDoc::GetParaAsString( pNode, nStartPos, nEndPos )); 299 if ( nNode < nEndNode ) 300 aText.append(aSep); 301 } 302 return aText.makeStringAndClear(); 303 } 304 305 bool ImpEditEngine::MouseButtonDown( const MouseEvent& rMEvt, EditView* pView ) 306 { 307 GetSelEngine().SetCurView( pView ); 308 SetActiveView( pView ); 309 310 if (!GetAutoCompleteText().isEmpty()) 311 SetAutoCompleteText( OUString(), true ); 312 313 GetSelEngine().SelMouseButtonDown( rMEvt ); 314 // Special treatment 315 EditSelection aCurSel( pView->pImpEditView->GetEditSelection() ); 316 if ( !rMEvt.IsShift() ) 317 { 318 if ( rMEvt.GetClicks() == 2 ) 319 { 320 // So that the SelectionEngine knows about the anchor. 321 aSelEngine.CursorPosChanging( true, false ); 322 323 EditSelection aNewSelection( SelectWord( aCurSel ) ); 324 pView->pImpEditView->DrawSelectionXOR(); 325 pView->pImpEditView->SetEditSelection( aNewSelection ); 326 pView->pImpEditView->DrawSelectionXOR(); 327 pView->ShowCursor(); 328 } 329 else if ( rMEvt.GetClicks() == 3 ) 330 { 331 // So that the SelectionEngine knows about the anchor. 332 aSelEngine.CursorPosChanging( true, false ); 333 334 EditSelection aNewSelection( aCurSel ); 335 aNewSelection.Min().SetIndex( 0 ); 336 aNewSelection.Max().SetIndex( aCurSel.Min().GetNode()->Len() ); 337 pView->pImpEditView->DrawSelectionXOR(); 338 pView->pImpEditView->SetEditSelection( aNewSelection ); 339 pView->pImpEditView->DrawSelectionXOR(); 340 pView->ShowCursor(); 341 } 342 } 343 return true; 344 } 345 346 void ImpEditEngine::Command( const CommandEvent& rCEvt, EditView* pView ) 347 { 348 GetSelEngine().SetCurView( pView ); 349 SetActiveView( pView ); 350 if ( rCEvt.GetCommand() == CommandEventId::StartExtTextInput ) 351 { 352 pView->DeleteSelected(); 353 mpIMEInfos.reset(); 354 EditPaM aPaM = pView->GetImpEditView()->GetEditSelection().Max(); 355 OUString aOldTextAfterStartPos = aPaM.GetNode()->Copy( aPaM.GetIndex() ); 356 sal_Int32 nMax = aOldTextAfterStartPos.indexOf( CH_FEATURE ); 357 if ( nMax != -1 ) // don't overwrite features! 358 aOldTextAfterStartPos = aOldTextAfterStartPos.copy( 0, nMax ); 359 mpIMEInfos.reset( new ImplIMEInfos( aPaM, aOldTextAfterStartPos ) ); 360 mpIMEInfos->bWasCursorOverwrite = !pView->IsInsertMode(); 361 UndoActionStart( EDITUNDO_INSERT ); 362 } 363 else if ( rCEvt.GetCommand() == CommandEventId::EndExtTextInput ) 364 { 365 OSL_ENSURE( mpIMEInfos, "CommandEventId::EndExtTextInput => No start ?" ); 366 if( mpIMEInfos ) 367 { 368 // #102812# convert quotes in IME text 369 // works on the last input character, this is especially in Korean text often done 370 // quotes that are inside of the string are not replaced! 371 // Borrowed from sw: edtwin.cxx 372 if ( mpIMEInfos->nLen ) 373 { 374 EditSelection aSel( mpIMEInfos->aPos ); 375 aSel.Min().SetIndex( aSel.Min().GetIndex() + mpIMEInfos->nLen-1 ); 376 aSel.Max().SetIndex( aSel.Max().GetIndex() + mpIMEInfos->nLen ); 377 // #102812# convert quotes in IME text 378 // works on the last input character, this is especially in Korean text often done 379 // quotes that are inside of the string are not replaced! 380 const sal_Unicode nCharCode = aSel.Min().GetNode()->GetChar( aSel.Min().GetIndex() ); 381 if ( ( GetStatus().DoAutoCorrect() ) && ( ( nCharCode == '\"' ) || ( nCharCode == '\'' ) ) ) 382 { 383 aSel = DeleteSelected( aSel ); 384 aSel = AutoCorrect( aSel, nCharCode, mpIMEInfos->bWasCursorOverwrite ); 385 pView->pImpEditView->SetEditSelection( aSel ); 386 } 387 } 388 389 ParaPortion* pPortion = FindParaPortion( mpIMEInfos->aPos.GetNode() ); 390 pPortion->MarkSelectionInvalid( mpIMEInfos->aPos.GetIndex() ); 391 392 bool bWasCursorOverwrite = mpIMEInfos->bWasCursorOverwrite; 393 394 mpIMEInfos.reset(); 395 396 FormatAndUpdate( pView ); 397 398 pView->SetInsertMode( !bWasCursorOverwrite ); 399 } 400 UndoActionEnd(); 401 } 402 else if ( rCEvt.GetCommand() == CommandEventId::ExtTextInput ) 403 { 404 OSL_ENSURE( mpIMEInfos, "CommandEventId::ExtTextInput => No Start ?" ); 405 if( mpIMEInfos ) 406 { 407 const CommandExtTextInputData* pData = rCEvt.GetExtTextInputData(); 408 409 if ( !pData->IsOnlyCursorChanged() ) 410 { 411 EditSelection aSel( mpIMEInfos->aPos ); 412 aSel.Max().SetIndex( aSel.Max().GetIndex() + mpIMEInfos->nLen ); 413 aSel = DeleteSelected( aSel ); 414 aSel = ImpInsertText( aSel, pData->GetText() ); 415 416 if ( mpIMEInfos->bWasCursorOverwrite ) 417 { 418 sal_Int32 nOldIMETextLen = mpIMEInfos->nLen; 419 sal_Int32 nNewIMETextLen = pData->GetText().getLength(); 420 421 if ( ( nOldIMETextLen > nNewIMETextLen ) && 422 ( nNewIMETextLen < mpIMEInfos->aOldTextAfterStartPos.getLength() ) ) 423 { 424 // restore old characters 425 sal_Int32 nRestore = nOldIMETextLen - nNewIMETextLen; 426 EditPaM aPaM( mpIMEInfos->aPos ); 427 aPaM.SetIndex( aPaM.GetIndex() + nNewIMETextLen ); 428 ImpInsertText( aPaM, mpIMEInfos->aOldTextAfterStartPos.copy( nNewIMETextLen, nRestore ) ); 429 } 430 else if ( ( nOldIMETextLen < nNewIMETextLen ) && 431 ( nOldIMETextLen < mpIMEInfos->aOldTextAfterStartPos.getLength() ) ) 432 { 433 // overwrite 434 sal_Int32 nOverwrite = nNewIMETextLen - nOldIMETextLen; 435 if ( ( nOldIMETextLen + nOverwrite ) > mpIMEInfos->aOldTextAfterStartPos.getLength() ) 436 nOverwrite = mpIMEInfos->aOldTextAfterStartPos.getLength() - nOldIMETextLen; 437 OSL_ENSURE( nOverwrite && (nOverwrite < 0xFF00), "IME Overwrite?!" ); 438 EditPaM aPaM( mpIMEInfos->aPos ); 439 aPaM.SetIndex( aPaM.GetIndex() + nNewIMETextLen ); 440 EditSelection _aSel( aPaM ); 441 _aSel.Max().SetIndex( _aSel.Max().GetIndex() + nOverwrite ); 442 DeleteSelected( _aSel ); 443 } 444 } 445 if ( pData->GetTextAttr() ) 446 { 447 mpIMEInfos->CopyAttribs( pData->GetTextAttr(), pData->GetText().getLength() ); 448 } 449 else 450 { 451 mpIMEInfos->DestroyAttribs(); 452 mpIMEInfos->nLen = pData->GetText().getLength(); 453 } 454 455 ParaPortion* pPortion = FindParaPortion( mpIMEInfos->aPos.GetNode() ); 456 pPortion->MarkSelectionInvalid( mpIMEInfos->aPos.GetIndex() ); 457 FormatAndUpdate( pView ); 458 } 459 460 EditSelection aNewSel = EditPaM( mpIMEInfos->aPos.GetNode(), mpIMEInfos->aPos.GetIndex()+pData->GetCursorPos() ); 461 pView->SetSelection( CreateESel( aNewSel ) ); 462 pView->SetInsertMode( !pData->IsCursorOverwrite() ); 463 464 if ( pData->IsCursorVisible() ) 465 pView->ShowCursor(); 466 else 467 pView->HideCursor(); 468 } 469 } 470 else if ( rCEvt.GetCommand() == CommandEventId::InputContextChange ) 471 { 472 } 473 else if ( rCEvt.GetCommand() == CommandEventId::CursorPos ) 474 { 475 if ( mpIMEInfos && mpIMEInfos->nLen ) 476 { 477 EditPaM aPaM( pView->pImpEditView->GetEditSelection().Max() ); 478 tools::Rectangle aR1 = PaMtoEditCursor( aPaM ); 479 480 sal_Int32 nInputEnd = mpIMEInfos->aPos.GetIndex() + mpIMEInfos->nLen; 481 482 if ( !IsFormatted() ) 483 FormatDoc(); 484 485 ParaPortion* pParaPortion = GetParaPortions().SafeGetObject( GetEditDoc().GetPos( aPaM.GetNode() ) ); 486 if (pParaPortion) 487 { 488 sal_Int32 nLine = pParaPortion->GetLines().FindLine( aPaM.GetIndex(), true ); 489 const EditLine& rLine = pParaPortion->GetLines()[nLine]; 490 if ( nInputEnd > rLine.GetEnd() ) 491 nInputEnd = rLine.GetEnd(); 492 tools::Rectangle aR2 = PaMtoEditCursor( EditPaM( aPaM.GetNode(), nInputEnd ), GetCursorFlags::EndOfLine ); 493 tools::Rectangle aRect = pView->GetImpEditView()->GetWindowPos( aR1 ); 494 pView->GetWindow()->SetCursorRect( &aRect, aR2.Left()-aR1.Right() ); 495 } 496 } 497 else 498 { 499 pView->GetWindow()->SetCursorRect(); 500 } 501 } 502 else if ( rCEvt.GetCommand() == CommandEventId::SelectionChange ) 503 { 504 const CommandSelectionChangeData *pData = rCEvt.GetSelectionChangeData(); 505 506 ESelection aSelection = pView->GetSelection(); 507 aSelection.Adjust(); 508 509 if( pView->HasSelection() ) 510 { 511 aSelection.nEndPos = aSelection.nStartPos; 512 aSelection.nStartPos += pData->GetStart(); 513 aSelection.nEndPos += pData->GetEnd(); 514 } 515 else 516 { 517 aSelection.nStartPos = pData->GetStart(); 518 aSelection.nEndPos = pData->GetEnd(); 519 } 520 pView->SetSelection( aSelection ); 521 } 522 else if ( rCEvt.GetCommand() == CommandEventId::PrepareReconversion ) 523 { 524 if ( pView->HasSelection() ) 525 { 526 ESelection aSelection = pView->GetSelection(); 527 aSelection.Adjust(); 528 529 if ( aSelection.nStartPara != aSelection.nEndPara ) 530 { 531 sal_Int32 aParaLen = pEditEngine->GetTextLen( aSelection.nStartPara ); 532 aSelection.nEndPara = aSelection.nStartPara; 533 aSelection.nEndPos = aParaLen; 534 pView->SetSelection( aSelection ); 535 } 536 } 537 } 538 else if ( rCEvt.GetCommand() == CommandEventId::QueryCharPosition ) 539 { 540 if ( mpIMEInfos && mpIMEInfos->nLen ) 541 { 542 EditPaM aPaM( pView->pImpEditView->GetEditSelection().Max() ); 543 if ( !IsFormatted() ) 544 FormatDoc(); 545 546 ParaPortion* pParaPortion = GetParaPortions().SafeGetObject( GetEditDoc().GetPos( aPaM.GetNode() ) ); 547 if (pParaPortion) 548 { 549 sal_Int32 nLine = pParaPortion->GetLines().FindLine( aPaM.GetIndex(), true ); 550 const EditLine& rLine = pParaPortion->GetLines()[nLine]; 551 std::unique_ptr<tools::Rectangle[]> aRects(new tools::Rectangle[ mpIMEInfos->nLen ]); 552 for (sal_Int32 i = 0; i < mpIMEInfos->nLen; ++i) 553 { 554 sal_Int32 nInputPos = mpIMEInfos->aPos.GetIndex() + i; 555 if ( nInputPos > rLine.GetEnd() ) 556 nInputPos = rLine.GetEnd(); 557 tools::Rectangle aR2 = GetEditCursor( pParaPortion, nInputPos ); 558 aRects[ i ] = pView->GetImpEditView()->GetWindowPos( aR2 ); 559 } 560 pView->GetWindow()->SetCompositionCharRect( aRects.get(), mpIMEInfos->nLen ); 561 } 562 } 563 } 564 565 GetSelEngine().Command( rCEvt ); 566 } 567 568 bool ImpEditEngine::MouseButtonUp( const MouseEvent& rMEvt, EditView* pView ) 569 { 570 GetSelEngine().SetCurView( pView ); 571 GetSelEngine().SelMouseButtonUp( rMEvt ); 572 573 // in the tiled rendering case, setting bInSelection here has unexpected 574 // consequences - further tiles painting removes the selection 575 // FIXME I believe resetting bInSelection should not be here even in the 576 // non-tiled-rendering case, but it has been here since 2000 (and before) 577 // so who knows what corner case it was supposed to solve back then 578 if (!comphelper::LibreOfficeKit::isActive()) 579 bInSelection = false; 580 581 // Special treatments 582 EditSelection aCurSel( pView->pImpEditView->GetEditSelection() ); 583 if ( !aCurSel.HasRange() ) 584 { 585 if ( ( rMEvt.GetClicks() == 1 ) && rMEvt.IsLeft() && !rMEvt.IsMod2() ) 586 { 587 const OutputDevice& rOutDev = pView->getEditViewCallbacks() ? pView->getEditViewCallbacks()->EditViewOutputDevice() : *pView->GetWindow(); 588 Point aLogicClick = rOutDev.PixelToLogic(rMEvt.GetPosPixel()); 589 if (const SvxFieldItem* pFld = pView->GetField(aLogicClick)) 590 { 591 EditPaM aPaM( aCurSel.Max() ); 592 sal_Int32 nPara = GetEditDoc().GetPos( aPaM.GetNode() ); 593 GetEditEnginePtr()->FieldClicked( *pFld, nPara, aPaM.GetIndex() ); 594 } 595 } 596 } 597 return true; 598 } 599 600 void ImpEditEngine::ReleaseMouse() 601 { 602 GetSelEngine().ReleaseMouse(); 603 } 604 605 bool ImpEditEngine::MouseMove( const MouseEvent& rMEvt, EditView* pView ) 606 { 607 // MouseMove is called directly after ShowQuickHelp()! 608 GetSelEngine().SetCurView( pView ); 609 GetSelEngine().SelMouseMove( rMEvt ); 610 return true; 611 } 612 613 EditPaM ImpEditEngine::InsertText(const EditSelection& aSel, const OUString& rStr) 614 { 615 EditPaM aPaM = ImpInsertText( aSel, rStr ); 616 return aPaM; 617 } 618 619 void ImpEditEngine::Clear() 620 { 621 InitDoc( false ); 622 623 EditPaM aPaM = aEditDoc.GetStartPaM(); 624 EditSelection aSel( aPaM ); 625 626 nCurTextHeight = 0; 627 nCurTextHeightNTP = 0; 628 629 ResetUndoManager(); 630 631 for (size_t nView = aEditViews.size(); nView; ) 632 { 633 EditView* pView = aEditViews[--nView]; 634 pView->pImpEditView->SetEditSelection( aSel ); 635 } 636 } 637 638 EditPaM ImpEditEngine::RemoveText() 639 { 640 InitDoc( true ); 641 642 EditPaM aStartPaM = aEditDoc.GetStartPaM(); 643 EditSelection aEmptySel( aStartPaM, aStartPaM ); 644 for (EditView* pView : aEditViews) 645 { 646 pView->pImpEditView->SetEditSelection( aEmptySel ); 647 } 648 ResetUndoManager(); 649 return aEditDoc.GetStartPaM(); 650 } 651 652 653 void ImpEditEngine::SetText(const OUString& rText) 654 { 655 // RemoveText deletes the undo list! 656 EditPaM aStartPaM = RemoveText(); 657 bool bUndoCurrentlyEnabled = IsUndoEnabled(); 658 // The text inserted manually can not be made reversible by the user 659 EnableUndo( false ); 660 661 EditSelection aEmptySel( aStartPaM, aStartPaM ); 662 EditPaM aPaM = aStartPaM; 663 if (!rText.isEmpty()) 664 aPaM = ImpInsertText( aEmptySel, rText ); 665 666 for (EditView* pView : aEditViews) 667 { 668 pView->pImpEditView->SetEditSelection( EditSelection( aPaM, aPaM ) ); 669 // If no text then also no Format&Update 670 // => The text remains. 671 if (rText.isEmpty() && GetUpdateMode()) 672 { 673 tools::Rectangle aTmpRect( pView->GetOutputArea().TopLeft(), 674 Size( aPaperSize.Width(), nCurTextHeight ) ); 675 aTmpRect.Intersection( pView->GetOutputArea() ); 676 pView->InvalidateWindow( aTmpRect ); 677 } 678 } 679 if (rText.isEmpty()) { // otherwise it must be invalidated later, !bFormatted is enough. 680 nCurTextHeight = 0; 681 nCurTextHeightNTP = 0; 682 } 683 EnableUndo( bUndoCurrentlyEnabled ); 684 OSL_ENSURE( !HasUndoManager() || !GetUndoManager().GetUndoActionCount(), "Undo after SetText?" ); 685 } 686 687 688 const SfxItemSet& ImpEditEngine::GetEmptyItemSet() 689 { 690 if ( !pEmptyItemSet ) 691 { 692 pEmptyItemSet = std::make_unique<SfxItemSet>( aEditDoc.GetItemPool(), svl::Items<EE_ITEMS_START, EE_ITEMS_END>{} ); 693 for ( sal_uInt16 nWhich = EE_ITEMS_START; nWhich <= EE_CHAR_END; nWhich++) 694 { 695 pEmptyItemSet->ClearItem( nWhich ); 696 } 697 } 698 return *pEmptyItemSet; 699 } 700 701 702 // MISC 703 704 void ImpEditEngine::CursorMoved( const ContentNode* pPrevNode ) 705 { 706 // Delete empty attributes, but only if paragraph is not empty! 707 if (pPrevNode->GetCharAttribs().HasEmptyAttribs() && pPrevNode->Len()) 708 { 709 const_cast<ContentNode*>(pPrevNode)->GetCharAttribs().DeleteEmptyAttribs(aEditDoc.GetItemPool()); 710 } 711 } 712 713 void ImpEditEngine::TextModified() 714 { 715 bFormatted = false; 716 717 if ( GetNotifyHdl().IsSet() ) 718 { 719 EENotify aNotify( EE_NOTIFY_TEXTMODIFIED ); 720 GetNotifyHdl().Call( aNotify ); 721 } 722 } 723 724 725 void ImpEditEngine::ParaAttribsChanged( ContentNode const * pNode, bool bIgnoreUndoCheck ) 726 { 727 assert(pNode && "ParaAttribsChanged: Which one?"); 728 729 aEditDoc.SetModified( true ); 730 bFormatted = false; 731 732 ParaPortion* pPortion = FindParaPortion( pNode ); 733 OSL_ENSURE( pPortion, "ParaAttribsChanged: Portion?" ); 734 pPortion->MarkSelectionInvalid( 0 ); 735 736 sal_Int32 nPara = aEditDoc.GetPos( pNode ); 737 if ( bIgnoreUndoCheck || pEditEngine->IsInUndo() ) 738 pEditEngine->ParaAttribsChanged( nPara ); 739 740 ParaPortion* pNextPortion = GetParaPortions().SafeGetObject( nPara+1 ); 741 // => is formatted again anyway, if Invalid. 742 if ( pNextPortion && !pNextPortion->IsInvalid() ) 743 CalcHeight( pNextPortion ); 744 } 745 746 747 // Cursor movements 748 749 750 EditSelection const & ImpEditEngine::MoveCursor( const KeyEvent& rKeyEvent, EditView* pEditView ) 751 { 752 // Actually, only necessary for up/down, but whatever. 753 CheckIdleFormatter(); 754 755 EditPaM aPaM( pEditView->pImpEditView->GetEditSelection().Max() ); 756 757 EditPaM aOldPaM( aPaM ); 758 759 TextDirectionality eTextDirection = TextDirectionality::LeftToRight_TopToBottom; 760 if (IsVertical() && IsTopToBottom()) 761 eTextDirection = TextDirectionality::TopToBottom_RightToLeft; 762 else if (IsVertical() && !IsTopToBottom()) 763 eTextDirection = TextDirectionality::BottomToTop_LeftToRight; 764 else if ( IsRightToLeft( GetEditDoc().GetPos( aPaM.GetNode() ) ) ) 765 eTextDirection = TextDirectionality::RightToLeft_TopToBottom; 766 767 KeyEvent aTranslatedKeyEvent = rKeyEvent.LogicalTextDirectionality( eTextDirection ); 768 769 bool bCtrl = aTranslatedKeyEvent.GetKeyCode().IsMod1(); 770 sal_uInt16 nCode = aTranslatedKeyEvent.GetKeyCode().GetCode(); 771 772 if ( DoVisualCursorTraveling() ) 773 { 774 // Only for simple cursor movement... 775 if ( !bCtrl && ( ( nCode == KEY_LEFT ) || ( nCode == KEY_RIGHT ) ) ) 776 { 777 aPaM = CursorVisualLeftRight( pEditView, aPaM, rKeyEvent.GetKeyCode().IsMod2() ? i18n::CharacterIteratorMode::SKIPCHARACTER : i18n::CharacterIteratorMode::SKIPCELL, rKeyEvent.GetKeyCode().GetCode() == KEY_LEFT ); 778 nCode = 0; // skip switch statement 779 } 780 } 781 782 bool bKeyModifySelection = aTranslatedKeyEvent.GetKeyCode().IsShift(); 783 switch ( nCode ) 784 { 785 case KEY_UP: aPaM = CursorUp( aPaM, pEditView ); 786 break; 787 case KEY_DOWN: aPaM = CursorDown( aPaM, pEditView ); 788 break; 789 case KEY_LEFT: aPaM = bCtrl ? WordLeft( aPaM ) : CursorLeft( aPaM, aTranslatedKeyEvent.GetKeyCode().IsMod2() ? i18n::CharacterIteratorMode::SKIPCHARACTER : i18n::CharacterIteratorMode::SKIPCELL ); 790 break; 791 case KEY_RIGHT: aPaM = bCtrl ? WordRight( aPaM ) : CursorRight( aPaM, aTranslatedKeyEvent.GetKeyCode().IsMod2() ? i18n::CharacterIteratorMode::SKIPCHARACTER : i18n::CharacterIteratorMode::SKIPCELL ); 792 break; 793 case KEY_HOME: aPaM = bCtrl ? CursorStartOfDoc() : CursorStartOfLine( aPaM ); 794 break; 795 case KEY_END: aPaM = bCtrl ? CursorEndOfDoc() : CursorEndOfLine( aPaM ); 796 break; 797 case KEY_PAGEUP: aPaM = bCtrl ? CursorStartOfDoc() : PageUp( aPaM, pEditView ); 798 break; 799 case KEY_PAGEDOWN: aPaM = bCtrl ? CursorEndOfDoc() : PageDown( aPaM, pEditView ); 800 break; 801 case css::awt::Key::MOVE_TO_BEGIN_OF_LINE: 802 aPaM = CursorStartOfLine( aPaM ); 803 bKeyModifySelection = false; 804 break; 805 case css::awt::Key::MOVE_TO_END_OF_LINE: 806 aPaM = CursorEndOfLine( aPaM ); 807 bKeyModifySelection = false; 808 break; 809 case css::awt::Key::MOVE_WORD_BACKWARD: 810 aPaM = WordLeft( aPaM ); 811 bKeyModifySelection = false; 812 break; 813 case css::awt::Key::MOVE_WORD_FORWARD: 814 aPaM = WordRight( aPaM ); 815 bKeyModifySelection = false; 816 break; 817 case css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH: 818 aPaM = CursorStartOfParagraph( aPaM ); 819 if( aPaM == aOldPaM ) 820 { 821 aPaM = CursorLeft( aPaM ); 822 aPaM = CursorStartOfParagraph( aPaM ); 823 } 824 bKeyModifySelection = false; 825 break; 826 case css::awt::Key::MOVE_TO_END_OF_PARAGRAPH: 827 aPaM = CursorEndOfParagraph( aPaM ); 828 if( aPaM == aOldPaM ) 829 { 830 aPaM = CursorRight( aPaM ); 831 aPaM = CursorEndOfParagraph( aPaM ); 832 } 833 bKeyModifySelection = false; 834 break; 835 case css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT: 836 aPaM = CursorStartOfDoc(); 837 bKeyModifySelection = false; 838 break; 839 case css::awt::Key::MOVE_TO_END_OF_DOCUMENT: 840 aPaM = CursorEndOfDoc(); 841 bKeyModifySelection = false; 842 break; 843 case css::awt::Key::SELECT_TO_BEGIN_OF_LINE: 844 aPaM = CursorStartOfLine( aPaM ); 845 bKeyModifySelection = true; 846 break; 847 case css::awt::Key::SELECT_TO_END_OF_LINE: 848 aPaM = CursorEndOfLine( aPaM ); 849 bKeyModifySelection = true; 850 break; 851 case css::awt::Key::SELECT_BACKWARD: 852 aPaM = CursorLeft( aPaM ); 853 bKeyModifySelection = true; 854 break; 855 case css::awt::Key::SELECT_FORWARD: 856 aPaM = CursorRight( aPaM ); 857 bKeyModifySelection = true; 858 break; 859 case css::awt::Key::SELECT_WORD_BACKWARD: 860 aPaM = WordLeft( aPaM ); 861 bKeyModifySelection = true; 862 break; 863 case css::awt::Key::SELECT_WORD_FORWARD: 864 aPaM = WordRight( aPaM ); 865 bKeyModifySelection = true; 866 break; 867 case css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH: 868 aPaM = CursorStartOfParagraph( aPaM ); 869 if( aPaM == aOldPaM ) 870 { 871 aPaM = CursorLeft( aPaM ); 872 aPaM = CursorStartOfParagraph( aPaM ); 873 } 874 bKeyModifySelection = true; 875 break; 876 case css::awt::Key::SELECT_TO_END_OF_PARAGRAPH: 877 aPaM = CursorEndOfParagraph( aPaM ); 878 if( aPaM == aOldPaM ) 879 { 880 aPaM = CursorRight( aPaM ); 881 aPaM = CursorEndOfParagraph( aPaM ); 882 } 883 bKeyModifySelection = true; 884 break; 885 case css::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT: 886 aPaM = CursorStartOfDoc(); 887 bKeyModifySelection = true; 888 break; 889 case css::awt::Key::SELECT_TO_END_OF_DOCUMENT: 890 aPaM = CursorEndOfDoc(); 891 bKeyModifySelection = true; 892 break; 893 } 894 895 if ( aOldPaM != aPaM ) 896 { 897 CursorMoved( aOldPaM.GetNode() ); 898 } 899 900 // May cause, a CreateAnchor or deselection all 901 aSelEngine.SetCurView( pEditView ); 902 aSelEngine.CursorPosChanging( bKeyModifySelection, aTranslatedKeyEvent.GetKeyCode().IsMod1() ); 903 EditPaM aOldEnd( pEditView->pImpEditView->GetEditSelection().Max() ); 904 905 { 906 EditSelection aNewSelection(pEditView->pImpEditView->GetEditSelection()); 907 aNewSelection.Max() = aPaM; 908 pEditView->pImpEditView->SetEditSelection(aNewSelection); 909 // const_cast<EditPaM&>(pEditView->pImpEditView->GetEditSelection().Max()) = aPaM; 910 } 911 912 if ( bKeyModifySelection ) 913 { 914 // Then the selection is expanded ... or the whole selection is painted in case of tiled rendering. 915 EditSelection aTmpNewSel( comphelper::LibreOfficeKit::isActive() ? pEditView->pImpEditView->GetEditSelection().Min() : aOldEnd, aPaM ); 916 pEditView->pImpEditView->DrawSelectionXOR( aTmpNewSel ); 917 } 918 else 919 { 920 EditSelection aNewSelection(pEditView->pImpEditView->GetEditSelection()); 921 aNewSelection.Min() = aPaM; 922 pEditView->pImpEditView->SetEditSelection(aNewSelection); 923 // const_cast<EditPaM&>(pEditView->pImpEditView->GetEditSelection().Min()) = aPaM; 924 } 925 926 return pEditView->pImpEditView->GetEditSelection(); 927 } 928 929 EditPaM ImpEditEngine::CursorVisualStartEnd( EditView const * pEditView, const EditPaM& rPaM, bool bStart ) 930 { 931 EditPaM aPaM( rPaM ); 932 933 sal_Int32 nPara = GetEditDoc().GetPos( aPaM.GetNode() ); 934 ParaPortion* pParaPortion = GetParaPortions().SafeGetObject( nPara ); 935 if (!pParaPortion) 936 return aPaM; 937 938 sal_Int32 nLine = pParaPortion->GetLines().FindLine( aPaM.GetIndex(), false ); 939 const EditLine& rLine = pParaPortion->GetLines()[nLine]; 940 bool bEmptyLine = rLine.GetStart() == rLine.GetEnd(); 941 942 pEditView->pImpEditView->nExtraCursorFlags = GetCursorFlags::NONE; 943 944 if ( !bEmptyLine ) 945 { 946 OUString aLine = aPaM.GetNode()->GetString().copy(rLine.GetStart(), rLine.GetEnd() - rLine.GetStart()); 947 948 UErrorCode nError = U_ZERO_ERROR; 949 UBiDi* pBidi = ubidi_openSized( aLine.getLength(), 0, &nError ); 950 951 const UBiDiLevel nBidiLevel = IsRightToLeft( nPara ) ? 1 /*RTL*/ : 0 /*LTR*/; 952 ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(aLine.getStr()), aLine.getLength(), nBidiLevel, nullptr, &nError ); 953 954 sal_Int32 nVisPos = bStart ? 0 : aLine.getLength()-1; 955 const sal_Int32 nLogPos = ubidi_getLogicalIndex( pBidi, nVisPos, &nError ); 956 957 ubidi_close( pBidi ); 958 959 aPaM.SetIndex( nLogPos + rLine.GetStart() ); 960 961 sal_Int32 nTmp; 962 sal_Int32 nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nTmp, true ); 963 const TextPortion& rTextPortion = pParaPortion->GetTextPortions()[nTextPortion]; 964 bool bPortionRTL = rTextPortion.IsRightToLeft(); 965 966 if ( bStart ) 967 { 968 pEditView->pImpEditView->SetCursorBidiLevel( bPortionRTL ? 0 : 1 ); 969 // Maybe we must be *behind* the character 970 if ( bPortionRTL && pEditView->IsInsertMode() ) 971 aPaM.SetIndex( aPaM.GetIndex()+1 ); 972 } 973 else 974 { 975 pEditView->pImpEditView->SetCursorBidiLevel( bPortionRTL ? 1 : 0 ); 976 if ( !bPortionRTL && pEditView->IsInsertMode() ) 977 aPaM.SetIndex( aPaM.GetIndex()+1 ); 978 } 979 } 980 981 return aPaM; 982 } 983 984 EditPaM ImpEditEngine::CursorVisualLeftRight( EditView const * pEditView, const EditPaM& rPaM, sal_uInt16 nCharacterIteratorMode, bool bVisualToLeft ) 985 { 986 EditPaM aPaM( rPaM ); 987 988 sal_Int32 nPara = GetEditDoc().GetPos( aPaM.GetNode() ); 989 ParaPortion* pParaPortion = GetParaPortions().SafeGetObject( nPara ); 990 if (!pParaPortion) 991 return aPaM; 992 993 sal_Int32 nLine = pParaPortion->GetLines().FindLine( aPaM.GetIndex(), false ); 994 const EditLine& rLine = pParaPortion->GetLines()[nLine]; 995 bool bEmptyLine = rLine.GetStart() == rLine.GetEnd(); 996 997 pEditView->pImpEditView->nExtraCursorFlags = GetCursorFlags::NONE; 998 999 bool bParaRTL = IsRightToLeft( nPara ); 1000 1001 bool bDone = false; 1002 1003 if ( bEmptyLine ) 1004 { 1005 if ( bVisualToLeft ) 1006 { 1007 aPaM = CursorUp( aPaM, pEditView ); 1008 if ( aPaM != rPaM ) 1009 aPaM = CursorVisualStartEnd( pEditView, aPaM, false ); 1010 } 1011 else 1012 { 1013 aPaM = CursorDown( aPaM, pEditView ); 1014 if ( aPaM != rPaM ) 1015 aPaM = CursorVisualStartEnd( pEditView, aPaM, true ); 1016 } 1017 1018 bDone = true; 1019 } 1020 1021 bool bLogicalBackward = bParaRTL ? !bVisualToLeft : bVisualToLeft; 1022 1023 if ( !bDone && pEditView->IsInsertMode() ) 1024 { 1025 // Check if we are within a portion and don't have overwrite mode, then it's easy... 1026 sal_Int32 nPortionStart; 1027 sal_Int32 nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nPortionStart ); 1028 const TextPortion& rTextPortion = pParaPortion->GetTextPortions()[nTextPortion]; 1029 1030 bool bPortionBoundary = ( aPaM.GetIndex() == nPortionStart ) || ( aPaM.GetIndex() == (nPortionStart+rTextPortion.GetLen()) ); 1031 sal_uInt16 nRTLLevel = rTextPortion.GetRightToLeftLevel(); 1032 1033 // Portion boundary doesn't matter if both have same RTL level 1034 sal_Int32 nRTLLevelNextPortion = -1; 1035 if ( bPortionBoundary && aPaM.GetIndex() && ( aPaM.GetIndex() < aPaM.GetNode()->Len() ) ) 1036 { 1037 sal_Int32 nTmp; 1038 sal_Int32 nNextTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex()+1, nTmp, !bLogicalBackward ); 1039 const TextPortion& rNextTextPortion = pParaPortion->GetTextPortions()[nNextTextPortion]; 1040 nRTLLevelNextPortion = rNextTextPortion.GetRightToLeftLevel(); 1041 } 1042 1043 if ( !bPortionBoundary || ( nRTLLevel == nRTLLevelNextPortion ) ) 1044 { 1045 if (bVisualToLeft != bool(nRTLLevel % 2)) 1046 { 1047 aPaM = CursorLeft( aPaM, nCharacterIteratorMode ); 1048 pEditView->pImpEditView->SetCursorBidiLevel( 1 ); 1049 } 1050 else 1051 { 1052 aPaM = CursorRight( aPaM, nCharacterIteratorMode ); 1053 pEditView->pImpEditView->SetCursorBidiLevel( 0 ); 1054 } 1055 bDone = true; 1056 } 1057 } 1058 1059 if ( !bDone ) 1060 { 1061 bool bGotoStartOfNextLine = false; 1062 bool bGotoEndOfPrevLine = false; 1063 1064 OUString aLine = aPaM.GetNode()->GetString().copy(rLine.GetStart(), rLine.GetEnd() - rLine.GetStart()); 1065 const sal_Int32 nPosInLine = aPaM.GetIndex() - rLine.GetStart(); 1066 1067 UErrorCode nError = U_ZERO_ERROR; 1068 UBiDi* pBidi = ubidi_openSized( aLine.getLength(), 0, &nError ); 1069 1070 const UBiDiLevel nBidiLevel = IsRightToLeft( nPara ) ? 1 /*RTL*/ : 0 /*LTR*/; 1071 ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(aLine.getStr()), aLine.getLength(), nBidiLevel, nullptr, &nError ); 1072 1073 if ( !pEditView->IsInsertMode() ) 1074 { 1075 bool bEndOfLine = nPosInLine == aLine.getLength(); 1076 sal_Int32 nVisPos = ubidi_getVisualIndex( pBidi, !bEndOfLine ? nPosInLine : nPosInLine-1, &nError ); 1077 if ( bVisualToLeft ) 1078 { 1079 bGotoEndOfPrevLine = nVisPos == 0; 1080 if ( !bEndOfLine ) 1081 nVisPos--; 1082 } 1083 else 1084 { 1085 bGotoStartOfNextLine = nVisPos == (aLine.getLength() - 1); 1086 if ( !bEndOfLine ) 1087 nVisPos++; 1088 } 1089 1090 if ( !bGotoEndOfPrevLine && !bGotoStartOfNextLine ) 1091 { 1092 aPaM.SetIndex( rLine.GetStart() + ubidi_getLogicalIndex( pBidi, nVisPos, &nError ) ); 1093 pEditView->pImpEditView->SetCursorBidiLevel( 0 ); 1094 } 1095 } 1096 else 1097 { 1098 bool bWasBehind = false; 1099 bool bBeforePortion = !nPosInLine || pEditView->pImpEditView->GetCursorBidiLevel() == 1; 1100 if ( nPosInLine && ( !bBeforePortion ) ) // before the next portion 1101 bWasBehind = true; // step one back, otherwise visual will be unusable when rtl portion follows. 1102 1103 sal_Int32 nPortionStart; 1104 sal_Int32 nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nPortionStart, bBeforePortion ); 1105 const TextPortion& rTextPortion = pParaPortion->GetTextPortions()[nTextPortion]; 1106 bool bRTLPortion = rTextPortion.IsRightToLeft(); 1107 1108 // -1: We are 'behind' the character 1109 long nVisPos = static_cast<long>(ubidi_getVisualIndex( pBidi, bWasBehind ? nPosInLine-1 : nPosInLine, &nError )); 1110 if ( bVisualToLeft ) 1111 { 1112 if ( !bWasBehind || bRTLPortion ) 1113 nVisPos--; 1114 } 1115 else 1116 { 1117 if ( bWasBehind || bRTLPortion || bBeforePortion ) 1118 nVisPos++; 1119 } 1120 1121 bGotoEndOfPrevLine = nVisPos < 0; 1122 bGotoStartOfNextLine = nVisPos >= aLine.getLength(); 1123 1124 if ( !bGotoEndOfPrevLine && !bGotoStartOfNextLine ) 1125 { 1126 aPaM.SetIndex( rLine.GetStart() + ubidi_getLogicalIndex( pBidi, nVisPos, &nError ) ); 1127 1128 // RTL portion, stay visually on the left side. 1129 sal_Int32 _nPortionStart; 1130 // sal_uInt16 nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nPortionStart, !bRTLPortion ); 1131 sal_Int32 _nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), _nPortionStart, true ); 1132 const TextPortion& _rTextPortion = pParaPortion->GetTextPortions()[_nTextPortion]; 1133 if ( bVisualToLeft && !bRTLPortion && _rTextPortion.IsRightToLeft() ) 1134 aPaM.SetIndex( aPaM.GetIndex()+1 ); 1135 else if ( !bVisualToLeft && bRTLPortion && ( bWasBehind || !_rTextPortion.IsRightToLeft() ) ) 1136 aPaM.SetIndex( aPaM.GetIndex()+1 ); 1137 1138 pEditView->pImpEditView->SetCursorBidiLevel( _nPortionStart ); 1139 } 1140 } 1141 1142 ubidi_close( pBidi ); 1143 1144 if ( bGotoEndOfPrevLine ) 1145 { 1146 aPaM = CursorUp( aPaM, pEditView ); 1147 if ( aPaM != rPaM ) 1148 aPaM = CursorVisualStartEnd( pEditView, aPaM, false ); 1149 } 1150 else if ( bGotoStartOfNextLine ) 1151 { 1152 aPaM = CursorDown( aPaM, pEditView ); 1153 if ( aPaM != rPaM ) 1154 aPaM = CursorVisualStartEnd( pEditView, aPaM, true ); 1155 } 1156 } 1157 return aPaM; 1158 } 1159 1160 1161 EditPaM ImpEditEngine::CursorLeft( const EditPaM& rPaM, sal_uInt16 nCharacterIteratorMode ) 1162 { 1163 EditPaM aCurPaM( rPaM ); 1164 EditPaM aNewPaM( aCurPaM ); 1165 1166 if ( aCurPaM.GetIndex() ) 1167 { 1168 sal_Int32 nCount = 1; 1169 uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() ); 1170 aNewPaM.SetIndex( 1171 _xBI->previousCharacters( 1172 aNewPaM.GetNode()->GetString(), aNewPaM.GetIndex(), GetLocale( aNewPaM ), nCharacterIteratorMode, nCount, nCount)); 1173 } 1174 else 1175 { 1176 ContentNode* pNode = aCurPaM.GetNode(); 1177 pNode = GetPrevVisNode( pNode ); 1178 if ( pNode ) 1179 { 1180 aNewPaM.SetNode( pNode ); 1181 aNewPaM.SetIndex( pNode->Len() ); 1182 } 1183 } 1184 1185 return aNewPaM; 1186 } 1187 1188 EditPaM ImpEditEngine::CursorRight( const EditPaM& rPaM, sal_uInt16 nCharacterIteratorMode ) 1189 { 1190 EditPaM aCurPaM( rPaM ); 1191 EditPaM aNewPaM( aCurPaM ); 1192 1193 if ( aCurPaM.GetIndex() < aCurPaM.GetNode()->Len() ) 1194 { 1195 uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() ); 1196 sal_Int32 nCount = 1; 1197 aNewPaM.SetIndex( 1198 _xBI->nextCharacters( 1199 aNewPaM.GetNode()->GetString(), aNewPaM.GetIndex(), GetLocale( aNewPaM ), nCharacterIteratorMode, nCount, nCount)); 1200 } 1201 else 1202 { 1203 ContentNode* pNode = aCurPaM.GetNode(); 1204 pNode = GetNextVisNode( pNode ); 1205 if ( pNode ) 1206 { 1207 aNewPaM.SetNode( pNode ); 1208 aNewPaM.SetIndex( 0 ); 1209 } 1210 } 1211 1212 return aNewPaM; 1213 } 1214 1215 EditPaM ImpEditEngine::CursorUp( const EditPaM& rPaM, EditView const * pView ) 1216 { 1217 assert(pView && "No View - No Cursor Movement!"); 1218 1219 const ParaPortion* pPPortion = FindParaPortion( rPaM.GetNode() ); 1220 OSL_ENSURE( pPPortion, "No matching portion found: CursorUp "); 1221 sal_Int32 nLine = pPPortion->GetLineNumber( rPaM.GetIndex() ); 1222 const EditLine& rLine = pPPortion->GetLines()[nLine]; 1223 1224 long nX; 1225 if ( pView->pImpEditView->nTravelXPos == TRAVEL_X_DONTKNOW ) 1226 { 1227 nX = GetXPos( pPPortion, &rLine, rPaM.GetIndex() ); 1228 pView->pImpEditView->nTravelXPos = nX+nOnePixelInRef; 1229 } 1230 else 1231 nX = pView->pImpEditView->nTravelXPos; 1232 1233 EditPaM aNewPaM( rPaM ); 1234 if ( nLine ) // same paragraph 1235 { 1236 const EditLine& rPrevLine = pPPortion->GetLines()[nLine-1]; 1237 aNewPaM.SetIndex( GetChar( pPPortion, &rPrevLine, nX ) ); 1238 // If a previous automatically wrapped line, and one has to be exactly 1239 // at the end of this line, the cursor lands on the current line at the 1240 // beginning. See Problem: Last character of an automatically wrapped 1241 // Row = cursor 1242 if ( aNewPaM.GetIndex() && ( aNewPaM.GetIndex() == rLine.GetStart() ) ) 1243 aNewPaM = CursorLeft( aNewPaM ); 1244 } 1245 else // previous paragraph 1246 { 1247 const ParaPortion* pPrevPortion = GetPrevVisPortion( pPPortion ); 1248 if ( pPrevPortion ) 1249 { 1250 const EditLine& rLine2 = pPrevPortion->GetLines()[pPrevPortion->GetLines().Count()-1]; 1251 aNewPaM.SetNode( pPrevPortion->GetNode() ); 1252 aNewPaM.SetIndex( GetChar( pPrevPortion, &rLine2, nX+nOnePixelInRef ) ); 1253 } 1254 } 1255 1256 return aNewPaM; 1257 } 1258 1259 EditPaM ImpEditEngine::CursorDown( const EditPaM& rPaM, EditView const * pView ) 1260 { 1261 OSL_ENSURE( pView, "No View - No Cursor Movement!" ); 1262 1263 const ParaPortion* pPPortion = FindParaPortion( rPaM.GetNode() ); 1264 OSL_ENSURE( pPPortion, "No matching portion found: CursorDown" ); 1265 sal_Int32 nLine = pPPortion->GetLineNumber( rPaM.GetIndex() ); 1266 1267 long nX; 1268 if ( pView->pImpEditView->nTravelXPos == TRAVEL_X_DONTKNOW ) 1269 { 1270 const EditLine& rLine = pPPortion->GetLines()[nLine]; 1271 nX = GetXPos( pPPortion, &rLine, rPaM.GetIndex() ); 1272 pView->pImpEditView->nTravelXPos = nX+nOnePixelInRef; 1273 } 1274 else 1275 nX = pView->pImpEditView->nTravelXPos; 1276 1277 EditPaM aNewPaM( rPaM ); 1278 if ( nLine < pPPortion->GetLines().Count()-1 ) 1279 { 1280 const EditLine& rNextLine = pPPortion->GetLines()[nLine+1]; 1281 aNewPaM.SetIndex( GetChar( pPPortion, &rNextLine, nX ) ); 1282 // Special treatment, see CursorUp ... 1283 if ( ( aNewPaM.GetIndex() == rNextLine.GetEnd() ) && ( aNewPaM.GetIndex() > rNextLine.GetStart() ) && ( aNewPaM.GetIndex() < pPPortion->GetNode()->Len() ) ) 1284 aNewPaM = CursorLeft( aNewPaM ); 1285 } 1286 else // next paragraph 1287 { 1288 const ParaPortion* pNextPortion = GetNextVisPortion( pPPortion ); 1289 if ( pNextPortion ) 1290 { 1291 const EditLine& rLine = pNextPortion->GetLines()[0]; 1292 aNewPaM.SetNode( pNextPortion->GetNode() ); 1293 // Never at the very end when several lines, because then a line 1294 // below the cursor appears. 1295 aNewPaM.SetIndex( GetChar( pNextPortion, &rLine, nX+nOnePixelInRef ) ); 1296 if ( ( aNewPaM.GetIndex() == rLine.GetEnd() ) && ( aNewPaM.GetIndex() > rLine.GetStart() ) && ( pNextPortion->GetLines().Count() > 1 ) ) 1297 aNewPaM = CursorLeft( aNewPaM ); 1298 } 1299 } 1300 1301 return aNewPaM; 1302 } 1303 1304 EditPaM ImpEditEngine::CursorStartOfLine( const EditPaM& rPaM ) 1305 { 1306 const ParaPortion* pCurPortion = FindParaPortion( rPaM.GetNode() ); 1307 OSL_ENSURE( pCurPortion, "No Portion for the PaM ?" ); 1308 sal_Int32 nLine = pCurPortion->GetLineNumber( rPaM.GetIndex() ); 1309 const EditLine& rLine = pCurPortion->GetLines()[nLine]; 1310 1311 EditPaM aNewPaM( rPaM ); 1312 aNewPaM.SetIndex( rLine.GetStart() ); 1313 return aNewPaM; 1314 } 1315 1316 EditPaM ImpEditEngine::CursorEndOfLine( const EditPaM& rPaM ) 1317 { 1318 const ParaPortion* pCurPortion = FindParaPortion( rPaM.GetNode() ); 1319 OSL_ENSURE( pCurPortion, "No Portion for the PaM ?" ); 1320 sal_Int32 nLine = pCurPortion->GetLineNumber( rPaM.GetIndex() ); 1321 const EditLine& rLine = pCurPortion->GetLines()[nLine]; 1322 1323 EditPaM aNewPaM( rPaM ); 1324 aNewPaM.SetIndex( rLine.GetEnd() ); 1325 if ( rLine.GetEnd() > rLine.GetStart() ) 1326 { 1327 if ( aNewPaM.GetNode()->IsFeature( aNewPaM.GetIndex() - 1 ) ) 1328 { 1329 // When a soft break, be in front of it! 1330 const EditCharAttrib* pNextFeature = aNewPaM.GetNode()->GetCharAttribs().FindFeature( aNewPaM.GetIndex()-1 ); 1331 if ( pNextFeature && ( pNextFeature->GetItem()->Which() == EE_FEATURE_LINEBR ) ) 1332 aNewPaM = CursorLeft( aNewPaM ); 1333 } 1334 else if ( ( aNewPaM.GetNode()->GetChar( aNewPaM.GetIndex() - 1 ) == ' ' ) && ( aNewPaM.GetIndex() != aNewPaM.GetNode()->Len() ) ) 1335 { 1336 // For a Blank in an auto wrapped line, it makes sense, to stand 1337 // in front of it, since the user wants to be after the word. 1338 // If this is changed, special treatment for Pos1 to End! 1339 aNewPaM = CursorLeft( aNewPaM ); 1340 } 1341 } 1342 return aNewPaM; 1343 } 1344 1345 EditPaM ImpEditEngine::CursorStartOfParagraph( const EditPaM& rPaM ) 1346 { 1347 EditPaM aPaM(rPaM); 1348 aPaM.SetIndex(0); 1349 return aPaM; 1350 } 1351 1352 EditPaM ImpEditEngine::CursorEndOfParagraph( const EditPaM& rPaM ) 1353 { 1354 EditPaM aPaM(rPaM); 1355 aPaM.SetIndex(rPaM.GetNode()->Len()); 1356 return aPaM; 1357 } 1358 1359 EditPaM ImpEditEngine::CursorStartOfDoc() 1360 { 1361 EditPaM aPaM( aEditDoc.GetObject( 0 ), 0 ); 1362 return aPaM; 1363 } 1364 1365 EditPaM ImpEditEngine::CursorEndOfDoc() 1366 { 1367 ContentNode* pLastNode = aEditDoc.GetObject( aEditDoc.Count()-1 ); 1368 ParaPortion* pLastPortion = GetParaPortions().SafeGetObject( aEditDoc.Count()-1 ); 1369 OSL_ENSURE( pLastNode && pLastPortion, "CursorEndOfDoc: Node or Portion not found" ); 1370 if (!(pLastNode && pLastPortion)) 1371 return EditPaM(); 1372 1373 if ( !pLastPortion->IsVisible() ) 1374 { 1375 pLastNode = GetPrevVisNode( pLastPortion->GetNode() ); 1376 OSL_ENSURE( pLastNode, "No visible paragraph?" ); 1377 if ( !pLastNode ) 1378 pLastNode = aEditDoc.GetObject( aEditDoc.Count()-1 ); 1379 } 1380 1381 EditPaM aPaM( pLastNode, pLastNode->Len() ); 1382 return aPaM; 1383 } 1384 1385 EditPaM ImpEditEngine::PageUp( const EditPaM& rPaM, EditView const * pView ) 1386 { 1387 tools::Rectangle aRect = PaMtoEditCursor( rPaM ); 1388 Point aTopLeft = aRect.TopLeft(); 1389 aTopLeft.AdjustY( -(pView->GetVisArea().GetHeight() *9/10) ); 1390 aTopLeft.AdjustX(nOnePixelInRef ); 1391 if ( aTopLeft.Y() < 0 ) 1392 { 1393 aTopLeft.setY( 0 ); 1394 } 1395 return GetPaM( aTopLeft ); 1396 } 1397 1398 EditPaM ImpEditEngine::PageDown( const EditPaM& rPaM, EditView const * pView ) 1399 { 1400 tools::Rectangle aRect = PaMtoEditCursor( rPaM ); 1401 Point aBottomRight = aRect.BottomRight(); 1402 aBottomRight.AdjustY(pView->GetVisArea().GetHeight() *9/10 ); 1403 aBottomRight.AdjustX(nOnePixelInRef ); 1404 long nHeight = GetTextHeight(); 1405 if ( aBottomRight.Y() > nHeight ) 1406 { 1407 aBottomRight.setY( nHeight-2 ); 1408 } 1409 return GetPaM( aBottomRight ); 1410 } 1411 1412 EditPaM ImpEditEngine::WordLeft( const EditPaM& rPaM ) 1413 { 1414 const sal_Int32 nCurrentPos = rPaM.GetIndex(); 1415 EditPaM aNewPaM( rPaM ); 1416 if ( nCurrentPos == 0 ) 1417 { 1418 // Previous paragraph... 1419 sal_Int32 nCurPara = aEditDoc.GetPos( aNewPaM.GetNode() ); 1420 ContentNode* pPrevNode = aEditDoc.GetObject( --nCurPara ); 1421 if ( pPrevNode ) 1422 { 1423 aNewPaM.SetNode( pPrevNode ); 1424 aNewPaM.SetIndex( pPrevNode->Len() ); 1425 } 1426 } 1427 else 1428 { 1429 // we need to increase the position by 1 when retrieving the locale 1430 // since the attribute for the char left to the cursor position is returned 1431 EditPaM aTmpPaM( aNewPaM ); 1432 if ( aTmpPaM.GetIndex() < rPaM.GetNode()->Len() ) 1433 aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 ); 1434 lang::Locale aLocale( GetLocale( aTmpPaM ) ); 1435 1436 uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() ); 1437 i18n::Boundary aBoundary = 1438 _xBI->getWordBoundary(aNewPaM.GetNode()->GetString(), nCurrentPos, aLocale, css::i18n::WordType::ANYWORD_IGNOREWHITESPACES, true); 1439 if ( aBoundary.startPos >= nCurrentPos ) 1440 aBoundary = _xBI->previousWord( 1441 aNewPaM.GetNode()->GetString(), nCurrentPos, aLocale, css::i18n::WordType::ANYWORD_IGNOREWHITESPACES); 1442 aNewPaM.SetIndex( ( aBoundary.startPos != -1 ) ? aBoundary.startPos : 0 ); 1443 } 1444 1445 return aNewPaM; 1446 } 1447 1448 EditPaM ImpEditEngine::WordRight( const EditPaM& rPaM, sal_Int16 nWordType ) 1449 { 1450 const sal_Int32 nMax = rPaM.GetNode()->Len(); 1451 EditPaM aNewPaM( rPaM ); 1452 if ( aNewPaM.GetIndex() < nMax ) 1453 { 1454 // we need to increase the position by 1 when retrieving the locale 1455 // since the attribute for the char left to the cursor position is returned 1456 EditPaM aTmpPaM( aNewPaM ); 1457 aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 ); 1458 lang::Locale aLocale( GetLocale( aTmpPaM ) ); 1459 1460 uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() ); 1461 i18n::Boundary aBoundary = _xBI->nextWord( 1462 aNewPaM.GetNode()->GetString(), aNewPaM.GetIndex(), aLocale, nWordType); 1463 aNewPaM.SetIndex( aBoundary.startPos ); 1464 } 1465 // not 'else', maybe the index reached nMax now... 1466 if ( aNewPaM.GetIndex() >= nMax ) 1467 { 1468 // Next paragraph ... 1469 sal_Int32 nCurPara = aEditDoc.GetPos( aNewPaM.GetNode() ); 1470 ContentNode* pNextNode = aEditDoc.GetObject( ++nCurPara ); 1471 if ( pNextNode ) 1472 { 1473 aNewPaM.SetNode( pNextNode ); 1474 aNewPaM.SetIndex( 0 ); 1475 } 1476 } 1477 return aNewPaM; 1478 } 1479 1480 EditPaM ImpEditEngine::StartOfWord( const EditPaM& rPaM ) 1481 { 1482 EditPaM aNewPaM( rPaM ); 1483 1484 // we need to increase the position by 1 when retrieving the locale 1485 // since the attribute for the char left to the cursor position is returned 1486 EditPaM aTmpPaM( aNewPaM ); 1487 if ( aTmpPaM.GetIndex() < rPaM.GetNode()->Len() ) 1488 aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 ); 1489 lang::Locale aLocale( GetLocale( aTmpPaM ) ); 1490 1491 uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() ); 1492 i18n::Boundary aBoundary = _xBI->getWordBoundary( 1493 rPaM.GetNode()->GetString(), rPaM.GetIndex(), aLocale, css::i18n::WordType::ANYWORD_IGNOREWHITESPACES, true); 1494 1495 aNewPaM.SetIndex( aBoundary.startPos ); 1496 return aNewPaM; 1497 } 1498 1499 EditPaM ImpEditEngine::EndOfWord( const EditPaM& rPaM ) 1500 { 1501 EditPaM aNewPaM( rPaM ); 1502 1503 // we need to increase the position by 1 when retrieving the locale 1504 // since the attribute for the char left to the cursor position is returned 1505 EditPaM aTmpPaM( aNewPaM ); 1506 if ( aTmpPaM.GetIndex() < rPaM.GetNode()->Len() ) 1507 aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 ); 1508 lang::Locale aLocale( GetLocale( aTmpPaM ) ); 1509 1510 uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() ); 1511 i18n::Boundary aBoundary = _xBI->getWordBoundary( 1512 rPaM.GetNode()->GetString(), rPaM.GetIndex(), aLocale, css::i18n::WordType::ANYWORD_IGNOREWHITESPACES, true); 1513 1514 aNewPaM.SetIndex( aBoundary.endPos ); 1515 return aNewPaM; 1516 } 1517 1518 EditSelection ImpEditEngine::SelectWord( const EditSelection& rCurSel, sal_Int16 nWordType, bool bAcceptStartOfWord ) 1519 { 1520 EditSelection aNewSel( rCurSel ); 1521 EditPaM aPaM( rCurSel.Max() ); 1522 1523 // we need to increase the position by 1 when retrieving the locale 1524 // since the attribute for the char left to the cursor position is returned 1525 EditPaM aTmpPaM( aPaM ); 1526 if ( aTmpPaM.GetIndex() < aPaM.GetNode()->Len() ) 1527 aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 ); 1528 lang::Locale aLocale( GetLocale( aTmpPaM ) ); 1529 1530 uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() ); 1531 sal_Int16 nType = _xBI->getWordType( 1532 aPaM.GetNode()->GetString(), aPaM.GetIndex(), aLocale); 1533 1534 if ( nType == i18n::WordType::ANY_WORD ) 1535 { 1536 i18n::Boundary aBoundary = _xBI->getWordBoundary( 1537 aPaM.GetNode()->GetString(), aPaM.GetIndex(), aLocale, nWordType, true); 1538 1539 // don't select when cursor at end of word 1540 if ( ( aBoundary.endPos > aPaM.GetIndex() ) && 1541 ( ( aBoundary.startPos < aPaM.GetIndex() ) || ( bAcceptStartOfWord && ( aBoundary.startPos == aPaM.GetIndex() ) ) ) ) 1542 { 1543 aNewSel.Min().SetIndex( aBoundary.startPos ); 1544 aNewSel.Max().SetIndex( aBoundary.endPos ); 1545 } 1546 } 1547 1548 return aNewSel; 1549 } 1550 1551 EditSelection ImpEditEngine::SelectSentence( const EditSelection& rCurSel ) 1552 const 1553 { 1554 uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() ); 1555 const EditPaM& rPaM = rCurSel.Min(); 1556 const ContentNode* pNode = rPaM.GetNode(); 1557 // #i50710# line breaks are marked with 0x01 - the break iterator prefers 0x0a for that 1558 const OUString sParagraph = pNode->GetString().replaceAll("\x01", "\x0a"); 1559 //return Null if search starts at the beginning of the string 1560 sal_Int32 nStart = rPaM.GetIndex() ? _xBI->beginOfSentence( sParagraph, rPaM.GetIndex(), GetLocale( rPaM ) ) : 0; 1561 1562 sal_Int32 nEnd = _xBI->endOfSentence( 1563 pNode->GetString(), rPaM.GetIndex(), GetLocale(rPaM)); 1564 1565 EditSelection aNewSel( rCurSel ); 1566 OSL_ENSURE(pNode->Len() ? (nStart < pNode->Len()) : (nStart == 0), "sentence start index out of range"); 1567 OSL_ENSURE(nEnd <= pNode->Len(), "sentence end index out of range"); 1568 aNewSel.Min().SetIndex( nStart ); 1569 aNewSel.Max().SetIndex( nEnd ); 1570 return aNewSel; 1571 } 1572 1573 bool ImpEditEngine::IsInputSequenceCheckingRequired( sal_Unicode nChar, const EditSelection& rCurSel ) const 1574 { 1575 uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() ); 1576 if (!pCTLOptions) 1577 pCTLOptions.reset( new SvtCTLOptions ); 1578 1579 // get the index that really is first 1580 const sal_Int32 nFirstPos = std::min(rCurSel.Min().GetIndex(), rCurSel.Max().GetIndex()); 1581 1582 bool bIsSequenceChecking = 1583 pCTLOptions->IsCTLFontEnabled() && 1584 pCTLOptions->IsCTLSequenceChecking() && 1585 nFirstPos != 0 && /* first char needs not to be checked */ 1586 _xBI.is() && i18n::ScriptType::COMPLEX == _xBI->getScriptType( OUString( nChar ), 0 ); 1587 1588 return bIsSequenceChecking; 1589 } 1590 1591 static bool lcl_HasStrongLTR ( const OUString& rTxt, sal_Int32 nStart, sal_Int32 nEnd ) 1592 { 1593 for( sal_Int32 nCharIdx = nStart; nCharIdx < nEnd; ++nCharIdx ) 1594 { 1595 const UCharDirection nCharDir = u_charDirection ( rTxt[ nCharIdx ] ); 1596 if ( nCharDir == U_LEFT_TO_RIGHT || 1597 nCharDir == U_LEFT_TO_RIGHT_EMBEDDING || 1598 nCharDir == U_LEFT_TO_RIGHT_OVERRIDE ) 1599 return true; 1600 } 1601 return false; 1602 } 1603 1604 1605 void ImpEditEngine::InitScriptTypes( sal_Int32 nPara ) 1606 { 1607 ParaPortion* pParaPortion = GetParaPortions().SafeGetObject( nPara ); 1608 if (!pParaPortion) 1609 return; 1610 1611 ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos; 1612 rTypes.clear(); 1613 1614 ContentNode* pNode = pParaPortion->GetNode(); 1615 if ( pNode->Len() ) 1616 { 1617 uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() ); 1618 1619 OUString aText = pNode->GetString(); 1620 1621 // To handle fields put the character from the field in the string, 1622 // because endOfScript( ... ) will skip the CH_FEATURE, because this is WEAK 1623 const EditCharAttrib* pField = pNode->GetCharAttribs().FindNextAttrib( EE_FEATURE_FIELD, 0 ); 1624 while ( pField ) 1625 { 1626 const OUString aFldText = static_cast<const EditCharAttribField*>(pField)->GetFieldValue(); 1627 if ( !aFldText.isEmpty() ) 1628 { 1629 aText = aText.replaceAt( pField->GetStart(), 1, aFldText.copy(0,1) ); 1630 short nFldScriptType = _xBI->getScriptType( aFldText, 0 ); 1631 1632 for ( sal_Int32 nCharInField = 1; nCharInField < aFldText.getLength(); nCharInField++ ) 1633 { 1634 short nTmpType = _xBI->getScriptType( aFldText, nCharInField ); 1635 1636 // First char from field wins... 1637 if ( nFldScriptType == i18n::ScriptType::WEAK ) 1638 { 1639 nFldScriptType = nTmpType; 1640 aText = aText.replaceAt( pField->GetStart(), 1, aFldText.copy(nCharInField,1) ); 1641 } 1642 1643 // ... but if the first one is LATIN, and there are CJK or CTL chars too, 1644 // we prefer that ScriptType because we need another font. 1645 if ( ( nTmpType == i18n::ScriptType::ASIAN ) || ( nTmpType == i18n::ScriptType::COMPLEX ) ) 1646 { 1647 aText = aText.replaceAt( pField->GetStart(), 1, aFldText.copy(nCharInField,1) ); 1648 break; 1649 } 1650 } 1651 } 1652 // #112831# Last Field might go from 0xffff to 0x0000 1653 pField = pField->GetEnd() ? pNode->GetCharAttribs().FindNextAttrib( EE_FEATURE_FIELD, pField->GetEnd() ) : nullptr; 1654 } 1655 1656 sal_Int32 nTextLen = aText.getLength(); 1657 1658 sal_Int32 nPos = 0; 1659 short nScriptType = _xBI->getScriptType( aText, nPos ); 1660 rTypes.emplace_back( nScriptType, nPos, nTextLen ); 1661 nPos = _xBI->endOfScript( aText, nPos, nScriptType ); 1662 while ( ( nPos != -1 ) && ( nPos < nTextLen ) ) 1663 { 1664 rTypes.back().nEndPos = nPos; 1665 1666 nScriptType = _xBI->getScriptType( aText, nPos ); 1667 long nEndPos = _xBI->endOfScript( aText, nPos, nScriptType ); 1668 1669 if ( ( nScriptType == i18n::ScriptType::WEAK ) || ( nScriptType == rTypes.back().nScriptType ) ) 1670 { 1671 // Expand last ScriptTypePosInfo, don't create weak or unnecessary portions 1672 rTypes.back().nEndPos = nEndPos; 1673 } 1674 else 1675 { 1676 if ( _xBI->getScriptType( aText, nPos - 1 ) == i18n::ScriptType::WEAK ) 1677 { 1678 switch ( u_charType(aText.iterateCodePoints(&nPos, 0) ) ) { 1679 case U_NON_SPACING_MARK: 1680 case U_ENCLOSING_MARK: 1681 case U_COMBINING_SPACING_MARK: 1682 --nPos; 1683 rTypes.back().nEndPos--; 1684 break; 1685 } 1686 } 1687 rTypes.emplace_back( nScriptType, nPos, nTextLen ); 1688 } 1689 1690 nPos = nEndPos; 1691 } 1692 1693 if ( rTypes[0].nScriptType == i18n::ScriptType::WEAK ) 1694 rTypes[0].nScriptType = ( rTypes.size() > 1 ) ? rTypes[1].nScriptType : SvtLanguageOptions::GetI18NScriptTypeOfLanguage( GetDefaultLanguage() ); 1695 1696 // create writing direction information: 1697 if ( pParaPortion->aWritingDirectionInfos.empty() ) 1698 InitWritingDirections( nPara ); 1699 1700 // i89825: Use CTL font for numbers embedded into an RTL run: 1701 WritingDirectionInfos& rDirInfos = pParaPortion->aWritingDirectionInfos; 1702 for (WritingDirectionInfo & rDirInfo : rDirInfos) 1703 { 1704 const sal_Int32 nStart = rDirInfo.nStartPos; 1705 const sal_Int32 nEnd = rDirInfo.nEndPos; 1706 const sal_uInt8 nCurrDirType = rDirInfo.nType; 1707 1708 if ( nCurrDirType % 2 == UBIDI_RTL || // text in RTL run 1709 ( nCurrDirType > UBIDI_LTR && !lcl_HasStrongLTR( aText, nStart, nEnd ) ) ) // non-strong text in embedded LTR run 1710 { 1711 size_t nIdx = 0; 1712 1713 // Skip entries in ScriptArray which are not inside the RTL run: 1714 while ( nIdx < rTypes.size() && rTypes[nIdx].nStartPos < nStart ) 1715 ++nIdx; 1716 1717 // Remove any entries *inside* the current run: 1718 while (nIdx < rTypes.size() && rTypes[nIdx].nEndPos <= nEnd) 1719 { 1720 // coverity[use_iterator] - we're protected from a bad iterator by the above condition 1721 rTypes.erase(rTypes.begin() + nIdx); 1722 } 1723 1724 // special case: 1725 if(nIdx < rTypes.size() && rTypes[nIdx].nStartPos < nStart && rTypes[nIdx].nEndPos > nEnd) 1726 { 1727 rTypes.insert( rTypes.begin()+nIdx, ScriptTypePosInfo( rTypes[nIdx].nScriptType, nEnd, rTypes[nIdx].nEndPos ) ); 1728 rTypes[nIdx].nEndPos = nStart; 1729 } 1730 1731 if( nIdx ) 1732 rTypes[nIdx - 1].nEndPos = nStart; 1733 1734 rTypes.insert( rTypes.begin()+nIdx, ScriptTypePosInfo( i18n::ScriptType::COMPLEX, nStart, nEnd) ); 1735 ++nIdx; 1736 1737 if( nIdx < rTypes.size() ) 1738 rTypes[nIdx].nStartPos = nEnd; 1739 } 1740 } 1741 } 1742 } 1743 1744 namespace { 1745 1746 struct FindByPos 1747 { 1748 explicit FindByPos(sal_Int32 nPos) 1749 : mnPos(nPos) 1750 { 1751 } 1752 1753 bool operator()(const ScriptTypePosInfos::value_type& rValue) 1754 { 1755 return rValue.nStartPos <= mnPos && rValue.nEndPos >= mnPos; 1756 } 1757 1758 private: 1759 sal_Int32 mnPos; 1760 }; 1761 1762 } 1763 1764 sal_uInt16 ImpEditEngine::GetI18NScriptType( const EditPaM& rPaM, sal_Int32* pEndPos ) const 1765 { 1766 sal_uInt16 nScriptType = 0; 1767 1768 if ( pEndPos ) 1769 *pEndPos = rPaM.GetNode()->Len(); 1770 1771 if ( rPaM.GetNode()->Len() ) 1772 { 1773 sal_Int32 nPara = GetEditDoc().GetPos( rPaM.GetNode() ); 1774 const ParaPortion* pParaPortion = GetParaPortions().SafeGetObject( nPara ); 1775 if (pParaPortion) 1776 { 1777 if ( pParaPortion->aScriptInfos.empty() ) 1778 const_cast<ImpEditEngine*>(this)->InitScriptTypes( nPara ); 1779 1780 const ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos; 1781 1782 const sal_Int32 nPos = rPaM.GetIndex(); 1783 ScriptTypePosInfos::const_iterator itr = std::find_if(rTypes.begin(), rTypes.end(), FindByPos(nPos)); 1784 if(itr != rTypes.end()) 1785 { 1786 nScriptType = itr->nScriptType; 1787 if( pEndPos ) 1788 *pEndPos = itr->nEndPos; 1789 } 1790 } 1791 } 1792 return nScriptType ? nScriptType : SvtLanguageOptions::GetI18NScriptTypeOfLanguage( GetDefaultLanguage() ); 1793 } 1794 1795 SvtScriptType ImpEditEngine::GetItemScriptType( const EditSelection& rSel ) const 1796 { 1797 EditSelection aSel( rSel ); 1798 aSel.Adjust( aEditDoc ); 1799 1800 SvtScriptType nScriptType = SvtScriptType::NONE; 1801 1802 sal_Int32 nStartPara = GetEditDoc().GetPos( aSel.Min().GetNode() ); 1803 sal_Int32 nEndPara = GetEditDoc().GetPos( aSel.Max().GetNode() ); 1804 1805 for ( sal_Int32 nPara = nStartPara; nPara <= nEndPara; nPara++ ) 1806 { 1807 const ParaPortion* pParaPortion = GetParaPortions().SafeGetObject( nPara ); 1808 if (!pParaPortion) 1809 continue; 1810 1811 if ( pParaPortion->aScriptInfos.empty() ) 1812 const_cast<ImpEditEngine*>(this)->InitScriptTypes( nPara ); 1813 1814 const ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos; 1815 1816 // find all the scripts of this range 1817 sal_Int32 nS = ( nPara == nStartPara ) ? aSel.Min().GetIndex() : 0; 1818 sal_Int32 nE = ( nPara == nEndPara ) ? aSel.Max().GetIndex() : pParaPortion->GetNode()->Len(); 1819 1820 //no selection, just bare cursor 1821 if (nStartPara == nEndPara && nS == nE) 1822 { 1823 //If we are not at the start of the paragraph we want the properties of the 1824 //preceding character. Otherwise get the properties of the next (or what the 1825 //next would have if it existed) 1826 if (nS != 0) 1827 --nS; 1828 else 1829 ++nE; 1830 } 1831 1832 for (const ScriptTypePosInfo & rType : rTypes) 1833 { 1834 bool bStartInRange = rType.nStartPos <= nS && nS < rType.nEndPos; 1835 bool bEndInRange = rType.nStartPos < nE && nE <= rType.nEndPos; 1836 1837 if (bStartInRange || bEndInRange) 1838 { 1839 if ( rType.nScriptType != i18n::ScriptType::WEAK ) 1840 nScriptType |= SvtLanguageOptions::FromI18NToSvtScriptType( rType.nScriptType ); 1841 } 1842 } 1843 } 1844 return bool(nScriptType) ? nScriptType : SvtLanguageOptions::GetScriptTypeOfLanguage( GetDefaultLanguage() ); 1845 } 1846 1847 bool ImpEditEngine::IsScriptChange( const EditPaM& rPaM ) const 1848 { 1849 bool bScriptChange = false; 1850 1851 if ( rPaM.GetNode()->Len() ) 1852 { 1853 sal_Int32 nPara = GetEditDoc().GetPos( rPaM.GetNode() ); 1854 const ParaPortion* pParaPortion = GetParaPortions().SafeGetObject( nPara ); 1855 if (pParaPortion) 1856 { 1857 if ( pParaPortion->aScriptInfos.empty() ) 1858 const_cast<ImpEditEngine*>(this)->InitScriptTypes( nPara ); 1859 1860 const ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos; 1861 const sal_Int32 nPos = rPaM.GetIndex(); 1862 for (const ScriptTypePosInfo & rType : rTypes) 1863 { 1864 if ( rType.nStartPos == nPos ) 1865 { 1866 bScriptChange = true; 1867 break; 1868 } 1869 } 1870 } 1871 } 1872 return bScriptChange; 1873 } 1874 1875 bool ImpEditEngine::HasScriptType( sal_Int32 nPara, sal_uInt16 nType ) const 1876 { 1877 bool bTypeFound = false; 1878 1879 const ParaPortion* pParaPortion = GetParaPortions().SafeGetObject( nPara ); 1880 if (pParaPortion) 1881 { 1882 if ( pParaPortion->aScriptInfos.empty() ) 1883 const_cast<ImpEditEngine*>(this)->InitScriptTypes( nPara ); 1884 1885 const ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos; 1886 for ( size_t n = rTypes.size(); n && !bTypeFound; ) 1887 { 1888 if ( rTypes[--n].nScriptType == nType ) 1889 bTypeFound = true; 1890 } 1891 } 1892 return bTypeFound; 1893 } 1894 1895 void ImpEditEngine::InitWritingDirections( sal_Int32 nPara ) 1896 { 1897 ParaPortion* pParaPortion = GetParaPortions().SafeGetObject( nPara ); 1898 if (!pParaPortion) 1899 return; 1900 1901 WritingDirectionInfos& rInfos = pParaPortion->aWritingDirectionInfos; 1902 rInfos.clear(); 1903 1904 if (pParaPortion->GetNode()->Len()) 1905 { 1906 const OUString aText = pParaPortion->GetNode()->GetString(); 1907 1908 // Bidi functions from icu 2.0 1909 1910 UErrorCode nError = U_ZERO_ERROR; 1911 UBiDi* pBidi = ubidi_openSized( aText.getLength(), 0, &nError ); 1912 nError = U_ZERO_ERROR; 1913 1914 const UBiDiLevel nBidiLevel = IsRightToLeft(nPara) ? 1 /*RTL*/ : 0 /*LTR*/; 1915 ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(aText.getStr()), aText.getLength(), nBidiLevel, nullptr, &nError ); 1916 nError = U_ZERO_ERROR; 1917 1918 int32_t nCount = ubidi_countRuns( pBidi, &nError ); 1919 1920 /* ubidi_countRuns can return -1 in case of error */ 1921 if (nCount > 0) 1922 { 1923 int32_t nStart = 0; 1924 int32_t nEnd; 1925 UBiDiLevel nCurrDir; 1926 1927 for (int32_t nIdx = 0; nIdx < nCount; ++nIdx) 1928 { 1929 ubidi_getLogicalRun( pBidi, nStart, &nEnd, &nCurrDir ); 1930 rInfos.emplace_back( nCurrDir, nStart, nEnd ); 1931 nStart = nEnd; 1932 } 1933 } 1934 1935 ubidi_close( pBidi ); 1936 } 1937 1938 // No infos mean ubidi error, default to LTR 1939 if ( rInfos.empty() ) 1940 rInfos.emplace_back( 0, 0, pParaPortion->GetNode()->Len() ); 1941 1942 } 1943 1944 bool ImpEditEngine::IsRightToLeft( sal_Int32 nPara ) const 1945 { 1946 bool bR2L = false; 1947 const SvxFrameDirectionItem* pFrameDirItem = nullptr; 1948 1949 if ( !IsVertical() ) 1950 { 1951 bR2L = GetDefaultHorizontalTextDirection() == EEHorizontalTextDirection::R2L; 1952 pFrameDirItem = &GetParaAttrib( nPara, EE_PARA_WRITINGDIR ); 1953 if ( pFrameDirItem->GetValue() == SvxFrameDirection::Environment ) 1954 { 1955 // #103045# if DefaultHorizontalTextDirection is set, use that value, otherwise pool default. 1956 if ( GetDefaultHorizontalTextDirection() != EEHorizontalTextDirection::Default ) 1957 { 1958 pFrameDirItem = nullptr; // bR2L already set to default horizontal text direction 1959 } 1960 else 1961 { 1962 // Use pool default 1963 pFrameDirItem = &const_cast<ImpEditEngine*>(this)->GetEmptyItemSet().Get( EE_PARA_WRITINGDIR ); 1964 } 1965 } 1966 } 1967 1968 if ( pFrameDirItem ) 1969 bR2L = pFrameDirItem->GetValue() == SvxFrameDirection::Horizontal_RL_TB; 1970 1971 return bR2L; 1972 } 1973 1974 bool ImpEditEngine::HasDifferentRTLLevels( const ContentNode* pNode ) 1975 { 1976 bool bHasDifferentRTLLevels = false; 1977 1978 sal_Int32 nPara = GetEditDoc().GetPos( pNode ); 1979 ParaPortion* pParaPortion = GetParaPortions().SafeGetObject( nPara ); 1980 if (pParaPortion) 1981 { 1982 sal_uInt16 nRTLLevel = IsRightToLeft( nPara ) ? 1 : 0; 1983 for ( sal_Int32 n = 0; n < pParaPortion->GetTextPortions().Count(); n++ ) 1984 { 1985 const TextPortion& rTextPortion = pParaPortion->GetTextPortions()[n]; 1986 if ( rTextPortion.GetRightToLeftLevel() != nRTLLevel ) 1987 { 1988 bHasDifferentRTLLevels = true; 1989 break; 1990 } 1991 } 1992 } 1993 return bHasDifferentRTLLevels; 1994 } 1995 1996 1997 sal_uInt8 ImpEditEngine::GetRightToLeft( sal_Int32 nPara, sal_Int32 nPos, sal_Int32* pStart, sal_Int32* pEnd ) 1998 { 1999 sal_uInt8 nRightToLeft = 0; 2000 2001 ContentNode* pNode = aEditDoc.GetObject( nPara ); 2002 if ( pNode && pNode->Len() ) 2003 { 2004 ParaPortion* pParaPortion = GetParaPortions().SafeGetObject( nPara ); 2005 if (pParaPortion) 2006 { 2007 if ( pParaPortion->aWritingDirectionInfos.empty() ) 2008 InitWritingDirections( nPara ); 2009 2010 WritingDirectionInfos& rDirInfos = pParaPortion->aWritingDirectionInfos; 2011 for (const WritingDirectionInfo & rDirInfo : rDirInfos) 2012 { 2013 if ( ( rDirInfo.nStartPos <= nPos ) && ( rDirInfo.nEndPos >= nPos ) ) 2014 { 2015 nRightToLeft = rDirInfo.nType; 2016 if ( pStart ) 2017 *pStart = rDirInfo.nStartPos; 2018 if ( pEnd ) 2019 *pEnd = rDirInfo.nEndPos; 2020 break; 2021 } 2022 } 2023 } 2024 } 2025 return nRightToLeft; 2026 } 2027 2028 SvxAdjust ImpEditEngine::GetJustification( sal_Int32 nPara ) const 2029 { 2030 SvxAdjust eJustification = SvxAdjust::Left; 2031 2032 if ( !aStatus.IsOutliner() ) 2033 { 2034 eJustification = GetParaAttrib( nPara, EE_PARA_JUST ).GetAdjust(); 2035 2036 if ( IsRightToLeft( nPara ) ) 2037 { 2038 if ( eJustification == SvxAdjust::Left ) 2039 eJustification = SvxAdjust::Right; 2040 else if ( eJustification == SvxAdjust::Right ) 2041 eJustification = SvxAdjust::Left; 2042 } 2043 } 2044 return eJustification; 2045 } 2046 2047 SvxCellJustifyMethod ImpEditEngine::GetJustifyMethod( sal_Int32 nPara ) const 2048 { 2049 const SvxJustifyMethodItem& rItem = GetParaAttrib(nPara, EE_PARA_JUST_METHOD); 2050 return static_cast<SvxCellJustifyMethod>(rItem.GetEnumValue()); 2051 } 2052 2053 SvxCellVerJustify ImpEditEngine::GetVerJustification( sal_Int32 nPara ) const 2054 { 2055 const SvxVerJustifyItem& rItem = GetParaAttrib(nPara, EE_PARA_VER_JUST); 2056 return static_cast<SvxCellVerJustify>(rItem.GetEnumValue()); 2057 } 2058 2059 // Text changes 2060 void ImpEditEngine::ImpRemoveChars( const EditPaM& rPaM, sal_Int32 nChars ) 2061 { 2062 if ( IsUndoEnabled() && !IsInUndo() ) 2063 { 2064 const OUString aStr( rPaM.GetNode()->Copy( rPaM.GetIndex(), nChars ) ); 2065 2066 // Check whether attributes are deleted or changed: 2067 const sal_Int32 nStart = rPaM.GetIndex(); 2068 const sal_Int32 nEnd = nStart + nChars; 2069 const CharAttribList::AttribsType& rAttribs = rPaM.GetNode()->GetCharAttribs().GetAttribs(); 2070 for (const auto & rAttrib : rAttribs) 2071 { 2072 const EditCharAttrib& rAttr = *rAttrib; 2073 if (rAttr.GetEnd() >= nStart && rAttr.GetStart() < nEnd) 2074 { 2075 EditSelection aSel( rPaM ); 2076 aSel.Max().SetIndex( aSel.Max().GetIndex() + nChars ); 2077 InsertUndo( CreateAttribUndo( aSel, GetEmptyItemSet() ) ); 2078 break; // for 2079 } 2080 } 2081 InsertUndo(std::make_unique<EditUndoRemoveChars>(pEditEngine, CreateEPaM(rPaM), aStr)); 2082 } 2083 2084 aEditDoc.RemoveChars( rPaM, nChars ); 2085 } 2086 2087 EditSelection ImpEditEngine::ImpMoveParagraphs( Range aOldPositions, sal_Int32 nNewPos ) 2088 { 2089 aOldPositions.Justify(); 2090 bool bValidAction = ( static_cast<long>(nNewPos) < aOldPositions.Min() ) || ( static_cast<long>(nNewPos) > aOldPositions.Max() ); 2091 OSL_ENSURE( bValidAction, "Move in itself?" ); 2092 OSL_ENSURE( aOldPositions.Max() <= static_cast<long>(GetParaPortions().Count()), "totally over it: MoveParagraphs" ); 2093 2094 EditSelection aSelection; 2095 2096 if ( !bValidAction ) 2097 { 2098 aSelection = aEditDoc.GetStartPaM(); 2099 return aSelection; 2100 } 2101 2102 sal_Int32 nParaCount = GetParaPortions().Count(); 2103 2104 if ( nNewPos >= nParaCount ) 2105 nNewPos = nParaCount; 2106 2107 // Height may change when moving first or last Paragraph 2108 ParaPortion* pRecalc1 = nullptr; 2109 ParaPortion* pRecalc2 = nullptr; 2110 ParaPortion* pRecalc3 = nullptr; 2111 ParaPortion* pRecalc4 = nullptr; 2112 2113 if ( nNewPos == 0 ) // Move to Start 2114 { 2115 pRecalc1 = GetParaPortions()[0]; 2116 pRecalc2 = GetParaPortions()[aOldPositions.Min()]; 2117 2118 } 2119 else if ( nNewPos == nParaCount ) 2120 { 2121 pRecalc1 = GetParaPortions()[nParaCount-1]; 2122 pRecalc2 = GetParaPortions()[aOldPositions.Max()]; 2123 } 2124 2125 if ( aOldPositions.Min() == 0 ) // Move from Start 2126 { 2127 pRecalc3 = GetParaPortions()[0]; 2128 pRecalc4 = GetParaPortions()[aOldPositions.Max()+1]; 2129 } 2130 else if ( aOldPositions.Max() == (nParaCount-1) ) 2131 { 2132 pRecalc3 = GetParaPortions()[aOldPositions.Max()]; 2133 pRecalc4 = GetParaPortions()[aOldPositions.Min()-1]; 2134 } 2135 2136 MoveParagraphsInfo aMoveParagraphsInfo( aOldPositions.Min(), aOldPositions.Max(), nNewPos ); 2137 aBeginMovingParagraphsHdl.Call( aMoveParagraphsInfo ); 2138 2139 if ( IsUndoEnabled() && !IsInUndo()) 2140 InsertUndo(std::make_unique<EditUndoMoveParagraphs>(pEditEngine, aOldPositions, nNewPos)); 2141 2142 // do not lose sight of the Position ! 2143 ParaPortion* pDestPortion = GetParaPortions().SafeGetObject( nNewPos ); 2144 2145 ParaPortionList aTmpPortionList; 2146 for (long i = aOldPositions.Min(); i <= aOldPositions.Max(); i++ ) 2147 { 2148 // always aOldPositions.Min(), since Remove(). 2149 std::unique_ptr<ParaPortion> pTmpPortion = GetParaPortions().Release(aOldPositions.Min()); 2150 aEditDoc.Release( aOldPositions.Min() ); 2151 aTmpPortionList.Append(std::move(pTmpPortion)); 2152 } 2153 2154 sal_Int32 nRealNewPos = pDestPortion ? GetParaPortions().GetPos( pDestPortion ) : GetParaPortions().Count(); 2155 OSL_ENSURE( nRealNewPos != EE_PARA_NOT_FOUND, "ImpMoveParagraphs: Invalid Position!" ); 2156 2157 sal_Int32 i = 0; 2158 while( aTmpPortionList.Count() > 0 ) 2159 { 2160 std::unique_ptr<ParaPortion> pTmpPortion = aTmpPortionList.Release(0); 2161 if ( i == 0 ) 2162 aSelection.Min().SetNode( pTmpPortion->GetNode() ); 2163 2164 aSelection.Max().SetNode( pTmpPortion->GetNode() ); 2165 aSelection.Max().SetIndex( pTmpPortion->GetNode()->Len() ); 2166 2167 ContentNode* pN = pTmpPortion->GetNode(); 2168 aEditDoc.Insert(nRealNewPos+i, pN); 2169 2170 GetParaPortions().Insert(nRealNewPos+i, std::move(pTmpPortion)); 2171 ++i; 2172 } 2173 2174 aEndMovingParagraphsHdl.Call( aMoveParagraphsInfo ); 2175 2176 if ( GetNotifyHdl().IsSet() ) 2177 { 2178 EENotify aNotify( EE_NOTIFY_PARAGRAPHSMOVED ); 2179 aNotify.nParagraph = nNewPos; 2180 aNotify.nParam1 = aOldPositions.Min(); 2181 aNotify.nParam2 = aOldPositions.Max(); 2182 GetNotifyHdl().Call( aNotify ); 2183 } 2184 2185 aEditDoc.SetModified( true ); 2186 2187 if ( pRecalc1 ) 2188 CalcHeight( pRecalc1 ); 2189 if ( pRecalc2 ) 2190 CalcHeight( pRecalc2 ); 2191 if ( pRecalc3 ) 2192 CalcHeight( pRecalc3 ); 2193 if ( pRecalc4 ) 2194 CalcHeight( pRecalc4 ); 2195 2196 #if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG 2197 ParaPortionList::DbgCheck(GetParaPortions(), aEditDoc); 2198 #endif 2199 return aSelection; 2200 } 2201 2202 2203 EditPaM ImpEditEngine::ImpConnectParagraphs( ContentNode* pLeft, ContentNode* pRight, bool bBackward ) 2204 { 2205 OSL_ENSURE( pLeft != pRight, "Join together the same paragraph ?" ); 2206 OSL_ENSURE( aEditDoc.GetPos( pLeft ) != EE_PARA_NOT_FOUND, "Inserted node not found (1)" ); 2207 OSL_ENSURE( aEditDoc.GetPos( pRight ) != EE_PARA_NOT_FOUND, "Inserted node not found (2)" ); 2208 2209 // #i120020# it is possible that left and right are *not* in the desired order (left/right) 2210 // so correct it. This correction is needed, else an invalid SfxLinkUndoAction will be 2211 // created from ConnectParagraphs below. Assert this situation, it should be corrected by the 2212 // caller. 2213 if(aEditDoc.GetPos( pLeft ) > aEditDoc.GetPos( pRight )) 2214 { 2215 OSL_ENSURE(false, "ImpConnectParagraphs with wrong order of pLeft/pRight nodes (!)"); 2216 std::swap(pLeft, pRight); 2217 } 2218 2219 sal_Int32 nParagraphTobeDeleted = aEditDoc.GetPos( pRight ); 2220 aDeletedNodes.push_back(std::make_unique<DeletedNodeInfo>( pRight, nParagraphTobeDeleted )); 2221 2222 GetEditEnginePtr()->ParagraphConnected( aEditDoc.GetPos( pLeft ), aEditDoc.GetPos( pRight ) ); 2223 2224 if ( IsUndoEnabled() && !IsInUndo() ) 2225 { 2226 InsertUndo( std::make_unique<EditUndoConnectParas>(pEditEngine, 2227 aEditDoc.GetPos( pLeft ), pLeft->Len(), 2228 pLeft->GetContentAttribs().GetItems(), pRight->GetContentAttribs().GetItems(), 2229 pLeft->GetStyleSheet(), pRight->GetStyleSheet(), bBackward ) ); 2230 } 2231 2232 if ( bBackward ) 2233 { 2234 pLeft->SetStyleSheet( pRight->GetStyleSheet() ); 2235 pLeft->GetContentAttribs().GetItems().Set( pRight->GetContentAttribs().GetItems() ); 2236 pLeft->GetCharAttribs().GetDefFont() = pRight->GetCharAttribs().GetDefFont(); 2237 } 2238 2239 ParaAttribsChanged( pLeft, true ); 2240 2241 // First search for Portions since pRight is gone after ConnectParagraphs. 2242 ParaPortion* pLeftPortion = FindParaPortion( pLeft ); 2243 OSL_ENSURE( pLeftPortion, "Blind Portion in ImpConnectParagraphs(1)" ); 2244 2245 if ( GetStatus().DoOnlineSpelling() ) 2246 { 2247 sal_Int32 nEnd = pLeft->Len(); 2248 sal_Int32 nInv = nEnd ? nEnd-1 : nEnd; 2249 pLeft->GetWrongList()->ClearWrongs( nInv, static_cast<size_t>(-1), pLeft ); // Possibly remove one 2250 pLeft->GetWrongList()->SetInvalidRange(nInv, nEnd+1); 2251 // Take over misspelled words 2252 WrongList* pRWrongs = pRight->GetWrongList(); 2253 for (auto & elem : *pRWrongs) 2254 { 2255 if (elem.mnStart != 0) // Not a subsequent 2256 { 2257 elem.mnStart = elem.mnStart + nEnd; 2258 elem.mnEnd = elem.mnEnd + nEnd; 2259 pLeft->GetWrongList()->push_back(elem); 2260 } 2261 } 2262 } 2263 2264 if ( IsCallParaInsertedOrDeleted() ) 2265 GetEditEnginePtr()->ParagraphDeleted( nParagraphTobeDeleted ); 2266 2267 EditPaM aPaM = aEditDoc.ConnectParagraphs( pLeft, pRight ); 2268 GetParaPortions().Remove( nParagraphTobeDeleted ); 2269 2270 pLeftPortion->MarkSelectionInvalid( aPaM.GetIndex() ); 2271 2272 // the right node is deleted by EditDoc:ConnectParagraphs(). 2273 if ( GetTextRanger() ) 2274 { 2275 // By joining together the two, the left is although reformatted, 2276 // however if its height does not change then the formatting receives 2277 // the change of the total text height too late... 2278 for ( sal_Int32 n = nParagraphTobeDeleted; n < GetParaPortions().Count(); n++ ) 2279 { 2280 ParaPortion* pPP = GetParaPortions()[n]; 2281 pPP->MarkSelectionInvalid( 0 ); 2282 pPP->GetLines().Reset(); 2283 } 2284 } 2285 2286 TextModified(); 2287 2288 return aPaM; 2289 } 2290 2291 EditPaM ImpEditEngine::DeleteLeftOrRight( const EditSelection& rSel, sal_uInt8 nMode, DeleteMode nDelMode ) 2292 { 2293 OSL_ENSURE( !rSel.DbgIsBuggy( aEditDoc ), "Index out of range in DeleteLeftOrRight" ); 2294 2295 if ( rSel.HasRange() ) // only then Delete Selection 2296 return ImpDeleteSelection( rSel ); 2297 2298 EditPaM aCurPos( rSel.Max() ); 2299 EditPaM aDelStart( aCurPos ); 2300 EditPaM aDelEnd( aCurPos ); 2301 if ( nMode == DEL_LEFT ) 2302 { 2303 if ( nDelMode == DeleteMode::Simple ) 2304 { 2305 sal_uInt16 nCharMode = i18n::CharacterIteratorMode::SKIPCHARACTER; 2306 // Check if we are deleting a CJK ideograph variance sequence (IVS). 2307 sal_Int32 nIndex = aCurPos.GetIndex(); 2308 if (nIndex > 0) 2309 { 2310 const OUString& rString = aCurPos.GetNode()->GetString(); 2311 sal_Int32 nCode = rString.iterateCodePoints(&nIndex, -1); 2312 if (unicode::isIVSSelector(nCode) && nIndex > 0 && 2313 unicode::isCJKIVSCharacter(rString.iterateCodePoints(&nIndex, -1))) 2314 { 2315 nCharMode = i18n::CharacterIteratorMode::SKIPCELL; 2316 } 2317 } 2318 aDelStart = CursorLeft(aCurPos, nCharMode); 2319 } 2320 else if ( nDelMode == DeleteMode::RestOfWord ) 2321 { 2322 aDelStart = StartOfWord( aCurPos ); 2323 if ( aDelStart.GetIndex() == aCurPos.GetIndex() ) 2324 aDelStart = WordLeft( aCurPos ); 2325 } 2326 else // DELMODE_RESTOFCONTENT 2327 { 2328 aDelStart.SetIndex( 0 ); 2329 if ( aDelStart == aCurPos ) 2330 { 2331 // Complete paragraph previous 2332 ContentNode* pPrev = GetPrevVisNode( aCurPos.GetNode() ); 2333 if ( pPrev ) 2334 aDelStart = EditPaM( pPrev, 0 ); 2335 } 2336 } 2337 } 2338 else 2339 { 2340 if ( nDelMode == DeleteMode::Simple ) 2341 { 2342 aDelEnd = CursorRight( aCurPos ); 2343 } 2344 else if ( nDelMode == DeleteMode::RestOfWord ) 2345 { 2346 aDelEnd = EndOfWord( aCurPos ); 2347 2348 if (aDelEnd.GetIndex() == aCurPos.GetIndex()) 2349 { 2350 const sal_Int32 nLen(aCurPos.GetNode()->Len()); 2351 2352 // #i120020# when 0 == nLen, aDelStart needs to be adapted, not 2353 // aDelEnd. This would (and did) lead to a wrong order in the 2354 // ImpConnectParagraphs call later. 2355 if(nLen) 2356 { 2357 // end of para? 2358 if (aDelEnd.GetIndex() == nLen) 2359 { 2360 aDelEnd = WordLeft( aCurPos ); 2361 } 2362 else // there's still sth to delete on the right 2363 { 2364 aDelEnd = EndOfWord( WordRight( aCurPos ) ); 2365 // if there'n no next word... 2366 if (aDelEnd.GetIndex() == nLen ) 2367 { 2368 aDelEnd.SetIndex( nLen ); 2369 } 2370 } 2371 } 2372 else 2373 { 2374 aDelStart = WordLeft(aCurPos); 2375 } 2376 } 2377 } 2378 else // DELMODE_RESTOFCONTENT 2379 { 2380 aDelEnd.SetIndex( aCurPos.GetNode()->Len() ); 2381 if ( aDelEnd == aCurPos ) 2382 { 2383 // Complete paragraph next 2384 ContentNode* pNext = GetNextVisNode( aCurPos.GetNode() ); 2385 if ( pNext ) 2386 aDelEnd = EditPaM( pNext, pNext->Len() ); 2387 } 2388 } 2389 } 2390 2391 // ConnectParagraphs not enough for different Nodes when 2392 // DeleteMode::RestOfContent. 2393 if ( ( nDelMode == DeleteMode::RestOfContent ) || ( aDelStart.GetNode() == aDelEnd.GetNode() ) ) 2394 return ImpDeleteSelection( EditSelection( aDelStart, aDelEnd ) ); 2395 2396 return ImpConnectParagraphs(aDelStart.GetNode(), aDelEnd.GetNode()); 2397 } 2398 2399 EditPaM ImpEditEngine::ImpDeleteSelection(const EditSelection& rCurSel) 2400 { 2401 if ( !rCurSel.HasRange() ) 2402 return rCurSel.Min(); 2403 2404 EditSelection aCurSel(rCurSel); 2405 aCurSel.Adjust( aEditDoc ); 2406 EditPaM aStartPaM(aCurSel.Min()); 2407 EditPaM aEndPaM(aCurSel.Max()); 2408 2409 CursorMoved( aStartPaM.GetNode() ); // only so that newly set Attributes disappear... 2410 CursorMoved( aEndPaM.GetNode() ); // only so that newly set Attributes disappear... 2411 2412 OSL_ENSURE( aStartPaM.GetIndex() <= aStartPaM.GetNode()->Len(), "Index out of range in ImpDeleteSelection" ); 2413 OSL_ENSURE( aEndPaM.GetIndex() <= aEndPaM.GetNode()->Len(), "Index out of range in ImpDeleteSelection" ); 2414 2415 sal_Int32 nStartNode = aEditDoc.GetPos( aStartPaM.GetNode() ); 2416 sal_Int32 nEndNode = aEditDoc.GetPos( aEndPaM.GetNode() ); 2417 2418 OSL_ENSURE( nEndNode != EE_PARA_NOT_FOUND, "Start > End ?!" ); 2419 OSL_ENSURE( nStartNode <= nEndNode, "Start > End ?!" ); 2420 2421 // Remove all nodes in between .... 2422 for ( sal_Int32 z = nStartNode+1; z < nEndNode; z++ ) 2423 { 2424 // Always nStartNode+1, due to Remove()! 2425 ImpRemoveParagraph( nStartNode+1 ); 2426 } 2427 2428 if ( aStartPaM.GetNode() != aEndPaM.GetNode() ) 2429 { 2430 // The Rest of the StartNodes... 2431 ImpRemoveChars( aStartPaM, aStartPaM.GetNode()->Len() - aStartPaM.GetIndex() ); 2432 ParaPortion* pPortion = FindParaPortion( aStartPaM.GetNode() ); 2433 OSL_ENSURE( pPortion, "Blind Portion in ImpDeleteSelection(3)" ); 2434 pPortion->MarkSelectionInvalid( aStartPaM.GetIndex() ); 2435 2436 // The beginning of the EndNodes.... 2437 const sal_Int32 nChars = aEndPaM.GetIndex(); 2438 aEndPaM.SetIndex( 0 ); 2439 ImpRemoveChars( aEndPaM, nChars ); 2440 pPortion = FindParaPortion( aEndPaM.GetNode() ); 2441 OSL_ENSURE( pPortion, "Blind Portion in ImpDeleteSelection(4)" ); 2442 pPortion->MarkSelectionInvalid( 0 ); 2443 // Join together.... 2444 aStartPaM = ImpConnectParagraphs( aStartPaM.GetNode(), aEndPaM.GetNode() ); 2445 } 2446 else 2447 { 2448 ImpRemoveChars( aStartPaM, aEndPaM.GetIndex() - aStartPaM.GetIndex() ); 2449 ParaPortion* pPortion = FindParaPortion( aStartPaM.GetNode() ); 2450 OSL_ENSURE( pPortion, "Blind Portion in ImpDeleteSelection(5)" ); 2451 pPortion->MarkInvalid( aEndPaM.GetIndex(), aStartPaM.GetIndex() - aEndPaM.GetIndex() ); 2452 } 2453 2454 UpdateSelections(); 2455 TextModified(); 2456 return aStartPaM; 2457 } 2458 2459 void ImpEditEngine::ImpRemoveParagraph( sal_Int32 nPara ) 2460 { 2461 ContentNode* pNode = aEditDoc.GetObject( nPara ); 2462 ContentNode* pNextNode = aEditDoc.GetObject( nPara+1 ); 2463 2464 OSL_ENSURE( pNode, "Blind Node in ImpRemoveParagraph" ); 2465 2466 aDeletedNodes.push_back(std::make_unique<DeletedNodeInfo>( pNode, nPara )); 2467 2468 // The node is managed by the undo and possibly destroyed! 2469 aEditDoc.Release( nPara ); 2470 GetParaPortions().Remove( nPara ); 2471 2472 if ( IsCallParaInsertedOrDeleted() ) 2473 { 2474 GetEditEnginePtr()->ParagraphDeleted( nPara ); 2475 } 2476 2477 // Extra-Space may be determined again in the following. For 2478 // ParaAttribsChanged the paragraph is unfortunately formatted again, 2479 // however this method should not be time critical! 2480 if ( pNextNode ) 2481 ParaAttribsChanged( pNextNode ); 2482 2483 if ( IsUndoEnabled() && !IsInUndo() ) 2484 InsertUndo(std::make_unique<EditUndoDelContent>(pEditEngine, pNode, nPara)); 2485 else 2486 { 2487 aEditDoc.RemoveItemsFromPool(*pNode); 2488 if ( pNode->GetStyleSheet() ) 2489 EndListening( *pNode->GetStyleSheet() ); 2490 delete pNode; 2491 } 2492 } 2493 2494 EditPaM ImpEditEngine::AutoCorrect( const EditSelection& rCurSel, sal_Unicode c, 2495 bool bOverwrite, vcl::Window const * pFrameWin ) 2496 { 2497 // i.e. Calc has special needs regarding a leading single quotation mark 2498 // when starting cell input. 2499 if (c == '\'' && !IsReplaceLeadingSingleQuotationMark() && 2500 rCurSel.Min() == rCurSel.Max() && rCurSel.Max().GetIndex() == 0) 2501 { 2502 return InsertTextUserInput( rCurSel, c, bOverwrite ); 2503 } 2504 2505 EditSelection aSel( rCurSel ); 2506 SvxAutoCorrect* pAutoCorrect = SvxAutoCorrCfg::Get().GetAutoCorrect(); 2507 if ( pAutoCorrect ) 2508 { 2509 if ( aSel.HasRange() ) 2510 aSel = ImpDeleteSelection( rCurSel ); 2511 2512 // #i78661 allow application to turn off capitalization of 2513 // start sentence explicitly. 2514 // (This is done by setting IsFirstWordCapitalization to sal_False.) 2515 bool bOldCapitalStartSentence = pAutoCorrect->IsAutoCorrFlag( ACFlags::CapitalStartSentence ); 2516 if (!IsFirstWordCapitalization()) 2517 { 2518 ESelection aESel( CreateESel(aSel) ); 2519 EditSelection aFirstWordSel; 2520 EditSelection aSecondWordSel; 2521 if (aESel.nEndPara == 0) // is this the first para? 2522 { 2523 // select first word... 2524 // start by checking if para starts with word. 2525 aFirstWordSel = SelectWord( CreateSel(ESelection()) ); 2526 if (aFirstWordSel.Min().GetIndex() == 0 && aFirstWordSel.Max().GetIndex() == 0) 2527 { 2528 // para does not start with word -> select next/first word 2529 EditPaM aRightWord( WordRight( aFirstWordSel.Max() ) ); 2530 aFirstWordSel = SelectWord( EditSelection( aRightWord ) ); 2531 } 2532 2533 // select second word 2534 // (sometimes aSel might not point to the end of the first word 2535 // but to some following char like '.'. ':', ... 2536 // In those cases we need aSecondWordSel to see if aSel 2537 // will actually effect the first word.) 2538 EditPaM aRight2Word( WordRight( aFirstWordSel.Max() ) ); 2539 aSecondWordSel = SelectWord( EditSelection( aRight2Word ) ); 2540 } 2541 bool bIsFirstWordInFirstPara = aESel.nEndPara == 0 && 2542 aFirstWordSel.Max().GetIndex() <= aSel.Max().GetIndex() && 2543 aSel.Max().GetIndex() <= aSecondWordSel.Min().GetIndex(); 2544 2545 if (bIsFirstWordInFirstPara) 2546 pAutoCorrect->SetAutoCorrFlag( ACFlags::CapitalStartSentence, IsFirstWordCapitalization() ); 2547 } 2548 2549 ContentNode* pNode = aSel.Max().GetNode(); 2550 const sal_Int32 nIndex = aSel.Max().GetIndex(); 2551 EdtAutoCorrDoc aAuto(pEditEngine, pNode, nIndex, c); 2552 // FIXME: this _must_ be called with reference to the actual node text! 2553 OUString const& rNodeString(pNode->GetString()); 2554 pAutoCorrect->DoAutoCorrect( 2555 aAuto, rNodeString, nIndex, c, !bOverwrite, mbNbspRunNext, pFrameWin ); 2556 aSel.Max().SetIndex( aAuto.GetCursor() ); 2557 2558 // #i78661 since the SvxAutoCorrect object used here is 2559 // shared we need to reset the value to its original state. 2560 pAutoCorrect->SetAutoCorrFlag( ACFlags::CapitalStartSentence, bOldCapitalStartSentence ); 2561 } 2562 return aSel.Max(); 2563 } 2564 2565 2566 EditPaM ImpEditEngine::InsertTextUserInput( const EditSelection& rCurSel, 2567 sal_Unicode c, bool bOverwrite ) 2568 { 2569 OSL_ENSURE( c != '\t', "Tab for InsertText ?" ); 2570 OSL_ENSURE( c != '\n', "Word wrapping for InsertText ?"); 2571 2572 EditPaM aPaM( rCurSel.Min() ); 2573 2574 bool bDoOverwrite = bOverwrite && 2575 ( aPaM.GetIndex() < aPaM.GetNode()->Len() ); 2576 2577 bool bUndoAction = ( rCurSel.HasRange() || bDoOverwrite ); 2578 2579 if ( bUndoAction ) 2580 UndoActionStart( EDITUNDO_INSERT ); 2581 2582 if ( rCurSel.HasRange() ) 2583 { 2584 aPaM = ImpDeleteSelection( rCurSel ); 2585 } 2586 else if ( bDoOverwrite ) 2587 { 2588 // If selected, then do not also overwrite a character! 2589 EditSelection aTmpSel( aPaM ); 2590 aTmpSel.Max().SetIndex( aTmpSel.Max().GetIndex()+1 ); 2591 OSL_ENSURE( !aTmpSel.DbgIsBuggy( aEditDoc ), "Overwrite: Wrong selection! "); 2592 ImpDeleteSelection( aTmpSel ); 2593 } 2594 2595 if ( aPaM.GetNode()->Len() < MAXCHARSINPARA ) 2596 { 2597 if (IsInputSequenceCheckingRequired( c, rCurSel )) 2598 { 2599 uno::Reference < i18n::XExtendedInputSequenceChecker > _xISC( ImplGetInputSequenceChecker() ); 2600 if (!pCTLOptions) 2601 pCTLOptions.reset( new SvtCTLOptions ); 2602 2603 if (_xISC) 2604 { 2605 const sal_Int32 nTmpPos = aPaM.GetIndex(); 2606 sal_Int16 nCheckMode = pCTLOptions->IsCTLSequenceCheckingRestricted() ? 2607 i18n::InputSequenceCheckMode::STRICT : i18n::InputSequenceCheckMode::BASIC; 2608 2609 // the text that needs to be checked is only the one 2610 // before the current cursor position 2611 const OUString aOldText( aPaM.GetNode()->Copy(0, nTmpPos) ); 2612 OUString aNewText( aOldText ); 2613 if (pCTLOptions->IsCTLSequenceCheckingTypeAndReplace()) 2614 { 2615 _xISC->correctInputSequence(aNewText, nTmpPos - 1, c, nCheckMode); 2616 2617 // find position of first character that has changed 2618 sal_Int32 nOldLen = aOldText.getLength(); 2619 sal_Int32 nNewLen = aNewText.getLength(); 2620 const sal_Unicode *pOldTxt = aOldText.getStr(); 2621 const sal_Unicode *pNewTxt = aNewText.getStr(); 2622 sal_Int32 nChgPos = 0; 2623 while ( nChgPos < nOldLen && nChgPos < nNewLen && 2624 pOldTxt[nChgPos] == pNewTxt[nChgPos] ) 2625 ++nChgPos; 2626 2627 const OUString aChgText( aNewText.copy( nChgPos ) ); 2628 2629 // select text from first pos to be changed to current pos 2630 EditSelection aSel( EditPaM( aPaM.GetNode(), nChgPos ), aPaM ); 2631 2632 if (!aChgText.isEmpty()) 2633 return InsertText( aSel, aChgText ); // implicitly handles undo 2634 else 2635 return aPaM; 2636 } 2637 else 2638 { 2639 // should the character be ignored (i.e. not get inserted) ? 2640 if (!_xISC->checkInputSequence( aOldText, nTmpPos - 1, c, nCheckMode )) 2641 return aPaM; // nothing to be done -> no need for undo 2642 } 2643 } 2644 2645 // at this point now we will insert the character 'normally' some lines below... 2646 } 2647 2648 if ( IsUndoEnabled() && !IsInUndo() ) 2649 { 2650 std::unique_ptr<EditUndoInsertChars> pNewUndo(new EditUndoInsertChars(pEditEngine, CreateEPaM(aPaM), OUString(c))); 2651 bool bTryMerge = !bDoOverwrite && ( c != ' ' ); 2652 InsertUndo( std::move(pNewUndo), bTryMerge ); 2653 } 2654 2655 aEditDoc.InsertText( aPaM, OUString(c) ); 2656 ParaPortion* pPortion = FindParaPortion( aPaM.GetNode() ); 2657 OSL_ENSURE( pPortion, "Blind Portion in InsertText" ); 2658 pPortion->MarkInvalid( aPaM.GetIndex(), 1 ); 2659 aPaM.SetIndex( aPaM.GetIndex()+1 ); // does not do EditDoc-Method anymore 2660 } 2661 2662 TextModified(); 2663 2664 if ( bUndoAction ) 2665 UndoActionEnd(); 2666 2667 return aPaM; 2668 } 2669 2670 EditPaM ImpEditEngine::ImpInsertText(const EditSelection& aCurSel, const OUString& rStr) 2671 { 2672 UndoActionStart( EDITUNDO_INSERT ); 2673 2674 EditPaM aPaM; 2675 if ( aCurSel.HasRange() ) 2676 aPaM = ImpDeleteSelection( aCurSel ); 2677 else 2678 aPaM = aCurSel.Max(); 2679 2680 EditPaM aCurPaM( aPaM ); // for the Invalidate 2681 2682 // get word boundaries in order to clear possible WrongList entries 2683 // and invalidate all the necessary text (everything after and including the 2684 // start of the word) 2685 // #i107201# do the expensive SelectWord call only if online spelling is active 2686 EditSelection aCurWord; 2687 if ( GetStatus().DoOnlineSpelling() ) 2688 aCurWord = SelectWord( aCurPaM, i18n::WordType::DICTIONARY_WORD ); 2689 2690 OUString aText(convertLineEnd(rStr, LINEEND_LF)); 2691 if (utl::ConfigManager::IsFuzzing()) //tab expansion performance in editeng is appalling 2692 aText = aText.replaceAll("\t","-"); 2693 SfxVoidItem aTabItem( EE_FEATURE_TAB ); 2694 2695 // Converts to linesep = \n 2696 // Token LINE_SEP query, 2697 // since the MAC-Compiler makes something else from \n ! 2698 2699 sal_Int32 nStart = 0; 2700 while ( nStart < aText.getLength() ) 2701 { 2702 sal_Int32 nEnd = aText.indexOf( LINE_SEP, nStart ); 2703 if ( nEnd == -1 ) 2704 nEnd = aText.getLength(); // not dereference! 2705 2706 // Start == End => empty line 2707 if ( nEnd > nStart ) 2708 { 2709 OUString aLine = aText.copy( nStart, nEnd-nStart ); 2710 sal_Int32 nExistingChars = aPaM.GetNode()->Len(); 2711 sal_Int32 nChars = nExistingChars + aLine.getLength(); 2712 if (nChars > MAXCHARSINPARA) 2713 { 2714 sal_Int32 nMaxNewChars = std::max<sal_Int32>(0, MAXCHARSINPARA - nExistingChars); 2715 nEnd -= ( aLine.getLength() - nMaxNewChars ); // Then the characters end up in the next paragraph. 2716 aLine = aLine.copy( 0, nMaxNewChars ); // Delete the Rest... 2717 } 2718 if ( IsUndoEnabled() && !IsInUndo() ) 2719 InsertUndo(std::make_unique<EditUndoInsertChars>(pEditEngine, CreateEPaM(aPaM), aLine)); 2720 // Tabs ? 2721 if ( aLine.indexOf( '\t' ) == -1 ) 2722 aPaM = aEditDoc.InsertText( aPaM, aLine ); 2723 else 2724 { 2725 sal_Int32 nStart2 = 0; 2726 while ( nStart2 < aLine.getLength() ) 2727 { 2728 sal_Int32 nEnd2 = aLine.indexOf( "\t", nStart2 ); 2729 if ( nEnd2 == -1 ) 2730 nEnd2 = aLine.getLength(); // not dereference! 2731 2732 if ( nEnd2 > nStart2 ) 2733 aPaM = aEditDoc.InsertText( aPaM, aLine.copy( nStart2, nEnd2-nStart2 ) ); 2734 if ( nEnd2 < aLine.getLength() ) 2735 { 2736 aPaM = aEditDoc.InsertFeature( aPaM, aTabItem ); 2737 } 2738 nStart2 = nEnd2+1; 2739 } 2740 } 2741 ParaPortion* pPortion = FindParaPortion( aPaM.GetNode() ); 2742 OSL_ENSURE( pPortion, "Blind Portion in InsertText" ); 2743 2744 if ( GetStatus().DoOnlineSpelling() ) 2745 { 2746 // now remove the Wrongs (red spell check marks) from both words... 2747 WrongList *pWrongs = aCurPaM.GetNode()->GetWrongList(); 2748 if (pWrongs && !pWrongs->empty()) 2749 pWrongs->ClearWrongs( aCurWord.Min().GetIndex(), aPaM.GetIndex(), aPaM.GetNode() ); 2750 // ... and mark both words as 'to be checked again' 2751 pPortion->MarkInvalid( aCurWord.Min().GetIndex(), aLine.getLength() ); 2752 } 2753 else 2754 pPortion->MarkInvalid( aCurPaM.GetIndex(), aLine.getLength() ); 2755 } 2756 if ( nEnd < aText.getLength() ) 2757 aPaM = ImpInsertParaBreak( aPaM ); 2758 2759 nStart = nEnd+1; 2760 } 2761 2762 UndoActionEnd(); 2763 2764 TextModified(); 2765 return aPaM; 2766 } 2767 2768 EditPaM ImpEditEngine::ImpFastInsertText( EditPaM aPaM, const OUString& rStr ) 2769 { 2770 OSL_ENSURE( rStr.indexOf( 0x0A ) == -1, "FastInsertText: Newline not allowed! "); 2771 OSL_ENSURE( rStr.indexOf( 0x0D ) == -1, "FastInsertText: Newline not allowed! "); 2772 OSL_ENSURE( rStr.indexOf( '\t' ) == -1, "FastInsertText: Newline not allowed! "); 2773 2774 if ( ( aPaM.GetNode()->Len() + rStr.getLength() ) < MAXCHARSINPARA ) 2775 { 2776 if ( IsUndoEnabled() && !IsInUndo() ) 2777 InsertUndo(std::make_unique<EditUndoInsertChars>(pEditEngine, CreateEPaM(aPaM), rStr)); 2778 2779 aPaM = aEditDoc.InsertText( aPaM, rStr ); 2780 TextModified(); 2781 } 2782 else 2783 { 2784 aPaM = ImpInsertText( aPaM, rStr ); 2785 } 2786 2787 return aPaM; 2788 } 2789 2790 EditPaM ImpEditEngine::ImpInsertFeature(const EditSelection& rCurSel, const SfxPoolItem& rItem) 2791 { 2792 EditPaM aPaM; 2793 if ( rCurSel.HasRange() ) 2794 aPaM = ImpDeleteSelection( rCurSel ); 2795 else 2796 aPaM = rCurSel.Max(); 2797 2798 if ( aPaM.GetIndex() >= SAL_MAX_INT32-1 ) 2799 return aPaM; 2800 2801 if ( IsUndoEnabled() && !IsInUndo() ) 2802 InsertUndo(std::make_unique<EditUndoInsertFeature>(pEditEngine, CreateEPaM(aPaM), rItem)); 2803 aPaM = aEditDoc.InsertFeature( aPaM, rItem ); 2804 UpdateFields(); 2805 2806 ParaPortion* pPortion = FindParaPortion( aPaM.GetNode() ); 2807 OSL_ENSURE( pPortion, "Blind Portion in InsertFeature" ); 2808 pPortion->MarkInvalid( aPaM.GetIndex()-1, 1 ); 2809 2810 TextModified(); 2811 2812 return aPaM; 2813 } 2814 2815 EditPaM ImpEditEngine::ImpInsertParaBreak( const EditSelection& rCurSel ) 2816 { 2817 EditPaM aPaM; 2818 if ( rCurSel.HasRange() ) 2819 aPaM = ImpDeleteSelection( rCurSel ); 2820 else 2821 aPaM = rCurSel.Max(); 2822 2823 return ImpInsertParaBreak( aPaM ); 2824 } 2825 2826 EditPaM ImpEditEngine::ImpInsertParaBreak( EditPaM& rPaM, bool bKeepEndingAttribs ) 2827 { 2828 if ( aEditDoc.Count() >= EE_PARA_MAX_COUNT ) 2829 { 2830 SAL_WARN( "editeng", "ImpEditEngine::ImpInsertParaBreak - can't process more than " 2831 << EE_PARA_MAX_COUNT << " paragraphs!"); 2832 return rPaM; 2833 } 2834 2835 if ( IsUndoEnabled() && !IsInUndo() ) 2836 InsertUndo(std::make_unique<EditUndoSplitPara>(pEditEngine, aEditDoc.GetPos(rPaM.GetNode()), rPaM.GetIndex())); 2837 2838 EditPaM aPaM( aEditDoc.InsertParaBreak( rPaM, bKeepEndingAttribs ) ); 2839 2840 if ( GetStatus().DoOnlineSpelling() ) 2841 { 2842 sal_Int32 nEnd = rPaM.GetNode()->Len(); 2843 aPaM.GetNode()->CreateWrongList(); 2844 WrongList* pLWrongs = rPaM.GetNode()->GetWrongList(); 2845 WrongList* pRWrongs = aPaM.GetNode()->GetWrongList(); 2846 // take over misspelled words: 2847 for (auto & elem : *pLWrongs) 2848 { 2849 // Correct only if really a word gets overlapped in the process of 2850 // Spell checking 2851 if (elem.mnStart > static_cast<size_t>(nEnd)) 2852 { 2853 pRWrongs->push_back(elem); 2854 editeng::MisspellRange& rRWrong = pRWrongs->back(); 2855 rRWrong.mnStart = rRWrong.mnStart - nEnd; 2856 rRWrong.mnEnd = rRWrong.mnEnd - nEnd; 2857 } 2858 else if (elem.mnStart < static_cast<size_t>(nEnd) && elem.mnEnd > static_cast<size_t>(nEnd)) 2859 elem.mnEnd = nEnd; 2860 } 2861 sal_Int32 nInv = nEnd ? nEnd-1 : nEnd; 2862 if ( nEnd ) 2863 pLWrongs->SetInvalidRange(nInv, nEnd); 2864 else 2865 pLWrongs->SetValid(); 2866 pRWrongs->SetValid(); 2867 pRWrongs->SetInvalidRange(0, 1); // Only test the first word 2868 } 2869 2870 ParaPortion* pPortion = FindParaPortion( rPaM.GetNode() ); 2871 OSL_ENSURE( pPortion, "Blind Portion in ImpInsertParaBreak" ); 2872 pPortion->MarkInvalid( rPaM.GetIndex(), 0 ); 2873 2874 // Optimization: Do not place unnecessarily many getPos to Listen! 2875 // Here, as in undo, but also in all other methods. 2876 sal_Int32 nPos = GetParaPortions().GetPos( pPortion ); 2877 ParaPortion* pNewPortion = new ParaPortion( aPaM.GetNode() ); 2878 GetParaPortions().Insert(nPos+1, std::unique_ptr<ParaPortion>(pNewPortion)); 2879 ParaAttribsChanged( pNewPortion->GetNode() ); 2880 if ( IsCallParaInsertedOrDeleted() ) 2881 GetEditEnginePtr()->ParagraphInserted( nPos+1 ); 2882 2883 CursorMoved( rPaM.GetNode() ); // if empty Attributes have emerged. 2884 TextModified(); 2885 return aPaM; 2886 } 2887 2888 EditPaM ImpEditEngine::ImpFastInsertParagraph( sal_Int32 nPara ) 2889 { 2890 if ( IsUndoEnabled() && !IsInUndo() ) 2891 { 2892 if ( nPara ) 2893 { 2894 OSL_ENSURE( aEditDoc.GetObject( nPara-1 ), "FastInsertParagraph: Prev does not exist" ); 2895 InsertUndo(std::make_unique<EditUndoSplitPara>(pEditEngine, nPara-1, aEditDoc.GetObject( nPara-1 )->Len())); 2896 } 2897 else 2898 InsertUndo(std::make_unique<EditUndoSplitPara>(pEditEngine, 0, 0)); 2899 } 2900 2901 ContentNode* pNode = new ContentNode( aEditDoc.GetItemPool() ); 2902 // If flat mode, then later no Font is set: 2903 pNode->GetCharAttribs().GetDefFont() = aEditDoc.GetDefFont(); 2904 2905 if ( GetStatus().DoOnlineSpelling() ) 2906 pNode->CreateWrongList(); 2907 2908 aEditDoc.Insert(nPara, pNode); 2909 2910 GetParaPortions().Insert(nPara, std::make_unique<ParaPortion>( pNode )); 2911 if ( IsCallParaInsertedOrDeleted() ) 2912 GetEditEnginePtr()->ParagraphInserted( nPara ); 2913 2914 return EditPaM( pNode, 0 ); 2915 } 2916 2917 EditPaM ImpEditEngine::InsertParaBreak(const EditSelection& rCurSel) 2918 { 2919 EditPaM aPaM(ImpInsertParaBreak(rCurSel)); 2920 if ( aStatus.DoAutoIndenting() ) 2921 { 2922 sal_Int32 nPara = aEditDoc.GetPos( aPaM.GetNode() ); 2923 OSL_ENSURE( nPara > 0, "AutoIndenting: Error!" ); 2924 const OUString aPrevParaText( GetEditDoc().GetParaAsString( nPara-1 ) ); 2925 sal_Int32 n = 0; 2926 while ( ( n < aPrevParaText.getLength() ) && 2927 ( ( aPrevParaText[n] == ' ' ) || ( aPrevParaText[n] == '\t' ) ) ) 2928 { 2929 if ( aPrevParaText[n] == '\t' ) 2930 aPaM = ImpInsertFeature( aPaM, SfxVoidItem( EE_FEATURE_TAB ) ); 2931 else 2932 aPaM = ImpInsertText( aPaM, OUString(aPrevParaText[n]) ); 2933 n++; 2934 } 2935 2936 } 2937 return aPaM; 2938 } 2939 2940 EditPaM ImpEditEngine::InsertTab(const EditSelection& rCurSel) 2941 { 2942 EditPaM aPaM( ImpInsertFeature(rCurSel, SfxVoidItem(EE_FEATURE_TAB ))); 2943 return aPaM; 2944 } 2945 2946 EditPaM ImpEditEngine::InsertField(const EditSelection& rCurSel, const SvxFieldItem& rFld) 2947 { 2948 return ImpInsertFeature(rCurSel, rFld); 2949 } 2950 2951 bool ImpEditEngine::UpdateFields() 2952 { 2953 bool bChanges = false; 2954 sal_Int32 nParas = GetEditDoc().Count(); 2955 for ( sal_Int32 nPara = 0; nPara < nParas; nPara++ ) 2956 { 2957 bool bChangesInPara = false; 2958 ContentNode* pNode = GetEditDoc().GetObject( nPara ); 2959 OSL_ENSURE( pNode, "NULL-Pointer in Doc" ); 2960 CharAttribList::AttribsType& rAttribs = pNode->GetCharAttribs().GetAttribs(); 2961 for (std::unique_ptr<EditCharAttrib> & rAttrib : rAttribs) 2962 { 2963 EditCharAttrib& rAttr = *rAttrib; 2964 if (rAttr.Which() == EE_FEATURE_FIELD) 2965 { 2966 EditCharAttribField& rField = static_cast<EditCharAttribField&>(rAttr); 2967 std::unique_ptr<EditCharAttribField> pCurrent(new EditCharAttribField(rField)); 2968 rField.Reset(); 2969 2970 if (!aStatus.MarkNonUrlFields() && !aStatus.MarkUrlFields()) 2971 ; // nothing marked 2972 else if (aStatus.MarkNonUrlFields() && aStatus.MarkUrlFields()) 2973 rField.GetFieldColor() = GetColorConfig().GetColorValue( svtools::WRITERFIELDSHADINGS ).nColor; 2974 else 2975 { 2976 bool bURL = false; 2977 if (const SvxFieldItem* pFieldItem = dynamic_cast<const SvxFieldItem*>(rField.GetItem())) 2978 { 2979 if (const SvxFieldData* pFieldData = pFieldItem->GetField()) 2980 bURL = (dynamic_cast<const SvxURLField* >(pFieldData) != nullptr); 2981 } 2982 if ((bURL && aStatus.MarkUrlFields()) || (!bURL && aStatus.MarkNonUrlFields())) 2983 rField.GetFieldColor() = GetColorConfig().GetColorValue( svtools::WRITERFIELDSHADINGS ).nColor; 2984 } 2985 2986 const OUString aFldValue = 2987 GetEditEnginePtr()->CalcFieldValue( 2988 static_cast<const SvxFieldItem&>(*rField.GetItem()), 2989 nPara, rField.GetStart(), rField.GetTextColor(), rField.GetFieldColor()); 2990 2991 rField.SetFieldValue(aFldValue); 2992 if (rField != *pCurrent) 2993 { 2994 bChanges = true; 2995 bChangesInPara = true; 2996 } 2997 } 2998 } 2999 if ( bChangesInPara ) 3000 { 3001 // If possible be more precise when invalidate. 3002 ParaPortion* pPortion = GetParaPortions()[nPara]; 3003 OSL_ENSURE( pPortion, "NULL-Pointer in Doc" ); 3004 pPortion->MarkSelectionInvalid( 0 ); 3005 } 3006 } 3007 return bChanges; 3008 } 3009 3010 EditPaM ImpEditEngine::InsertLineBreak(const EditSelection& aCurSel) 3011 { 3012 EditPaM aPaM( ImpInsertFeature( aCurSel, SfxVoidItem( EE_FEATURE_LINEBR ) ) ); 3013 return aPaM; 3014 } 3015 3016 3017 // Helper functions 3018 3019 tools::Rectangle ImpEditEngine::PaMtoEditCursor( EditPaM aPaM, GetCursorFlags nFlags ) 3020 { 3021 OSL_ENSURE( GetUpdateMode(), "Must not be reached when Update=FALSE: PaMtoEditCursor" ); 3022 3023 tools::Rectangle aEditCursor; 3024 long nY = 0; 3025 for ( sal_Int32 nPortion = 0; nPortion < GetParaPortions().Count(); nPortion++ ) 3026 { 3027 ParaPortion* pPortion = GetParaPortions()[nPortion]; 3028 ContentNode* pNode = pPortion->GetNode(); 3029 OSL_ENSURE( pNode, "Invalid Node in Portion!" ); 3030 if ( pNode != aPaM.GetNode() ) 3031 { 3032 nY += pPortion->GetHeight(); 3033 } 3034 else 3035 { 3036 aEditCursor = GetEditCursor( pPortion, aPaM.GetIndex(), nFlags ); 3037 aEditCursor.AdjustTop(nY ); 3038 aEditCursor.AdjustBottom(nY ); 3039 return aEditCursor; 3040 } 3041 } 3042 OSL_FAIL( "Portion not found!" ); 3043 return aEditCursor; 3044 } 3045 3046 EditPaM ImpEditEngine::GetPaM( Point aDocPos, bool bSmart ) 3047 { 3048 OSL_ENSURE( GetUpdateMode(), "Must not be reached when Update=FALSE: GetPaM" ); 3049 3050 long nY = 0; 3051 EditPaM aPaM; 3052 sal_Int32 nPortion; 3053 for ( nPortion = 0; nPortion < GetParaPortions().Count(); nPortion++ ) 3054 { 3055 ParaPortion* pPortion = GetParaPortions()[nPortion]; 3056 const long nTmpHeight = pPortion->GetHeight(); // should also be correct for !bVisible! 3057 nY += nTmpHeight; 3058 if ( nY > aDocPos.Y() ) 3059 { 3060 nY -= nTmpHeight; 3061 aDocPos.AdjustY( -nY ); 3062 // Skip invisible Portions: 3063 while ( pPortion && !pPortion->IsVisible() ) 3064 { 3065 nPortion++; 3066 pPortion = GetParaPortions().SafeGetObject( nPortion ); 3067 } 3068 SAL_WARN_IF(!pPortion, "editeng", "worrying lack of any visible paragraph"); 3069 if (!pPortion) 3070 return aPaM; 3071 return GetPaM(pPortion, aDocPos, bSmart); 3072 3073 } 3074 } 3075 // Then search for the last visible: 3076 nPortion = GetParaPortions().Count()-1; 3077 while ( nPortion && !GetParaPortions()[nPortion]->IsVisible() ) 3078 nPortion--; 3079 3080 OSL_ENSURE( GetParaPortions()[nPortion]->IsVisible(), "No visible paragraph found: GetPaM" ); 3081 aPaM.SetNode( GetParaPortions()[nPortion]->GetNode() ); 3082 aPaM.SetIndex( GetParaPortions()[nPortion]->GetNode()->Len() ); 3083 return aPaM; 3084 } 3085 3086 sal_uInt32 ImpEditEngine::GetTextHeight() const 3087 { 3088 OSL_ENSURE( GetUpdateMode(), "Should not be used for Update=FALSE: GetTextHeight" ); 3089 OSL_ENSURE( IsFormatted() || IsFormatting(), "GetTextHeight: Not formatted" ); 3090 return nCurTextHeight; 3091 } 3092 3093 sal_uInt32 ImpEditEngine::CalcTextWidth( bool bIgnoreExtraSpace ) 3094 { 3095 // If still not formatted and not in the process. 3096 // Will be brought in the formatting for AutoPageSize. 3097 if ( !IsFormatted() && !IsFormatting() ) 3098 FormatDoc(); 3099 3100 sal_uInt32 nMaxWidth = 0; 3101 3102 // Over all the paragraphs ... 3103 3104 sal_Int32 nParas = GetParaPortions().Count(); 3105 for ( sal_Int32 nPara = 0; nPara < nParas; nPara++ ) 3106 { 3107 nMaxWidth = std::max(nMaxWidth, CalcParaWidth(nPara, bIgnoreExtraSpace)); 3108 } 3109 3110 return nMaxWidth; 3111 } 3112 3113 sal_uInt32 ImpEditEngine::CalcParaWidth( sal_Int32 nPara, bool bIgnoreExtraSpace ) 3114 { 3115 // If still not formatted and not in the process. 3116 // Will be brought in the formatting for AutoPageSize. 3117 if ( !IsFormatted() && !IsFormatting() ) 3118 FormatDoc(); 3119 3120 long nMaxWidth = 0; 3121 3122 // Over all the paragraphs ... 3123 3124 OSL_ENSURE( 0 <= nPara && nPara < GetParaPortions().Count(), "CalcParaWidth: Out of range" ); 3125 ParaPortion* pPortion = GetParaPortions()[nPara]; 3126 if ( pPortion && pPortion->IsVisible() ) 3127 { 3128 const SvxLRSpaceItem& rLRItem = GetLRSpaceItem( pPortion->GetNode() ); 3129 sal_Int32 nSpaceBeforeAndMinLabelWidth = GetSpaceBeforeAndMinLabelWidth( pPortion->GetNode() ); 3130 3131 3132 // On the lines of the paragraph ... 3133 3134 sal_Int32 nLines = pPortion->GetLines().Count(); 3135 for ( sal_Int32 nLine = 0; nLine < nLines; nLine++ ) 3136 { 3137 EditLine& rLine = pPortion->GetLines()[nLine]; 3138 // nCurWidth = pLine->GetStartPosX(); 3139 // For Center- or Right- alignment it depends on the paper 3140 // width, here not preferred. I general, it is best not leave it 3141 // to StartPosX, also the right indents have to be taken into 3142 // account! 3143 long nCurWidth = GetXValue( rLRItem.GetTextLeft() + nSpaceBeforeAndMinLabelWidth ); 3144 if ( nLine == 0 ) 3145 { 3146 long nFI = GetXValue( rLRItem.GetTextFirstLineOfst() ); 3147 nCurWidth -= nFI; 3148 if ( pPortion->GetBulletX() > nCurWidth ) 3149 { 3150 nCurWidth += nFI; // LI? 3151 if ( pPortion->GetBulletX() > nCurWidth ) 3152 nCurWidth = pPortion->GetBulletX(); 3153 } 3154 } 3155 nCurWidth += GetXValue( rLRItem.GetRight() ); 3156 nCurWidth += CalcLineWidth( pPortion, &rLine, bIgnoreExtraSpace ); 3157 if ( nCurWidth > nMaxWidth ) 3158 { 3159 nMaxWidth = nCurWidth; 3160 } 3161 } 3162 } 3163 3164 nMaxWidth++; // widen it, because in CreateLines for >= is wrapped. 3165 return static_cast<sal_uInt32>(nMaxWidth); 3166 } 3167 3168 sal_uInt32 ImpEditEngine::CalcLineWidth( ParaPortion* pPortion, EditLine* pLine, bool bIgnoreExtraSpace ) 3169 { 3170 sal_Int32 nPara = GetEditDoc().GetPos( pPortion->GetNode() ); 3171 3172 // #114278# Saving both layout mode and language (since I'm 3173 // potentially changing both) 3174 GetRefDevice()->Push( PushFlags::TEXTLAYOUTMODE|PushFlags::TEXTLANGUAGE ); 3175 3176 ImplInitLayoutMode( GetRefDevice(), nPara, -1 ); 3177 3178 SvxAdjust eJustification = GetJustification( nPara ); 3179 3180 // Calculation of the width without the Indents ... 3181 sal_uInt32 nWidth = 0; 3182 sal_Int32 nPos = pLine->GetStart(); 3183 for ( sal_Int32 nTP = pLine->GetStartPortion(); nTP <= pLine->GetEndPortion(); nTP++ ) 3184 { 3185 const TextPortion& rTextPortion = pPortion->GetTextPortions()[nTP]; 3186 switch ( rTextPortion.GetKind() ) 3187 { 3188 case PortionKind::FIELD: 3189 case PortionKind::HYPHENATOR: 3190 case PortionKind::TAB: 3191 { 3192 nWidth += rTextPortion.GetSize().Width(); 3193 } 3194 break; 3195 case PortionKind::TEXT: 3196 { 3197 if ( ( eJustification != SvxAdjust::Block ) || ( !bIgnoreExtraSpace ) ) 3198 { 3199 nWidth += rTextPortion.GetSize().Width(); 3200 } 3201 else 3202 { 3203 SvxFont aTmpFont( pPortion->GetNode()->GetCharAttribs().GetDefFont() ); 3204 SeekCursor( pPortion->GetNode(), nPos+1, aTmpFont ); 3205 aTmpFont.SetPhysFont( GetRefDevice() ); 3206 ImplInitDigitMode(GetRefDevice(), aTmpFont.GetLanguage()); 3207 nWidth += aTmpFont.QuickGetTextSize( GetRefDevice(), pPortion->GetNode()->GetString(), nPos, rTextPortion.GetLen() ).Width(); 3208 } 3209 } 3210 break; 3211 case PortionKind::LINEBREAK: break; 3212 } 3213 nPos = nPos + rTextPortion.GetLen(); 3214 } 3215 3216 GetRefDevice()->Pop(); 3217 3218 return nWidth; 3219 } 3220 3221 sal_uInt32 ImpEditEngine::GetTextHeightNTP() const 3222 { 3223 DBG_ASSERT( GetUpdateMode(), "Should not be used for Update=FALSE: GetTextHeight" ); 3224 DBG_ASSERT( IsFormatted() || IsFormatting(), "GetTextHeight: Not formatted" ); 3225 return nCurTextHeightNTP; 3226 } 3227 3228 sal_uInt32 ImpEditEngine::CalcTextHeight( sal_uInt32* pHeightNTP ) 3229 { 3230 OSL_ENSURE( GetUpdateMode(), "Should not be used when Update=FALSE: CalcTextHeight" ); 3231 sal_uInt32 nY = 0; 3232 sal_uInt32 nPH; 3233 sal_uInt32 nEmptyHeight = 0; 3234 for ( sal_Int32 nPortion = 0; nPortion < GetParaPortions().Count(); nPortion++ ) { 3235 ParaPortion* pPortion = GetParaPortions()[nPortion]; 3236 nPH = pPortion->GetHeight(); 3237 nY += nPH; 3238 if( pHeightNTP ) { 3239 if ( pPortion->IsEmpty() ) 3240 nEmptyHeight += nPH; 3241 else 3242 nEmptyHeight = 0; 3243 } 3244 } 3245 3246 if ( pHeightNTP ) 3247 *pHeightNTP = nY - nEmptyHeight; 3248 3249 return nY; 3250 } 3251 3252 sal_Int32 ImpEditEngine::GetLineCount( sal_Int32 nParagraph ) const 3253 { 3254 OSL_ENSURE( 0 <= nParagraph && nParagraph < GetParaPortions().Count(), "GetLineCount: Out of range" ); 3255 const ParaPortion* pPPortion = GetParaPortions().SafeGetObject( nParagraph ); 3256 OSL_ENSURE( pPPortion, "Paragraph not found: GetLineCount" ); 3257 if ( pPPortion ) 3258 return pPPortion->GetLines().Count(); 3259 3260 return -1; 3261 } 3262 3263 sal_Int32 ImpEditEngine::GetLineLen( sal_Int32 nParagraph, sal_Int32 nLine ) const 3264 { 3265 OSL_ENSURE( 0 <= nParagraph && nParagraph < GetParaPortions().Count(), "GetLineLen: Out of range" ); 3266 const ParaPortion* pPPortion = GetParaPortions().SafeGetObject( nParagraph ); 3267 OSL_ENSURE( pPPortion, "Paragraph not found: GetLineLen" ); 3268 if ( pPPortion && ( nLine < pPPortion->GetLines().Count() ) ) 3269 { 3270 const EditLine& rLine = pPPortion->GetLines()[nLine]; 3271 return rLine.GetLen(); 3272 } 3273 3274 return -1; 3275 } 3276 3277 void ImpEditEngine::GetLineBoundaries( /*out*/sal_Int32 &rStart, /*out*/sal_Int32 &rEnd, sal_Int32 nParagraph, sal_Int32 nLine ) const 3278 { 3279 OSL_ENSURE( 0 <= nParagraph && nParagraph < GetParaPortions().Count(), "GetLineCount: Out of range" ); 3280 const ParaPortion* pPPortion = GetParaPortions().SafeGetObject( nParagraph ); 3281 OSL_ENSURE( pPPortion, "Paragraph not found: GetLineBoundaries" ); 3282 rStart = rEnd = -1; // default values in case of error 3283 if ( pPPortion && ( nLine < pPPortion->GetLines().Count() ) ) 3284 { 3285 const EditLine& rLine = pPPortion->GetLines()[nLine]; 3286 rStart = rLine.GetStart(); 3287 rEnd = rLine.GetEnd(); 3288 } 3289 } 3290 3291 sal_Int32 ImpEditEngine::GetLineNumberAtIndex( sal_Int32 nPara, sal_Int32 nIndex ) const 3292 { 3293 sal_Int32 nLineNo = -1; 3294 const ContentNode* pNode = GetEditDoc().GetObject( nPara ); 3295 OSL_ENSURE( pNode, "GetLineNumberAtIndex: invalid paragraph index" ); 3296 if (pNode) 3297 { 3298 // we explicitly allow for the index to point at the character right behind the text 3299 const bool bValidIndex = /*0 <= nIndex &&*/ nIndex <= pNode->Len(); 3300 OSL_ENSURE( bValidIndex, "GetLineNumberAtIndex: invalid index" ); 3301 const sal_Int32 nLineCount = GetLineCount( nPara ); 3302 if (nIndex == pNode->Len()) 3303 nLineNo = nLineCount > 0 ? nLineCount - 1 : 0; 3304 else if (bValidIndex) // nIndex < pNode->Len() 3305 { 3306 sal_Int32 nStart = -1, nEnd = -1; 3307 for (sal_Int32 i = 0; i < nLineCount && nLineNo == -1; ++i) 3308 { 3309 GetLineBoundaries( nStart, nEnd, nPara, i ); 3310 if (nStart >= 0 && nStart <= nIndex && nEnd >= 0 && nIndex < nEnd) 3311 nLineNo = i; 3312 } 3313 } 3314 } 3315 return nLineNo; 3316 } 3317 3318 sal_uInt16 ImpEditEngine::GetLineHeight( sal_Int32 nParagraph, sal_Int32 nLine ) 3319 { 3320 OSL_ENSURE( 0 <= nParagraph && nParagraph < GetParaPortions().Count(), "GetLineCount: Out of range" ); 3321 ParaPortion* pPPortion = GetParaPortions().SafeGetObject( nParagraph ); 3322 OSL_ENSURE( pPPortion, "Paragraph not found: GetLineHeight" ); 3323 if ( pPPortion && ( nLine < pPPortion->GetLines().Count() ) ) 3324 { 3325 const EditLine& rLine = pPPortion->GetLines()[nLine]; 3326 return rLine.GetHeight(); 3327 } 3328 3329 return 0xFFFF; 3330 } 3331 3332 sal_uInt32 ImpEditEngine::GetParaHeight( sal_Int32 nParagraph ) 3333 { 3334 sal_uInt32 nHeight = 0; 3335 3336 ParaPortion* pPPortion = GetParaPortions().SafeGetObject( nParagraph ); 3337 OSL_ENSURE( pPPortion, "Paragraph not found: GetParaHeight" ); 3338 3339 if ( pPPortion ) 3340 nHeight = pPPortion->GetHeight(); 3341 3342 return nHeight; 3343 } 3344 3345 void ImpEditEngine::UpdateSelections() 3346 { 3347 // Check whether one of the selections is at a deleted node... 3348 // If the node is valid, the index has yet to be examined! 3349 for (EditView* pView : aEditViews) 3350 { 3351 EditSelection aCurSel( pView->pImpEditView->GetEditSelection() ); 3352 bool bChanged = false; 3353 for (std::unique_ptr<DeletedNodeInfo> & aDeletedNode : aDeletedNodes) 3354 { 3355 const DeletedNodeInfo& rInf = *aDeletedNode; 3356 if ( ( aCurSel.Min().GetNode() == rInf.GetNode() ) || 3357 ( aCurSel.Max().GetNode() == rInf.GetNode() ) ) 3358 { 3359 // Use ParaPortions, as now also hidden paragraphs have to be 3360 // taken into account! 3361 sal_Int32 nPara = rInf.GetPosition(); 3362 if (!GetParaPortions().SafeGetObject(nPara)) // Last paragraph 3363 { 3364 nPara = GetParaPortions().Count()-1; 3365 } 3366 assert(GetParaPortions()[nPara] && "Empty Document in UpdateSelections ?"); 3367 // Do not end up from a hidden paragraph: 3368 sal_Int32 nCurPara = nPara; 3369 sal_Int32 nLastPara = GetParaPortions().Count()-1; 3370 while ( nPara <= nLastPara && !GetParaPortions()[nPara]->IsVisible() ) 3371 nPara++; 3372 if ( nPara > nLastPara ) // then also backwards ... 3373 { 3374 nPara = nCurPara; 3375 while ( nPara && !GetParaPortions()[nPara]->IsVisible() ) 3376 nPara--; 3377 } 3378 OSL_ENSURE( GetParaPortions()[nPara]->IsVisible(), "No visible paragraph found: UpdateSelections" ); 3379 3380 ParaPortion* pParaPortion = GetParaPortions()[nPara]; 3381 EditSelection aTmpSelection( EditPaM( pParaPortion->GetNode(), 0 ) ); 3382 pView->pImpEditView->SetEditSelection( aTmpSelection ); 3383 bChanged=true; 3384 break; // for loop 3385 } 3386 } 3387 if ( !bChanged ) 3388 { 3389 // Check Index if node shrunk. 3390 if ( aCurSel.Min().GetIndex() > aCurSel.Min().GetNode()->Len() ) 3391 { 3392 aCurSel.Min().SetIndex( aCurSel.Min().GetNode()->Len() ); 3393 pView->pImpEditView->SetEditSelection( aCurSel ); 3394 } 3395 if ( aCurSel.Max().GetIndex() > aCurSel.Max().GetNode()->Len() ) 3396 { 3397 aCurSel.Max().SetIndex( aCurSel.Max().GetNode()->Len() ); 3398 pView->pImpEditView->SetEditSelection( aCurSel ); 3399 } 3400 } 3401 } 3402 aDeletedNodes.clear(); 3403 } 3404 3405 EditSelection ImpEditEngine::ConvertSelection( 3406 sal_Int32 nStartPara, sal_Int32 nStartPos, sal_Int32 nEndPara, sal_Int32 nEndPos ) 3407 { 3408 EditSelection aNewSelection; 3409 3410 // Start... 3411 ContentNode* pNode = aEditDoc.GetObject( nStartPara ); 3412 sal_Int32 nIndex = nStartPos; 3413 if ( !pNode ) 3414 { 3415 pNode = aEditDoc[ aEditDoc.Count()-1 ]; 3416 nIndex = pNode->Len(); 3417 } 3418 else if ( nIndex > pNode->Len() ) 3419 nIndex = pNode->Len(); 3420 3421 aNewSelection.Min().SetNode( pNode ); 3422 aNewSelection.Min().SetIndex( nIndex ); 3423 3424 // End... 3425 pNode = aEditDoc.GetObject( nEndPara ); 3426 nIndex = nEndPos; 3427 if ( !pNode ) 3428 { 3429 pNode = aEditDoc[ aEditDoc.Count()-1 ]; 3430 nIndex = pNode->Len(); 3431 } 3432 else if ( nIndex > pNode->Len() ) 3433 nIndex = pNode->Len(); 3434 3435 aNewSelection.Max().SetNode( pNode ); 3436 aNewSelection.Max().SetIndex( nIndex ); 3437 3438 return aNewSelection; 3439 } 3440 3441 void ImpEditEngine::SetActiveView( EditView* pView ) 3442 { 3443 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 3444 // Actually, now bHasVisSel and HideSelection would be necessary !!! 3445 3446 if ( pView == pActiveView ) 3447 return; 3448 3449 if ( pActiveView && pActiveView->HasSelection() ) 3450 pActiveView->pImpEditView->DrawSelectionXOR(); 3451 3452 pActiveView = pView; 3453 3454 if ( pActiveView && pActiveView->HasSelection() ) 3455 pActiveView->pImpEditView->DrawSelectionXOR(); 3456 3457 // NN: Quick fix for #78668#: 3458 // When editing of a cell in Calc is ended, the edit engine is not deleted, 3459 // only the edit views are removed. If mpIMEInfos is still set in that case, 3460 // mpIMEInfos->aPos points to an invalid selection. 3461 // -> reset mpIMEInfos now 3462 // (probably something like this is necessary whenever the content is modified 3463 // from the outside) 3464 3465 if ( !pView && mpIMEInfos ) 3466 { 3467 mpIMEInfos.reset(); 3468 } 3469 } 3470 3471 uno::Reference< datatransfer::XTransferable > ImpEditEngine::CreateTransferable( const EditSelection& rSelection ) 3472 { 3473 EditSelection aSelection( rSelection ); 3474 aSelection.Adjust( GetEditDoc() ); 3475 3476 EditDataObject* pDataObj = new EditDataObject; 3477 uno::Reference< datatransfer::XTransferable > xDataObj = pDataObj; 3478 3479 pDataObj->GetString() = convertLineEnd(GetSelected(aSelection), GetSystemLineEnd()); // System specific 3480 3481 WriteRTF( pDataObj->GetRTFStream(), aSelection ); 3482 pDataObj->GetRTFStream().Seek( 0 ); 3483 3484 WriteXML( pDataObj->GetODFStream(), aSelection ); 3485 pDataObj->GetODFStream().Seek( 0 ); 3486 3487 //Dumping the ODFStream to a XML file for testing purpose 3488 /* 3489 std::filebuf afilebuf; 3490 afilebuf.open ("gsoc17_clipboard_test.xml",std::ios::out); 3491 std::ostream os(&afilebuf); 3492 os.write((const char*)(pDataObj->GetODFStream().GetBuffer()), pDataObj->GetODFStream().remainingSize()); 3493 afilebuf.close(); 3494 */ 3495 //dumping ends 3496 3497 if ( ( aSelection.Min().GetNode() == aSelection.Max().GetNode() ) 3498 && ( aSelection.Max().GetIndex() == (aSelection.Min().GetIndex()+1) ) ) 3499 { 3500 const EditCharAttrib* pAttr = aSelection.Min().GetNode()->GetCharAttribs(). 3501 FindFeature( aSelection.Min().GetIndex() ); 3502 if ( pAttr && 3503 ( pAttr->GetStart() == aSelection.Min().GetIndex() ) && 3504 ( pAttr->Which() == EE_FEATURE_FIELD ) ) 3505 { 3506 const SvxFieldItem* pField = static_cast<const SvxFieldItem*>(pAttr->GetItem()); 3507 const SvxFieldData* pFld = pField->GetField(); 3508 if ( auto pUrlField = dynamic_cast<const SvxURLField* >(pFld) ) 3509 { 3510 // Office-Bookmark 3511 pDataObj->GetURL() = pUrlField->GetURL(); 3512 } 3513 } 3514 } 3515 3516 return xDataObj; 3517 } 3518 3519 EditSelection ImpEditEngine::PasteText( uno::Reference< datatransfer::XTransferable > const & rxDataObj, const OUString& rBaseURL, const EditPaM& rPaM, bool bUseSpecial ) 3520 { 3521 EditSelection aNewSelection( rPaM ); 3522 3523 if ( !rxDataObj.is() ) 3524 return aNewSelection; 3525 3526 datatransfer::DataFlavor aFlavor; 3527 bool bDone = false; 3528 3529 if ( bUseSpecial ) 3530 { 3531 // XML 3532 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT, aFlavor ); 3533 if ( rxDataObj->isDataFlavorSupported( aFlavor ) ) 3534 { 3535 try 3536 { 3537 uno::Any aData = rxDataObj->getTransferData( aFlavor ); 3538 uno::Sequence< sal_Int8 > aSeq; 3539 aData >>= aSeq; 3540 { 3541 SvMemoryStream aODFStream( aSeq.getArray(), aSeq.getLength(), StreamMode::READ ); 3542 aNewSelection = Read( aODFStream, rBaseURL, EETextFormat::Xml, rPaM ); 3543 } 3544 bDone = true; 3545 } 3546 catch( const css::uno::Exception&) 3547 { 3548 TOOLS_WARN_EXCEPTION( "editeng", "Unable to paste EDITENGINE_ODF_TEXT_FLAT" ); 3549 } 3550 } 3551 3552 if ( !bDone ) 3553 { 3554 // RTF 3555 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::RTF, aFlavor ); 3556 // RICHTEXT 3557 datatransfer::DataFlavor aFlavorRichtext; 3558 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::RICHTEXT, aFlavorRichtext ); 3559 bool bRtfSupported = rxDataObj->isDataFlavorSupported( aFlavor ); 3560 bool bRichtextSupported = rxDataObj->isDataFlavorSupported( aFlavorRichtext ); 3561 if ( bRtfSupported || bRichtextSupported ) 3562 { 3563 if(bRichtextSupported) 3564 { 3565 aFlavor = aFlavorRichtext; 3566 } 3567 try 3568 { 3569 uno::Any aData = rxDataObj->getTransferData( aFlavor ); 3570 uno::Sequence< sal_Int8 > aSeq; 3571 aData >>= aSeq; 3572 { 3573 SvMemoryStream aRTFStream( aSeq.getArray(), aSeq.getLength(), StreamMode::READ ); 3574 aNewSelection = Read( aRTFStream, rBaseURL, EETextFormat::Rtf, rPaM ); 3575 } 3576 bDone = true; 3577 } 3578 catch( const css::uno::Exception& ) 3579 { 3580 } 3581 } 3582 } 3583 } 3584 if ( !bDone ) 3585 { 3586 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor ); 3587 if ( rxDataObj->isDataFlavorSupported( aFlavor ) ) 3588 { 3589 try 3590 { 3591 uno::Any aData = rxDataObj->getTransferData( aFlavor ); 3592 OUString aText; 3593 aData >>= aText; 3594 aNewSelection = ImpInsertText( rPaM, aText ); 3595 } 3596 catch( ... ) 3597 { 3598 ; // #i9286# can happen, even if isDataFlavorSupported returns true... 3599 } 3600 } 3601 } 3602 3603 return aNewSelection; 3604 } 3605 3606 Range ImpEditEngine::GetInvalidYOffsets( ParaPortion* pPortion ) 3607 { 3608 Range aRange( 0, 0 ); 3609 3610 if ( pPortion->IsVisible() ) 3611 { 3612 const SvxULSpaceItem& rULSpace = pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_ULSPACE ); 3613 const SvxLineSpacingItem& rLSItem = pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL ); 3614 sal_uInt16 nSBL = ( rLSItem.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Fix ) 3615 ? GetYValue( rLSItem.GetInterLineSpace() ) : 0; 3616 3617 // only from the top ... 3618 sal_Int32 nFirstInvalid = -1; 3619 sal_Int32 nLine; 3620 for ( nLine = 0; nLine < pPortion->GetLines().Count(); nLine++ ) 3621 { 3622 const EditLine& rL = pPortion->GetLines()[nLine]; 3623 if ( rL.IsInvalid() ) 3624 { 3625 nFirstInvalid = nLine; 3626 break; 3627 } 3628 if ( nLine && !aStatus.IsOutliner() ) // not the first line 3629 aRange.Min() += nSBL; 3630 aRange.Min() += rL.GetHeight(); 3631 } 3632 OSL_ENSURE( nFirstInvalid != -1, "No invalid line found in GetInvalidYOffset(1)" ); 3633 3634 3635 // Syndicate and more ... 3636 aRange.Max() = aRange.Min(); 3637 aRange.Max() += pPortion->GetFirstLineOffset(); 3638 if (nFirstInvalid >= 0) // Only if the first line is invalid 3639 aRange.Min() = aRange.Max(); 3640 3641 sal_Int32 nLastInvalid = pPortion->GetLines().Count()-1; 3642 if (nFirstInvalid >= 0) 3643 { 3644 for ( nLine = nFirstInvalid; nLine < pPortion->GetLines().Count(); nLine++ ) 3645 { 3646 const EditLine& rL = pPortion->GetLines()[nLine]; 3647 if ( rL.IsValid() ) 3648 { 3649 nLastInvalid = nLine; 3650 break; 3651 } 3652 if ( nLine && !aStatus.IsOutliner() ) 3653 aRange.Max() += nSBL; 3654 aRange.Max() += rL.GetHeight(); 3655 } 3656 3657 sal_uInt16 nPropLineSpace = rLSItem.GetPropLineSpace(); 3658 if ( ( rLSItem.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Prop ) 3659 && nPropLineSpace && ( nPropLineSpace < 100 ) ) 3660 { 3661 const EditLine& rL = pPortion->GetLines()[nFirstInvalid]; 3662 long n = rL.GetTxtHeight() * ( 100L - nPropLineSpace ); 3663 n /= 100; 3664 aRange.Min() -= n; 3665 aRange.Max() += n; 3666 } 3667 3668 if ( ( nLastInvalid == pPortion->GetLines().Count()-1 ) && ( !aStatus.IsOutliner() ) ) 3669 aRange.Max() += GetYValue( rULSpace.GetLower() ); 3670 } 3671 } 3672 return aRange; 3673 } 3674 3675 EditPaM ImpEditEngine::GetPaM( ParaPortion* pPortion, Point aDocPos, bool bSmart ) 3676 { 3677 OSL_ENSURE( pPortion->IsVisible(), "Why GetPaM() for an invisible paragraph?" ); 3678 OSL_ENSURE( IsFormatted(), "GetPaM: Not formatted" ); 3679 3680 sal_Int32 nCurIndex = 0; 3681 EditPaM aPaM; 3682 aPaM.SetNode( pPortion->GetNode() ); 3683 3684 const SvxLineSpacingItem& rLSItem = pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL ); 3685 sal_uInt16 nSBL = ( rLSItem.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Fix ) 3686 ? GetYValue( rLSItem.GetInterLineSpace() ) : 0; 3687 3688 long nY = pPortion->GetFirstLineOffset(); 3689 3690 OSL_ENSURE( pPortion->GetLines().Count(), "Empty ParaPortion in GetPaM!" ); 3691 3692 const EditLine* pLine = nullptr; 3693 for ( sal_Int32 nLine = 0; nLine < pPortion->GetLines().Count(); nLine++ ) 3694 { 3695 const EditLine& rTmpLine = pPortion->GetLines()[nLine]; 3696 nY += rTmpLine.GetHeight(); 3697 if ( !aStatus.IsOutliner() ) 3698 nY += nSBL; 3699 if ( nY > aDocPos.Y() ) 3700 { 3701 pLine = &rTmpLine; 3702 break; // correct Y-position is not of interest 3703 } 3704 3705 nCurIndex = nCurIndex + rTmpLine.GetLen(); 3706 } 3707 3708 if ( !pLine ) // may happen only in the range of SA! 3709 { 3710 #if OSL_DEBUG_LEVEL > 0 3711 const SvxULSpaceItem& rULSpace = pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_ULSPACE ); 3712 OSL_ENSURE( nY+GetYValue( rULSpace.GetLower() ) >= aDocPos.Y() , "Index in no line, GetPaM ?" ); 3713 #endif 3714 aPaM.SetIndex( pPortion->GetNode()->Len() ); 3715 return aPaM; 3716 } 3717 3718 // If no line found, only just X-Position => Index 3719 nCurIndex = GetChar( pPortion, pLine, aDocPos.X(), bSmart ); 3720 aPaM.SetIndex( nCurIndex ); 3721 3722 if ( nCurIndex && ( nCurIndex == pLine->GetEnd() ) && 3723 ( pLine != &pPortion->GetLines()[pPortion->GetLines().Count()-1] ) ) 3724 { 3725 aPaM = CursorLeft( aPaM ); 3726 } 3727 3728 return aPaM; 3729 } 3730 3731 sal_Int32 ImpEditEngine::GetChar( 3732 const ParaPortion* pParaPortion, const EditLine* pLine, long nXPos, bool bSmart) 3733 { 3734 OSL_ENSURE( pLine, "No line received: GetChar" ); 3735 3736 sal_Int32 nChar = -1; 3737 sal_Int32 nCurIndex = pLine->GetStart(); 3738 3739 3740 // Search best matching portion with GetPortionXOffset() 3741 for ( sal_Int32 i = pLine->GetStartPortion(); i <= pLine->GetEndPortion(); i++ ) 3742 { 3743 const TextPortion& rPortion = pParaPortion->GetTextPortions()[i]; 3744 long nXLeft = GetPortionXOffset( pParaPortion, pLine, i ); 3745 long nXRight = nXLeft + rPortion.GetSize().Width(); 3746 if ( ( nXLeft <= nXPos ) && ( nXRight >= nXPos ) ) 3747 { 3748 nChar = nCurIndex; 3749 3750 // Search within Portion... 3751 3752 // Don't search within special portions... 3753 if ( rPortion.GetKind() != PortionKind::TEXT ) 3754 { 3755 // ...but check on which side 3756 if ( bSmart ) 3757 { 3758 long nLeftDiff = nXPos-nXLeft; 3759 long nRightDiff = nXRight-nXPos; 3760 if ( nRightDiff < nLeftDiff ) 3761 nChar++; 3762 } 3763 } 3764 else 3765 { 3766 sal_Int32 nMax = rPortion.GetLen(); 3767 sal_Int32 nOffset = -1; 3768 sal_Int32 nTmpCurIndex = nChar - pLine->GetStart(); 3769 3770 long nXInPortion = nXPos - nXLeft; 3771 if ( rPortion.IsRightToLeft() ) 3772 nXInPortion = nXRight - nXPos; 3773 3774 // Search in Array... 3775 for ( sal_Int32 x = 0; x < nMax; x++ ) 3776 { 3777 long nTmpPosMax = pLine->GetCharPosArray()[nTmpCurIndex+x]; 3778 if ( nTmpPosMax > nXInPortion ) 3779 { 3780 // Check whether this or the previous... 3781 long nTmpPosMin = x ? pLine->GetCharPosArray()[nTmpCurIndex+x-1] : 0; 3782 long nDiffLeft = nXInPortion - nTmpPosMin; 3783 long nDiffRight = nTmpPosMax - nXInPortion; 3784 OSL_ENSURE( nDiffLeft >= 0, "DiffLeft negative" ); 3785 OSL_ENSURE( nDiffRight >= 0, "DiffRight negative" ); 3786 nOffset = ( bSmart && ( nDiffRight < nDiffLeft ) ) ? x+1 : x; 3787 // I18N: If there are character position with the length of 0, 3788 // they belong to the same character, we can not use this position as an index. 3789 // Skip all 0-positions, cheaper than using XBreakIterator: 3790 if ( nOffset < nMax ) 3791 { 3792 const long nX = pLine->GetCharPosArray()[nOffset]; 3793 while ( ( (nOffset+1) < nMax ) && ( pLine->GetCharPosArray()[nOffset+1] == nX ) ) 3794 nOffset++; 3795 } 3796 break; 3797 } 3798 } 3799 3800 // There should not be any inaccuracies when using the 3801 // CharPosArray! Maybe for kerning? 3802 // 0xFFF happens for example for Outline-Font when at the very end. 3803 if ( nOffset < 0 ) 3804 nOffset = nMax; 3805 3806 OSL_ENSURE( nOffset <= nMax, "nOffset > nMax" ); 3807 3808 nChar = nChar + nOffset; 3809 3810 // Check if index is within a cell: 3811 if ( nChar && ( nChar < pParaPortion->GetNode()->Len() ) ) 3812 { 3813 EditPaM aPaM( pParaPortion->GetNode(), nChar+1 ); 3814 sal_uInt16 nScriptType = GetI18NScriptType( aPaM ); 3815 if ( nScriptType == i18n::ScriptType::COMPLEX ) 3816 { 3817 uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() ); 3818 sal_Int32 nCount = 1; 3819 lang::Locale aLocale = GetLocale( aPaM ); 3820 sal_Int32 nRight = _xBI->nextCharacters( 3821 pParaPortion->GetNode()->GetString(), nChar, aLocale, css::i18n::CharacterIteratorMode::SKIPCELL, nCount, nCount ); 3822 sal_Int32 nLeft = _xBI->previousCharacters( 3823 pParaPortion->GetNode()->GetString(), nRight, aLocale, css::i18n::CharacterIteratorMode::SKIPCELL, nCount, nCount ); 3824 if ( ( nLeft != nChar ) && ( nRight != nChar ) ) 3825 { 3826 nChar = ( std::abs( nRight - nChar ) < std::abs( nLeft - nChar ) ) ? nRight : nLeft; 3827 } 3828 } 3829 } 3830 } 3831 } 3832 3833 nCurIndex = nCurIndex + rPortion.GetLen(); 3834 } 3835 3836 if ( nChar == -1 ) 3837 { 3838 nChar = ( nXPos <= pLine->GetStartPosX() ) ? pLine->GetStart() : pLine->GetEnd(); 3839 } 3840 3841 return nChar; 3842 } 3843 3844 Range ImpEditEngine::GetLineXPosStartEnd( const ParaPortion* pParaPortion, const EditLine* pLine ) const 3845 { 3846 Range aLineXPosStartEnd; 3847 3848 sal_Int32 nPara = GetEditDoc().GetPos( pParaPortion->GetNode() ); 3849 if ( !IsRightToLeft( nPara ) ) 3850 { 3851 aLineXPosStartEnd.Min() = pLine->GetStartPosX(); 3852 aLineXPosStartEnd.Max() = pLine->GetStartPosX() + pLine->GetTextWidth(); 3853 } 3854 else 3855 { 3856 aLineXPosStartEnd.Min() = GetPaperSize().Width() - ( pLine->GetStartPosX() + pLine->GetTextWidth() ); 3857 aLineXPosStartEnd.Max() = GetPaperSize().Width() - pLine->GetStartPosX(); 3858 } 3859 3860 3861 return aLineXPosStartEnd; 3862 } 3863 3864 long ImpEditEngine::GetPortionXOffset( 3865 const ParaPortion* pParaPortion, const EditLine* pLine, sal_Int32 nTextPortion) const 3866 { 3867 long nX = pLine->GetStartPosX(); 3868 3869 for ( sal_Int32 i = pLine->GetStartPortion(); i < nTextPortion; i++ ) 3870 { 3871 const TextPortion& rPortion = pParaPortion->GetTextPortions()[i]; 3872 switch ( rPortion.GetKind() ) 3873 { 3874 case PortionKind::FIELD: 3875 case PortionKind::TEXT: 3876 case PortionKind::HYPHENATOR: 3877 case PortionKind::TAB: 3878 { 3879 nX += rPortion.GetSize().Width(); 3880 } 3881 break; 3882 case PortionKind::LINEBREAK: break; 3883 } 3884 } 3885 3886 sal_Int32 nPara = GetEditDoc().GetPos( pParaPortion->GetNode() ); 3887 bool bR2LPara = IsRightToLeft( nPara ); 3888 3889 const TextPortion& rDestPortion = pParaPortion->GetTextPortions()[nTextPortion]; 3890 if ( rDestPortion.GetKind() != PortionKind::TAB ) 3891 { 3892 if ( !bR2LPara && rDestPortion.GetRightToLeftLevel() ) 3893 { 3894 // Portions behind must be added, visual before this portion 3895 sal_Int32 nTmpPortion = nTextPortion+1; 3896 while ( nTmpPortion <= pLine->GetEndPortion() ) 3897 { 3898 const TextPortion& rNextTextPortion = pParaPortion->GetTextPortions()[nTmpPortion]; 3899 if ( rNextTextPortion.GetRightToLeftLevel() && ( rNextTextPortion.GetKind() != PortionKind::TAB ) ) 3900 nX += rNextTextPortion.GetSize().Width(); 3901 else 3902 break; 3903 nTmpPortion++; 3904 } 3905 // Portions before must be removed, visual behind this portion 3906 nTmpPortion = nTextPortion; 3907 while ( nTmpPortion > pLine->GetStartPortion() ) 3908 { 3909 --nTmpPortion; 3910 const TextPortion& rPrevTextPortion = pParaPortion->GetTextPortions()[nTmpPortion]; 3911 if ( rPrevTextPortion.GetRightToLeftLevel() && ( rPrevTextPortion.GetKind() != PortionKind::TAB ) ) 3912 nX -= rPrevTextPortion.GetSize().Width(); 3913 else 3914 break; 3915 } 3916 } 3917 else if ( bR2LPara && !rDestPortion.IsRightToLeft() ) 3918 { 3919 // Portions behind must be removed, visual behind this portion 3920 sal_Int32 nTmpPortion = nTextPortion+1; 3921 while ( nTmpPortion <= pLine->GetEndPortion() ) 3922 { 3923 const TextPortion& rNextTextPortion = pParaPortion->GetTextPortions()[nTmpPortion]; 3924 if ( !rNextTextPortion.IsRightToLeft() && ( rNextTextPortion.GetKind() != PortionKind::TAB ) ) 3925 nX += rNextTextPortion.GetSize().Width(); 3926 else 3927 break; 3928 nTmpPortion++; 3929 } 3930 // Portions before must be added, visual before this portion 3931 nTmpPortion = nTextPortion; 3932 while ( nTmpPortion > pLine->GetStartPortion() ) 3933 { 3934 --nTmpPortion; 3935 const TextPortion& rPrevTextPortion = pParaPortion->GetTextPortions()[nTmpPortion]; 3936 if ( !rPrevTextPortion.IsRightToLeft() && ( rPrevTextPortion.GetKind() != PortionKind::TAB ) ) 3937 nX -= rPrevTextPortion.GetSize().Width(); 3938 else 3939 break; 3940 } 3941 } 3942 } 3943 if ( bR2LPara ) 3944 { 3945 // Switch X positions... 3946 OSL_ENSURE( GetTextRanger() || GetPaperSize().Width(), "GetPortionXOffset - paper size?!" ); 3947 OSL_ENSURE( GetTextRanger() || (nX <= GetPaperSize().Width()), "GetPortionXOffset - position out of paper size!" ); 3948 nX = GetPaperSize().Width() - nX; 3949 nX -= rDestPortion.GetSize().Width(); 3950 } 3951 3952 return nX; 3953 } 3954 3955 long ImpEditEngine::GetXPos( 3956 const ParaPortion* pParaPortion, const EditLine* pLine, sal_Int32 nIndex, bool bPreferPortionStart) const 3957 { 3958 OSL_ENSURE( pLine, "No line received: GetXPos" ); 3959 OSL_ENSURE( ( nIndex >= pLine->GetStart() ) && ( nIndex <= pLine->GetEnd() ) , "GetXPos has to be called properly!" ); 3960 3961 bool bDoPreferPortionStart = bPreferPortionStart; 3962 // Assure that the portion belongs to this line: 3963 if ( nIndex == pLine->GetStart() ) 3964 bDoPreferPortionStart = true; 3965 else if ( nIndex == pLine->GetEnd() ) 3966 bDoPreferPortionStart = false; 3967 3968 sal_Int32 nTextPortionStart = 0; 3969 sal_Int32 nTextPortion = pParaPortion->GetTextPortions().FindPortion( nIndex, nTextPortionStart, bDoPreferPortionStart ); 3970 3971 OSL_ENSURE( ( nTextPortion >= pLine->GetStartPortion() ) && ( nTextPortion <= pLine->GetEndPortion() ), "GetXPos: Portion not in current line! " ); 3972 3973 const TextPortion& rPortion = pParaPortion->GetTextPortions()[nTextPortion]; 3974 3975 long nX = GetPortionXOffset( pParaPortion, pLine, nTextPortion ); 3976 3977 // calc text width, portion size may include CJK/CTL spacing... 3978 // But the array might not be init yet, if using text ranger this method is called within CreateLines()... 3979 long nPortionTextWidth = rPortion.GetSize().Width(); 3980 if ( ( rPortion.GetKind() == PortionKind::TEXT ) && rPortion.GetLen() && !GetTextRanger() ) 3981 nPortionTextWidth = pLine->GetCharPosArray()[nTextPortionStart + rPortion.GetLen() - 1 - pLine->GetStart()]; 3982 3983 if ( nTextPortionStart != nIndex ) 3984 { 3985 // Search within portion... 3986 if ( nIndex == ( nTextPortionStart + rPortion.GetLen() ) ) 3987 { 3988 // End of Portion 3989 if ( rPortion.GetKind() == PortionKind::TAB ) 3990 { 3991 if ( nTextPortion+1 < pParaPortion->GetTextPortions().Count() ) 3992 { 3993 const TextPortion& rNextPortion = pParaPortion->GetTextPortions()[nTextPortion+1]; 3994 if ( rNextPortion.GetKind() != PortionKind::TAB ) 3995 { 3996 if ( !bPreferPortionStart ) 3997 nX = GetXPos( pParaPortion, pLine, nIndex, true ); 3998 else if ( !IsRightToLeft( GetEditDoc().GetPos( pParaPortion->GetNode() ) ) ) 3999 nX += nPortionTextWidth; 4000 } 4001 } 4002 else if ( !IsRightToLeft( GetEditDoc().GetPos( pParaPortion->GetNode() ) ) ) 4003 { 4004 nX += nPortionTextWidth; 4005 } 4006 } 4007 else if ( !rPortion.IsRightToLeft() ) 4008 { 4009 nX += nPortionTextWidth; 4010 } 4011 } 4012 else if ( rPortion.GetKind() == PortionKind::TEXT ) 4013 { 4014 OSL_ENSURE( nIndex != pLine->GetStart(), "Strange behavior in new GetXPos()" ); 4015 OSL_ENSURE( pLine && !pLine->GetCharPosArray().empty(), "svx::ImpEditEngine::GetXPos(), portion in an empty line?" ); 4016 4017 if( !pLine->GetCharPosArray().empty() ) 4018 { 4019 sal_Int32 nPos = nIndex - 1 - pLine->GetStart(); 4020 if (nPos < 0 || nPos >= static_cast<sal_Int32>(pLine->GetCharPosArray().size())) 4021 { 4022 nPos = pLine->GetCharPosArray().size()-1; 4023 OSL_FAIL("svx::ImpEditEngine::GetXPos(), index out of range!"); 4024 } 4025 4026 // old code restored see #i112788 (which leaves #i74188 unfixed again) 4027 long nPosInPortion = pLine->GetCharPosArray()[nPos]; 4028 4029 if ( !rPortion.IsRightToLeft() ) 4030 { 4031 nX += nPosInPortion; 4032 } 4033 else 4034 { 4035 nX += nPortionTextWidth - nPosInPortion; 4036 } 4037 4038 if ( rPortion.GetExtraInfos() && rPortion.GetExtraInfos()->bCompressed ) 4039 { 4040 nX += rPortion.GetExtraInfos()->nPortionOffsetX; 4041 if ( rPortion.GetExtraInfos()->nAsianCompressionTypes & AsianCompressionFlags::PunctuationRight ) 4042 { 4043 AsianCompressionFlags nType = GetCharTypeForCompression( pParaPortion->GetNode()->GetChar( nIndex ) ); 4044 if ( nType == AsianCompressionFlags::PunctuationRight && !pLine->GetCharPosArray().empty() ) 4045 { 4046 sal_Int32 n = nIndex - nTextPortionStart; 4047 const long* pDXArray = pLine->GetCharPosArray().data()+( nTextPortionStart-pLine->GetStart() ); 4048 sal_Int32 nCharWidth = ( ( (n+1) < rPortion.GetLen() ) ? pDXArray[n] : rPortion.GetSize().Width() ) 4049 - ( n ? pDXArray[n-1] : 0 ); 4050 if ( (n+1) < rPortion.GetLen() ) 4051 { 4052 // smaller, when char behind is AsianCompressionFlags::PunctuationRight also 4053 nType = GetCharTypeForCompression( pParaPortion->GetNode()->GetChar( nIndex+1 ) ); 4054 if ( nType == AsianCompressionFlags::PunctuationRight ) 4055 { 4056 sal_Int32 nNextCharWidth = ( ( (n+2) < rPortion.GetLen() ) ? pDXArray[n+1] : rPortion.GetSize().Width() ) 4057 - pDXArray[n]; 4058 sal_Int32 nCompressed = nNextCharWidth/2; 4059 nCompressed *= rPortion.GetExtraInfos()->nMaxCompression100thPercent; 4060 nCompressed /= 10000; 4061 nCharWidth += nCompressed; 4062 } 4063 } 4064 else 4065 { 4066 nCharWidth *= 2; // last char pos to portion end is only compressed size 4067 } 4068 nX += nCharWidth/2; // 50% compression 4069 } 4070 } 4071 } 4072 } 4073 } 4074 } 4075 else // if ( nIndex == pLine->GetStart() ) 4076 { 4077 if ( rPortion.IsRightToLeft() ) 4078 { 4079 nX += nPortionTextWidth; 4080 } 4081 } 4082 4083 return nX; 4084 } 4085 4086 void ImpEditEngine::CalcHeight( ParaPortion* pPortion ) 4087 { 4088 pPortion->nHeight = 0; 4089 pPortion->nFirstLineOffset = 0; 4090 4091 if ( pPortion->IsVisible() ) 4092 { 4093 OSL_ENSURE( pPortion->GetLines().Count(), "Paragraph with no lines in ParaPortion::CalcHeight" ); 4094 for (sal_Int32 nLine = 0; nLine < pPortion->GetLines().Count(); ++nLine) 4095 pPortion->nHeight += pPortion->GetLines()[nLine].GetHeight(); 4096 4097 if ( !aStatus.IsOutliner() ) 4098 { 4099 const SvxULSpaceItem& rULItem = pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_ULSPACE ); 4100 const SvxLineSpacingItem& rLSItem = pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL ); 4101 sal_Int32 nSBL = ( rLSItem.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Fix ) ? GetYValue( rLSItem.GetInterLineSpace() ) : 0; 4102 4103 if ( nSBL ) 4104 { 4105 if ( pPortion->GetLines().Count() > 1 ) 4106 pPortion->nHeight += ( pPortion->GetLines().Count() - 1 ) * nSBL; 4107 if ( aStatus.ULSpaceSummation() ) 4108 pPortion->nHeight += nSBL; 4109 } 4110 4111 sal_Int32 nPortion = GetParaPortions().GetPos( pPortion ); 4112 if ( nPortion ) 4113 { 4114 sal_uInt16 nUpper = GetYValue( rULItem.GetUpper() ); 4115 pPortion->nHeight += nUpper; 4116 pPortion->nFirstLineOffset = nUpper; 4117 } 4118 4119 if ( nPortion != (GetParaPortions().Count()-1) ) 4120 { 4121 pPortion->nHeight += GetYValue( rULItem.GetLower() ); // not in the last 4122 } 4123 4124 4125 if ( nPortion && !aStatus.ULSpaceSummation() ) 4126 { 4127 ParaPortion* pPrev = GetParaPortions().SafeGetObject( nPortion-1 ); 4128 if (pPrev) 4129 { 4130 const SvxULSpaceItem& rPrevULItem = pPrev->GetNode()->GetContentAttribs().GetItem( EE_PARA_ULSPACE ); 4131 const SvxLineSpacingItem& rPrevLSItem = pPrev->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL ); 4132 4133 // In relation between WinWord6/Writer3: 4134 // With a proportional line spacing the paragraph spacing is 4135 // also manipulated. 4136 // Only Writer3: Do not add up, but minimum distance. 4137 4138 // check if distance by LineSpacing > Upper: 4139 sal_uInt16 nExtraSpace = GetYValue( lcl_CalcExtraSpace( rLSItem ) ); 4140 if ( nExtraSpace > pPortion->nFirstLineOffset ) 4141 { 4142 // Paragraph becomes 'bigger': 4143 pPortion->nHeight += ( nExtraSpace - pPortion->nFirstLineOffset ); 4144 pPortion->nFirstLineOffset = nExtraSpace; 4145 } 4146 4147 // Determine nFirstLineOffset now f(pNode) => now f(pNode, pPrev): 4148 sal_uInt16 nPrevLower = GetYValue( rPrevULItem.GetLower() ); 4149 4150 // This PrevLower is still in the height of PrevPortion ... 4151 if ( nPrevLower > pPortion->nFirstLineOffset ) 4152 { 4153 // Paragraph is 'small': 4154 pPortion->nHeight -= pPortion->nFirstLineOffset; 4155 pPortion->nFirstLineOffset = 0; 4156 } 4157 else if ( nPrevLower ) 4158 { 4159 // Paragraph becomes 'somewhat smaller': 4160 pPortion->nHeight -= nPrevLower; 4161 pPortion->nFirstLineOffset = 4162 pPortion->nFirstLineOffset - nPrevLower; 4163 } 4164 // I find it not so good, but Writer3 feature: 4165 // Check if distance by LineSpacing > Lower: this value is not 4166 // stuck in the height of PrevPortion. 4167 if ( !pPrev->IsInvalid() ) 4168 { 4169 nExtraSpace = GetYValue( lcl_CalcExtraSpace( rPrevLSItem ) ); 4170 if ( nExtraSpace > nPrevLower ) 4171 { 4172 sal_uInt16 nMoreLower = nExtraSpace - nPrevLower; 4173 // Paragraph becomes 'bigger', 'grows' downwards: 4174 if ( nMoreLower > pPortion->nFirstLineOffset ) 4175 { 4176 pPortion->nHeight += ( nMoreLower - pPortion->nFirstLineOffset ); 4177 pPortion->nFirstLineOffset = nMoreLower; 4178 } 4179 } 4180 } 4181 } 4182 } 4183 } 4184 } 4185 } 4186 4187 tools::Rectangle ImpEditEngine::GetEditCursor( ParaPortion* pPortion, sal_Int32 nIndex, GetCursorFlags nFlags ) 4188 { 4189 OSL_ENSURE( pPortion->IsVisible(), "Why GetEditCursor() for an invisible paragraph?" ); 4190 OSL_ENSURE( IsFormatted() || GetTextRanger(), "GetEditCursor: Not formatted" ); 4191 4192 /* 4193 GetCursorFlags::EndOfLine: If after the last character of a wrapped line, remaining 4194 at the end of the line, not the beginning of the next one. 4195 Purpose: - END => really after the last character 4196 - Selection.... 4197 */ 4198 4199 long nY = pPortion->GetFirstLineOffset(); 4200 4201 const SvxLineSpacingItem& rLSItem = pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL ); 4202 sal_uInt16 nSBL = ( rLSItem.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Fix ) 4203 ? GetYValue( rLSItem.GetInterLineSpace() ) : 0; 4204 4205 sal_Int32 nCurIndex = 0; 4206 sal_Int32 nLineCount = pPortion->GetLines().Count(); 4207 OSL_ENSURE( nLineCount, "Empty ParaPortion in GetEditCursor!" ); 4208 if (nLineCount == 0) 4209 return tools::Rectangle(); 4210 const EditLine* pLine = nullptr; 4211 bool bEOL( nFlags & GetCursorFlags::EndOfLine ); 4212 for (sal_Int32 nLine = 0; nLine < nLineCount; ++nLine) 4213 { 4214 const EditLine& rTmpLine = pPortion->GetLines()[nLine]; 4215 if ( ( rTmpLine.GetStart() == nIndex ) || ( rTmpLine.IsIn( nIndex, bEOL ) ) ) 4216 { 4217 pLine = &rTmpLine; 4218 break; 4219 } 4220 4221 nCurIndex = nCurIndex + rTmpLine.GetLen(); 4222 nY += rTmpLine.GetHeight(); 4223 if ( !aStatus.IsOutliner() ) 4224 nY += nSBL; 4225 } 4226 if ( !pLine ) 4227 { 4228 // Cursor at the End of the paragraph. 4229 OSL_ENSURE( nIndex == nCurIndex, "Index dead wrong in GetEditCursor!" ); 4230 4231 pLine = &pPortion->GetLines()[nLineCount-1]; 4232 nY -= pLine->GetHeight(); 4233 if ( !aStatus.IsOutliner() ) 4234 nY -= nSBL; 4235 } 4236 4237 tools::Rectangle aEditCursor; 4238 4239 aEditCursor.SetTop( nY ); 4240 nY += pLine->GetHeight(); 4241 aEditCursor.SetBottom( nY-1 ); 4242 4243 // Search within the line... 4244 long nX; 4245 4246 if ( ( nIndex == pLine->GetStart() ) && ( nFlags & GetCursorFlags::StartOfLine ) ) 4247 { 4248 Range aXRange = GetLineXPosStartEnd( pPortion, pLine ); 4249 nX = !IsRightToLeft( GetEditDoc().GetPos( pPortion->GetNode() ) ) ? aXRange.Min() : aXRange.Max(); 4250 } 4251 else if ( ( nIndex == pLine->GetEnd() ) && ( nFlags & GetCursorFlags::EndOfLine ) ) 4252 { 4253 Range aXRange = GetLineXPosStartEnd( pPortion, pLine ); 4254 nX = !IsRightToLeft( GetEditDoc().GetPos( pPortion->GetNode() ) ) ? aXRange.Max() : aXRange.Min(); 4255 } 4256 else 4257 { 4258 nX = GetXPos( pPortion, pLine, nIndex, bool( nFlags & GetCursorFlags::PreferPortionStart ) ); 4259 } 4260 4261 aEditCursor.SetLeft(nX); 4262 aEditCursor.SetRight(nX); 4263 4264 if ( nFlags & GetCursorFlags::TextOnly ) 4265 aEditCursor.SetTop( aEditCursor.Bottom() - pLine->GetTxtHeight() + 1 ); 4266 else 4267 aEditCursor.SetTop( aEditCursor.Bottom() - std::min( pLine->GetTxtHeight(), pLine->GetHeight() ) + 1 ); 4268 4269 return aEditCursor; 4270 } 4271 4272 void ImpEditEngine::SetValidPaperSize( const Size& rNewSz ) 4273 { 4274 aPaperSize = rNewSz; 4275 4276 long nMinWidth = aStatus.AutoPageWidth() ? aMinAutoPaperSize.Width() : 0; 4277 long nMaxWidth = aStatus.AutoPageWidth() ? aMaxAutoPaperSize.Width() : 0x7FFFFFFF; 4278 long nMinHeight = aStatus.AutoPageHeight() ? aMinAutoPaperSize.Height() : 0; 4279 long nMaxHeight = aStatus.AutoPageHeight() ? aMaxAutoPaperSize.Height() : 0x7FFFFFFF; 4280 4281 // Minimum/Maximum width: 4282 if ( aPaperSize.Width() < nMinWidth ) 4283 aPaperSize.setWidth( nMinWidth ); 4284 else if ( aPaperSize.Width() > nMaxWidth ) 4285 aPaperSize.setWidth( nMaxWidth ); 4286 4287 // Minimum/Maximum height: 4288 if ( aPaperSize.Height() < nMinHeight ) 4289 aPaperSize.setHeight( nMinHeight ); 4290 else if ( aPaperSize.Height() > nMaxHeight ) 4291 aPaperSize.setHeight( nMaxHeight ); 4292 } 4293 4294 std::shared_ptr<SvxForbiddenCharactersTable> const & ImpEditEngine::GetForbiddenCharsTable() 4295 { 4296 return EditDLL::Get().GetGlobalData()->GetForbiddenCharsTable(); 4297 } 4298 4299 void ImpEditEngine::SetForbiddenCharsTable(const std::shared_ptr<SvxForbiddenCharactersTable>& xForbiddenChars) 4300 { 4301 EditDLL::Get().GetGlobalData()->SetForbiddenCharsTable( xForbiddenChars ); 4302 } 4303 4304 bool ImpEditEngine::IsVisualCursorTravelingEnabled() 4305 { 4306 bool bVisualCursorTravaling = false; 4307 4308 if( !pCTLOptions ) 4309 pCTLOptions.reset( new SvtCTLOptions ); 4310 4311 if ( pCTLOptions->IsCTLFontEnabled() && ( pCTLOptions->GetCTLCursorMovement() == SvtCTLOptions::MOVEMENT_VISUAL ) ) 4312 { 4313 bVisualCursorTravaling = true; 4314 } 4315 4316 return bVisualCursorTravaling; 4317 4318 } 4319 4320 bool ImpEditEngine::DoVisualCursorTraveling() 4321 { 4322 // Don't check if it's necessary, because we also need it when leaving the paragraph 4323 return IsVisualCursorTravelingEnabled(); 4324 } 4325 4326 IMPL_LINK_NOARG(ImpEditEngine, DocModified, LinkParamNone*, void) 4327 { 4328 aModifyHdl.Call( nullptr /*GetEditEnginePtr()*/ ); // NULL, because also used for Outliner 4329 } 4330 4331 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 4332
