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