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