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