75aeaab5ef
On stress test with shapes and typing a segfault ocurred due to using a freed vcl::Cursor. On SdrObjEditView::SdrEndTextEdit, delete pOLV can delete the cursor remembered in pTECursorBuffer. But if it is set to the window before the deletion, it will be safely removed from the window. And on SdrObjEditView::ModelHasChanged a re-anchoring sets a cursor on the window that sholdn't be there and other SdrObjEditView can see, remeber, and use it even after this one died and freed the cursor. Change-Id: I3cfef3b68b77e6e6b49c3b68297a6a20e1f9394a Reviewed-on: https://gerrit.libreoffice.org/c/core/+/171184 Reviewed-by: Miklos Vajna <vmiklos@collabora.com> Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice@gmail.com> (cherry picked from commit 3b5738ab1a646d089fa7cc59ffaeda7d011c1e07) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/171288 Tested-by: Jenkins
1537 lines
49 KiB
C++
1537 lines
49 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
#include <memory>
|
|
#include <com/sun/star/i18n/WordType.hpp>
|
|
|
|
#include <svl/itempool.hxx>
|
|
#include <editeng/editeng.hxx>
|
|
#include <editeng/editview.hxx>
|
|
#include <editeng/editdata.hxx>
|
|
|
|
#include <svl/style.hxx>
|
|
#include <svl/languageoptions.hxx>
|
|
#include <i18nlangtag/languagetag.hxx>
|
|
|
|
#include <editeng/outliner.hxx>
|
|
#include <outleeng.hxx>
|
|
#include "paralist.hxx"
|
|
#include "outlundo.hxx"
|
|
#include <editeng/outlobj.hxx>
|
|
#include <editeng/flditem.hxx>
|
|
#include <editeng/eeitem.hxx>
|
|
#include <editeng/numitem.hxx>
|
|
#include <vcl/window.hxx>
|
|
#include <vcl/event.hxx>
|
|
#include <vcl/ptrstyle.hxx>
|
|
#include <svl/itemset.hxx>
|
|
#include <svl/eitem.hxx>
|
|
#include <editeng/editstat.hxx>
|
|
#include <sal/log.hxx>
|
|
#include <osl/diagnose.h>
|
|
#include <tools/debug.hxx>
|
|
|
|
using namespace ::com::sun::star;
|
|
|
|
|
|
OutlinerView::OutlinerView( Outliner* pOut, vcl::Window* pWin )
|
|
{
|
|
pOwner = pOut;
|
|
pEditView.reset( new EditView( pOut->pEditEngine.get(), pWin ) );
|
|
}
|
|
|
|
OutlinerView::~OutlinerView()
|
|
{
|
|
}
|
|
|
|
void OutlinerView::Paint( const tools::Rectangle& rRect, OutputDevice* pTargetDevice )
|
|
{
|
|
// For the first Paint/KeyInput/Drop an empty Outliner is turned into
|
|
// an Outliner with exactly one paragraph.
|
|
if( pOwner->bFirstParaIsEmpty )
|
|
pOwner->Insert( OUString() );
|
|
|
|
pEditView->Paint( rRect, pTargetDevice );
|
|
}
|
|
|
|
bool OutlinerView::PostKeyEvent( const KeyEvent& rKEvt, vcl::Window const * pFrameWin )
|
|
{
|
|
// For the first Paint/KeyInput/Drop an empty Outliner is turned into
|
|
// an Outliner with exactly one paragraph.
|
|
if( pOwner->bFirstParaIsEmpty )
|
|
pOwner->Insert( OUString() );
|
|
|
|
bool bKeyProcessed = false;
|
|
ESelection aSel( pEditView->GetSelection() );
|
|
bool bSelection = aSel.HasRange();
|
|
vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
|
|
KeyFuncType eFunc = aKeyCode.GetFunction();
|
|
sal_uInt16 nCode = aKeyCode.GetCode();
|
|
bool bReadOnly = IsReadOnly();
|
|
|
|
if( bSelection && ( nCode != KEY_TAB ) && EditEngine::DoesKeyChangeText( rKEvt ) )
|
|
{
|
|
if ( ImpCalcSelectedPages( false ) && !pOwner->ImpCanDeleteSelectedPages( this ) )
|
|
return true;
|
|
}
|
|
|
|
if ( eFunc != KeyFuncType::DONTKNOW )
|
|
{
|
|
switch ( eFunc )
|
|
{
|
|
case KeyFuncType::CUT:
|
|
{
|
|
if ( !bReadOnly )
|
|
{
|
|
Cut();
|
|
bKeyProcessed = true;
|
|
}
|
|
}
|
|
break;
|
|
case KeyFuncType::COPY:
|
|
{
|
|
Copy();
|
|
bKeyProcessed = true;
|
|
}
|
|
break;
|
|
case KeyFuncType::PASTE:
|
|
{
|
|
if ( !bReadOnly )
|
|
{
|
|
PasteSpecial();
|
|
bKeyProcessed = true;
|
|
}
|
|
}
|
|
break;
|
|
case KeyFuncType::DELETE:
|
|
{
|
|
if( !bReadOnly && !bSelection && ( pOwner->GetOutlinerMode() != OutlinerMode::TextObject ) )
|
|
{
|
|
if( aSel.nEndPos == pOwner->pEditEngine->GetTextLen( aSel.nEndPara ) )
|
|
{
|
|
Paragraph* pNext = pOwner->pParaList->GetParagraph( aSel.nEndPara+1 );
|
|
if( pNext && pNext->HasFlag(ParaFlag::ISPAGE) )
|
|
{
|
|
if( !pOwner->ImpCanDeleteSelectedPages( this, aSel.nEndPara, 1 ) )
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default: // is then possibly edited below.
|
|
eFunc = KeyFuncType::DONTKNOW;
|
|
}
|
|
}
|
|
if ( eFunc == KeyFuncType::DONTKNOW )
|
|
{
|
|
switch ( nCode )
|
|
{
|
|
case KEY_TAB:
|
|
{
|
|
if ( !bReadOnly && !aKeyCode.IsMod1() && !aKeyCode.IsMod2() )
|
|
{
|
|
if ( ( pOwner->GetOutlinerMode() != OutlinerMode::TextObject ) &&
|
|
( pOwner->GetOutlinerMode() != OutlinerMode::TitleObject ) &&
|
|
( bSelection || !aSel.nStartPos ) )
|
|
{
|
|
Indent( aKeyCode.IsShift() ? -1 : +1 );
|
|
bKeyProcessed = true;
|
|
}
|
|
else if ( ( pOwner->GetOutlinerMode() == OutlinerMode::TextObject ) &&
|
|
!bSelection && !aSel.nEndPos && pOwner->ImplHasNumberFormat( aSel.nEndPara ) )
|
|
{
|
|
Indent( aKeyCode.IsShift() ? -1 : +1 );
|
|
bKeyProcessed = true;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case KEY_BACKSPACE:
|
|
{
|
|
if( !bReadOnly && !bSelection && aSel.nEndPara && !aSel.nEndPos )
|
|
{
|
|
Paragraph* pPara = pOwner->pParaList->GetParagraph( aSel.nEndPara );
|
|
Paragraph* pPrev = pOwner->pParaList->GetParagraph( aSel.nEndPara-1 );
|
|
if( !pPrev->IsVisible() )
|
|
return true;
|
|
if( !pPara->GetDepth() )
|
|
{
|
|
if(!pOwner->ImpCanDeleteSelectedPages(this, aSel.nEndPara , 1 ) )
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case KEY_RETURN:
|
|
{
|
|
if ( !bReadOnly )
|
|
{
|
|
// Special treatment: hard return at the end of a paragraph,
|
|
// which has collapsed subparagraphs.
|
|
Paragraph* pPara = pOwner->pParaList->GetParagraph( aSel.nEndPara );
|
|
|
|
if( !aKeyCode.IsShift() )
|
|
{
|
|
// Don't let insert empty paragraph with numbering. Instead end numbering.
|
|
if (pPara->GetDepth() > -1 &&
|
|
pOwner->pEditEngine->GetTextLen( aSel.nEndPara ) == 0)
|
|
{
|
|
ToggleBullets();
|
|
return true;
|
|
}
|
|
// ImpGetCursor again???
|
|
if( !bSelection &&
|
|
aSel.nEndPos == pOwner->pEditEngine->GetTextLen( aSel.nEndPara ) )
|
|
{
|
|
sal_Int32 nChildren = pOwner->pParaList->GetChildCount(pPara);
|
|
if( nChildren && !pOwner->pParaList->HasVisibleChildren(pPara))
|
|
{
|
|
pOwner->UndoActionStart( OLUNDO_INSERT );
|
|
sal_Int32 nTemp = aSel.nEndPara;
|
|
nTemp += nChildren;
|
|
nTemp++; // insert above next Non-Child
|
|
SAL_WARN_IF( nTemp < 0, "editeng", "OutlinerView::PostKeyEvent - overflow");
|
|
if (nTemp >= 0)
|
|
{
|
|
pOwner->Insert( OUString(),nTemp,pPara->GetDepth());
|
|
// Position the cursor
|
|
ESelection aTmpSel(nTemp,0,nTemp,0);
|
|
pEditView->SetSelection( aTmpSel );
|
|
}
|
|
pEditView->ShowCursor();
|
|
pOwner->UndoActionEnd();
|
|
bKeyProcessed = true;
|
|
}
|
|
}
|
|
}
|
|
if( !bKeyProcessed && !bSelection &&
|
|
!aKeyCode.IsShift() && aKeyCode.IsMod1() &&
|
|
( aSel.nEndPos == pOwner->pEditEngine->GetTextLen(aSel.nEndPara) ) )
|
|
{
|
|
pOwner->UndoActionStart( OLUNDO_INSERT );
|
|
sal_Int32 nTemp = aSel.nEndPara;
|
|
nTemp++;
|
|
pOwner->Insert( OUString(), nTemp, pPara->GetDepth()+1 );
|
|
|
|
// Position the cursor
|
|
ESelection aTmpSel(nTemp,0,nTemp,0);
|
|
pEditView->SetSelection( aTmpSel );
|
|
pEditView->ShowCursor();
|
|
pOwner->UndoActionEnd();
|
|
bKeyProcessed = true;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return bKeyProcessed || pEditView->PostKeyEvent( rKEvt, pFrameWin );
|
|
}
|
|
|
|
sal_Int32 OutlinerView::ImpCheckMousePos(const Point& rPosPix, MouseTarget& reTarget)
|
|
{
|
|
sal_Int32 nPara = EE_PARA_NOT_FOUND;
|
|
|
|
Point aMousePosWin = pEditView->GetOutputDevice().PixelToLogic( rPosPix );
|
|
if( !pEditView->GetOutputArea().Contains( aMousePosWin ) )
|
|
{
|
|
reTarget = MouseTarget::Outside;
|
|
}
|
|
else
|
|
{
|
|
reTarget = MouseTarget::Text;
|
|
|
|
Point aPaperPos( aMousePosWin );
|
|
tools::Rectangle aOutArea = pEditView->GetOutputArea();
|
|
tools::Rectangle aVisArea = pEditView->GetVisArea();
|
|
aPaperPos.AdjustX( -(aOutArea.Left()) );
|
|
aPaperPos.AdjustX(aVisArea.Left() );
|
|
aPaperPos.AdjustY( -(aOutArea.Top()) );
|
|
aPaperPos.AdjustY(aVisArea.Top() );
|
|
|
|
bool bBullet;
|
|
if ( pOwner->IsTextPos( aPaperPos, 0, &bBullet ) )
|
|
{
|
|
Point aDocPos = pOwner->GetDocPos( aPaperPos );
|
|
nPara = pOwner->pEditEngine->FindParagraph( aDocPos.Y() );
|
|
|
|
if ( bBullet )
|
|
{
|
|
reTarget = MouseTarget::Bullet;
|
|
}
|
|
else
|
|
{
|
|
// Check for hyperlink
|
|
const SvxFieldItem* pFieldItem = pEditView->GetField( aMousePosWin );
|
|
if ( pFieldItem && pFieldItem->GetField() && dynamic_cast< const SvxURLField* >(pFieldItem->GetField()) != nullptr )
|
|
reTarget = MouseTarget::Hypertext;
|
|
}
|
|
}
|
|
}
|
|
return nPara;
|
|
}
|
|
|
|
bool OutlinerView::MouseMove( const MouseEvent& rMEvt )
|
|
{
|
|
if( ( pOwner->GetOutlinerMode() == OutlinerMode::TextObject ) || pEditView->getEditEngine().IsInSelectionMode())
|
|
return pEditView->MouseMove( rMEvt );
|
|
|
|
Point aMousePosWin( pEditView->GetOutputDevice().PixelToLogic( rMEvt.GetPosPixel() ) );
|
|
if( !pEditView->GetOutputArea().Contains( aMousePosWin ) )
|
|
return false;
|
|
|
|
PointerStyle aPointer = GetPointer( rMEvt.GetPosPixel() );
|
|
pEditView->GetWindow()->SetPointer( aPointer );
|
|
return pEditView->MouseMove( rMEvt );
|
|
}
|
|
|
|
|
|
bool OutlinerView::MouseButtonDown( const MouseEvent& rMEvt )
|
|
{
|
|
if ( ( pOwner->GetOutlinerMode() == OutlinerMode::TextObject ) || pEditView->getEditEngine().IsInSelectionMode() )
|
|
return pEditView->MouseButtonDown( rMEvt );
|
|
|
|
Point aMousePosWin( pEditView->GetOutputDevice().PixelToLogic( rMEvt.GetPosPixel() ) );
|
|
if( !pEditView->GetOutputArea().Contains( aMousePosWin ) )
|
|
return false;
|
|
|
|
PointerStyle aPointer = GetPointer( rMEvt.GetPosPixel() );
|
|
pEditView->GetWindow()->SetPointer( aPointer );
|
|
|
|
MouseTarget eTarget;
|
|
sal_Int32 nPara = ImpCheckMousePos( rMEvt.GetPosPixel(), eTarget );
|
|
if ( eTarget == MouseTarget::Bullet )
|
|
{
|
|
Paragraph* pPara = pOwner->pParaList->GetParagraph( nPara );
|
|
bool bHasChildren = (pPara && pOwner->pParaList->HasChildren(pPara));
|
|
if( rMEvt.GetClicks() == 1 )
|
|
{
|
|
sal_Int32 nEndPara = nPara;
|
|
if ( bHasChildren && pOwner->pParaList->HasVisibleChildren(pPara) )
|
|
nEndPara += pOwner->pParaList->GetChildCount( pPara );
|
|
// The selection is inverted, so that EditEngine does not scroll
|
|
ESelection aSel(nEndPara, EE_TEXTPOS_ALL, nPara, 0 );
|
|
pEditView->SetSelection( aSel );
|
|
}
|
|
else if( rMEvt.GetClicks() == 2 && bHasChildren )
|
|
ImpToggleExpand( pPara );
|
|
|
|
return true;
|
|
}
|
|
|
|
// special case for outliner view in impress, check if double click hits the page icon for toggle
|
|
if( (nPara == EE_PARA_NOT_FOUND) && (pOwner->GetOutlinerMode() == OutlinerMode::OutlineView) && (eTarget == MouseTarget::Text) && (rMEvt.GetClicks() == 2) )
|
|
{
|
|
ESelection aSel( pEditView->GetSelection() );
|
|
nPara = aSel.nStartPara;
|
|
Paragraph* pPara = pOwner->pParaList->GetParagraph( nPara );
|
|
if( (pPara && pOwner->pParaList->HasChildren(pPara)) && pPara->HasFlag(ParaFlag::ISPAGE) )
|
|
{
|
|
ImpToggleExpand( pPara );
|
|
}
|
|
}
|
|
return pEditView->MouseButtonDown( rMEvt );
|
|
}
|
|
|
|
|
|
bool OutlinerView::MouseButtonUp( const MouseEvent& rMEvt )
|
|
{
|
|
if ( ( pOwner->GetOutlinerMode() == OutlinerMode::TextObject ) || pEditView->getEditEngine().IsInSelectionMode() )
|
|
return pEditView->MouseButtonUp( rMEvt );
|
|
|
|
Point aMousePosWin( pEditView->GetOutputDevice().PixelToLogic( rMEvt.GetPosPixel() ) );
|
|
if( !pEditView->GetOutputArea().Contains( aMousePosWin ) )
|
|
return false;
|
|
|
|
PointerStyle aPointer = GetPointer( rMEvt.GetPosPixel() );
|
|
pEditView->GetWindow()->SetPointer( aPointer );
|
|
|
|
return pEditView->MouseButtonUp( rMEvt );
|
|
}
|
|
|
|
void OutlinerView::ReleaseMouse()
|
|
{
|
|
pEditView->ReleaseMouse();
|
|
}
|
|
|
|
void OutlinerView::ImpToggleExpand( Paragraph const * pPara )
|
|
{
|
|
sal_Int32 nPara = pOwner->pParaList->GetAbsPos( pPara );
|
|
pEditView->SetSelection( ESelection( nPara, 0, nPara, 0 ) );
|
|
ImplExpandOrCollaps( nPara, nPara, !pOwner->pParaList->HasVisibleChildren( pPara ) );
|
|
pEditView->ShowCursor();
|
|
}
|
|
|
|
void OutlinerView::Select( Paragraph const * pParagraph, bool bSelect )
|
|
{
|
|
sal_Int32 nPara = pOwner->pParaList->GetAbsPos( pParagraph );
|
|
sal_Int32 nEnd = 0;
|
|
if ( bSelect )
|
|
nEnd = SAL_MAX_INT32;
|
|
|
|
ESelection aSel( nPara, 0, nPara, nEnd );
|
|
pEditView->SetSelection( aSel );
|
|
}
|
|
|
|
void OutlinerView::SetDepth(sal_Int32 nParagraph, sal_Int16 nDepth)
|
|
{
|
|
Paragraph* pParagraph = pOwner->GetParagraph(nParagraph);
|
|
pOwner->SetDepth(pParagraph, nDepth);
|
|
}
|
|
|
|
sal_Int16 OutlinerView::GetDepth() const
|
|
{
|
|
ESelection aESelection = GetSelection();
|
|
aESelection.Adjust();
|
|
sal_Int16 nDepth = pOwner->GetDepth(aESelection.nStartPara);
|
|
for (sal_Int32 nPara = aESelection.nStartPara + 1; nPara <= aESelection.nEndPara; ++nPara)
|
|
{
|
|
if (nDepth != pOwner->GetDepth(nPara))
|
|
return -2;
|
|
}
|
|
return nDepth;
|
|
}
|
|
|
|
void OutlinerView::SetAttribs( const SfxItemSet& rAttrs )
|
|
{
|
|
bool bUpdate = pOwner->pEditEngine->SetUpdateLayout( false );
|
|
|
|
if( !pOwner->IsInUndo() && pOwner->IsUndoEnabled() )
|
|
pOwner->UndoActionStart( OLUNDO_ATTR );
|
|
|
|
ParaRange aSel = ImpGetSelectedParagraphs( false );
|
|
|
|
pEditView->SetAttribs( rAttrs );
|
|
|
|
// Update Bullet text
|
|
for( sal_Int32 nPara= aSel.nStartPara; nPara <= aSel.nEndPara; nPara++ )
|
|
{
|
|
pOwner->ImplCheckNumBulletItem( nPara );
|
|
pOwner->ImplCalcBulletText( nPara, false, false );
|
|
|
|
if( !pOwner->IsInUndo() && pOwner->IsUndoEnabled() )
|
|
pOwner->InsertUndo( std::make_unique<OutlinerUndoCheckPara>( pOwner, nPara ) );
|
|
}
|
|
|
|
if( !pOwner->IsInUndo() && pOwner->IsUndoEnabled() )
|
|
pOwner->UndoActionEnd();
|
|
|
|
pEditView->SetEditEngineUpdateLayout( bUpdate );
|
|
}
|
|
|
|
ParaRange OutlinerView::ImpGetSelectedParagraphs( bool bIncludeHiddenChildren )
|
|
{
|
|
ESelection aSel = pEditView->GetSelection();
|
|
ParaRange aParas( aSel.nStartPara, aSel.nEndPara );
|
|
aParas.Adjust();
|
|
|
|
// Record the invisible Children of the last Parents in the selection
|
|
if ( bIncludeHiddenChildren )
|
|
{
|
|
Paragraph* pLast = pOwner->pParaList->GetParagraph( aParas.nEndPara );
|
|
if ( pOwner->pParaList->HasHiddenChildren( pLast ) )
|
|
aParas.nEndPara = aParas.nEndPara + pOwner->pParaList->GetChildCount( pLast );
|
|
}
|
|
return aParas;
|
|
}
|
|
|
|
// TODO: Name should be changed!
|
|
void OutlinerView::AdjustDepth( short nDX )
|
|
{
|
|
Indent( nDX );
|
|
}
|
|
|
|
void OutlinerView::Indent( short nDiff )
|
|
{
|
|
if( !nDiff || ( ( nDiff > 0 ) && ImpCalcSelectedPages( true ) && !pOwner->ImpCanIndentSelectedPages( this ) ) )
|
|
return;
|
|
|
|
const bool bOutlinerView = bool(pOwner->pEditEngine->GetControlWord() & EEControlBits::OUTLINER);
|
|
bool bUpdate = pOwner->pEditEngine->SetUpdateLayout( false );
|
|
|
|
bool bUndo = !pOwner->IsInUndo() && pOwner->IsUndoEnabled();
|
|
|
|
if( bUndo )
|
|
pOwner->UndoActionStart( OLUNDO_DEPTH );
|
|
|
|
sal_Int16 nMinDepth = -1; // Optimization: avoid recalculate too many paragraphs if not really needed.
|
|
|
|
ParaRange aSel = ImpGetSelectedParagraphs( true );
|
|
for ( sal_Int32 nPara = aSel.nStartPara; nPara <= aSel.nEndPara; nPara++ )
|
|
{
|
|
Paragraph* pPara = pOwner->pParaList->GetParagraph( nPara );
|
|
|
|
sal_Int16 nOldDepth = pPara->GetDepth();
|
|
sal_Int16 nNewDepth = nOldDepth + nDiff;
|
|
|
|
if( bOutlinerView && nPara )
|
|
{
|
|
const bool bPage = pPara->HasFlag(ParaFlag::ISPAGE);
|
|
if( (bPage && (nDiff == +1)) || (!bPage && (nDiff == -1) && (nOldDepth <= 0)) )
|
|
{
|
|
// Notify App
|
|
pOwner->nDepthChangedHdlPrevDepth = nOldDepth;
|
|
ParaFlag nPrevFlags = pPara->nFlags;
|
|
|
|
if( bPage )
|
|
pPara->RemoveFlag( ParaFlag::ISPAGE );
|
|
else
|
|
pPara->SetFlag( ParaFlag::ISPAGE );
|
|
|
|
pOwner->DepthChangedHdl(pPara, nPrevFlags);
|
|
pOwner->pEditEngine->QuickMarkInvalid( ESelection( nPara, 0, nPara, 0 ) );
|
|
|
|
if( bUndo )
|
|
pOwner->InsertUndo( std::make_unique<OutlinerUndoChangeParaFlags>( pOwner, nPara, nPrevFlags, pPara->nFlags ) );
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// do not switch off numeration with tab
|
|
if( (nOldDepth == 0) && (nNewDepth == -1) )
|
|
continue;
|
|
|
|
// do not indent if there is no numeration enabled
|
|
if( nOldDepth == -1 )
|
|
continue;
|
|
|
|
if ( nNewDepth < Outliner::gnMinDepth )
|
|
nNewDepth = Outliner::gnMinDepth;
|
|
if ( nNewDepth > pOwner->nMaxDepth )
|
|
nNewDepth = pOwner->nMaxDepth;
|
|
|
|
if( nOldDepth < nMinDepth )
|
|
nMinDepth = nOldDepth;
|
|
if( nNewDepth < nMinDepth )
|
|
nMinDepth = nNewDepth;
|
|
|
|
if( nOldDepth != nNewDepth )
|
|
{
|
|
if ( ( nPara == aSel.nStartPara ) && aSel.nStartPara && ( pOwner->GetOutlinerMode() != OutlinerMode::TextObject ))
|
|
{
|
|
// Special case: the predecessor of an indented paragraph is
|
|
// invisible and is now on the same level as the visible
|
|
// paragraph. In this case, the next visible paragraph is
|
|
// searched for and fluffed.
|
|
#ifdef DBG_UTIL
|
|
Paragraph* _pPara = pOwner->pParaList->GetParagraph( aSel.nStartPara );
|
|
DBG_ASSERT(_pPara->IsVisible(),"Selected Paragraph invisible ?!");
|
|
#endif
|
|
Paragraph* pPrev= pOwner->pParaList->GetParagraph( aSel.nStartPara-1 );
|
|
|
|
if( !pPrev->IsVisible() && ( pPrev->GetDepth() == nNewDepth ) )
|
|
{
|
|
// Predecessor is collapsed and is on the same level
|
|
// => find next visible paragraph and expand it
|
|
pPrev = pOwner->pParaList->GetParent( pPrev );
|
|
while( !pPrev->IsVisible() )
|
|
pPrev = pOwner->pParaList->GetParent( pPrev );
|
|
|
|
pOwner->Expand( pPrev );
|
|
pOwner->InvalidateBullet(pOwner->pParaList->GetAbsPos(pPrev));
|
|
}
|
|
}
|
|
|
|
pOwner->nDepthChangedHdlPrevDepth = nOldDepth;
|
|
ParaFlag nPrevFlags = pPara->nFlags;
|
|
|
|
pOwner->ImplInitDepth( nPara, nNewDepth, true );
|
|
pOwner->ImplCalcBulletText( nPara, false, false );
|
|
|
|
if ( pOwner->GetOutlinerMode() == OutlinerMode::OutlineObject )
|
|
pOwner->ImplSetLevelDependentStyleSheet( nPara );
|
|
|
|
// Notify App
|
|
pOwner->DepthChangedHdl(pPara, nPrevFlags);
|
|
}
|
|
else
|
|
{
|
|
// Needs at least a repaint...
|
|
pOwner->pEditEngine->QuickMarkInvalid( ESelection( nPara, 0, nPara, 0 ) );
|
|
}
|
|
}
|
|
|
|
sal_Int32 nParas = pOwner->pParaList->GetParagraphCount();
|
|
for ( sal_Int32 n = aSel.nEndPara+1; n < nParas; n++ )
|
|
{
|
|
Paragraph* pPara = pOwner->pParaList->GetParagraph( n );
|
|
if ( pPara->GetDepth() < nMinDepth )
|
|
break;
|
|
pOwner->ImplCalcBulletText( n, false, false );
|
|
}
|
|
|
|
if ( bUpdate )
|
|
{
|
|
pEditView->SetEditEngineUpdateLayout( true );
|
|
pEditView->ShowCursor();
|
|
}
|
|
|
|
if( bUndo )
|
|
pOwner->UndoActionEnd();
|
|
}
|
|
|
|
void OutlinerView::AdjustHeight( tools::Long nDY )
|
|
{
|
|
pEditView->MoveParagraphs( nDY );
|
|
}
|
|
|
|
tools::Rectangle OutlinerView::GetVisArea() const
|
|
{
|
|
return pEditView->GetVisArea();
|
|
}
|
|
|
|
void OutlinerView::Expand()
|
|
{
|
|
ParaRange aParas = ImpGetSelectedParagraphs( false );
|
|
ImplExpandOrCollaps( aParas.nStartPara, aParas.nEndPara, true );
|
|
}
|
|
|
|
|
|
void OutlinerView::Collapse()
|
|
{
|
|
ParaRange aParas = ImpGetSelectedParagraphs( false );
|
|
ImplExpandOrCollaps( aParas.nStartPara, aParas.nEndPara, false );
|
|
}
|
|
|
|
|
|
void OutlinerView::ExpandAll()
|
|
{
|
|
ImplExpandOrCollaps( 0, pOwner->pParaList->GetParagraphCount()-1, true );
|
|
}
|
|
|
|
|
|
void OutlinerView::CollapseAll()
|
|
{
|
|
ImplExpandOrCollaps( 0, pOwner->pParaList->GetParagraphCount()-1, false );
|
|
}
|
|
|
|
void OutlinerView::ImplExpandOrCollaps( sal_Int32 nStartPara, sal_Int32 nEndPara, bool bExpand )
|
|
{
|
|
bool bUpdate = pOwner->SetUpdateLayout( false );
|
|
|
|
bool bUndo = !pOwner->IsInUndo() && pOwner->IsUndoEnabled();
|
|
if( bUndo )
|
|
pOwner->UndoActionStart( bExpand ? OLUNDO_EXPAND : OLUNDO_COLLAPSE );
|
|
|
|
for ( sal_Int32 nPara = nStartPara; nPara <= nEndPara; nPara++ )
|
|
{
|
|
Paragraph* pPara = pOwner->pParaList->GetParagraph( nPara );
|
|
bool bDone = bExpand ? pOwner->Expand( pPara ) : pOwner->Collapse( pPara );
|
|
if( bDone )
|
|
{
|
|
// The line under the paragraph should disappear ...
|
|
pOwner->pEditEngine->QuickMarkToBeRepainted( nPara );
|
|
}
|
|
}
|
|
|
|
if( bUndo )
|
|
pOwner->UndoActionEnd();
|
|
|
|
if ( bUpdate )
|
|
{
|
|
pOwner->SetUpdateLayout( true );
|
|
pEditView->ShowCursor();
|
|
}
|
|
}
|
|
|
|
void OutlinerView::InsertText( const OutlinerParaObject& rParaObj )
|
|
{
|
|
// Like Paste, only EditView::Insert, instead of EditView::Paste.
|
|
// Actually not quite true that possible indentations must be corrected,
|
|
// but that comes later by a universal import. The indentation level is
|
|
// then determined right in the Inserted method.
|
|
// Possible structure:
|
|
// pImportInfo with DestPara, DestPos, nFormat, pParaObj...
|
|
// Possibly problematic:
|
|
// EditEngine, RTF => Splitting the area, later join together.
|
|
|
|
if ( ImpCalcSelectedPages( false ) && !pOwner->ImpCanDeleteSelectedPages( this ) )
|
|
return;
|
|
|
|
pOwner->UndoActionStart( OLUNDO_INSERT );
|
|
|
|
const bool bPrevUpdateLayout = pOwner->pEditEngine->SetUpdateLayout( false );
|
|
sal_Int32 nStart, nParaCount;
|
|
nParaCount = pOwner->pEditEngine->GetParagraphCount();
|
|
sal_uInt16 nSize = ImpInitPaste( nStart );
|
|
pEditView->InsertText( rParaObj.GetTextObject() );
|
|
ImpPasted( nStart, nParaCount, nSize);
|
|
pEditView->SetEditEngineUpdateLayout( bPrevUpdateLayout );
|
|
|
|
pOwner->UndoActionEnd();
|
|
|
|
pEditView->ShowCursor();
|
|
}
|
|
|
|
|
|
void OutlinerView::Cut()
|
|
{
|
|
if ( !ImpCalcSelectedPages( false ) || pOwner->ImpCanDeleteSelectedPages( this ) ) {
|
|
pEditView->Cut();
|
|
// Chaining handling
|
|
aEndCutPasteLink.Call(nullptr);
|
|
}
|
|
}
|
|
|
|
void OutlinerView::PasteSpecial(SotClipboardFormatId format)
|
|
{
|
|
Paste( true, format );
|
|
}
|
|
|
|
void OutlinerView::Paste( bool bUseSpecial, SotClipboardFormatId format)
|
|
{
|
|
if ( ImpCalcSelectedPages( false ) && !pOwner->ImpCanDeleteSelectedPages( this ) )
|
|
return;
|
|
|
|
pOwner->UndoActionStart( OLUNDO_INSERT );
|
|
|
|
const bool bPrevUpdateLayout = pOwner->pEditEngine->SetUpdateLayout( false );
|
|
pOwner->bPasting = true;
|
|
|
|
if ( bUseSpecial )
|
|
pEditView->PasteSpecial(format);
|
|
else
|
|
pEditView->Paste();
|
|
|
|
if ( pOwner->GetOutlinerMode() == OutlinerMode::OutlineObject )
|
|
{
|
|
const sal_Int32 nParaCount = pOwner->pEditEngine->GetParagraphCount();
|
|
|
|
for( sal_Int32 nPara = 0; nPara < nParaCount; nPara++ )
|
|
pOwner->ImplSetLevelDependentStyleSheet( nPara );
|
|
}
|
|
|
|
pEditView->SetEditEngineUpdateLayout( bPrevUpdateLayout );
|
|
pOwner->UndoActionEnd();
|
|
pEditView->ShowCursor();
|
|
|
|
// Chaining handling
|
|
// NOTE: We need to do this last because it pEditView may be deleted if a switch of box occurs
|
|
aEndCutPasteLink.Call(nullptr);
|
|
}
|
|
|
|
void OutlinerView::CreateSelectionList (std::vector<Paragraph*> &aSelList)
|
|
{
|
|
ParaRange aParas = ImpGetSelectedParagraphs( true );
|
|
|
|
for ( sal_Int32 nPara = aParas.nStartPara; nPara <= aParas.nEndPara; nPara++ )
|
|
{
|
|
Paragraph* pPara = pOwner->pParaList->GetParagraph( nPara );
|
|
aSelList.push_back(pPara);
|
|
}
|
|
}
|
|
|
|
void OutlinerView::SetStyleSheet(const OUString& rStyleName)
|
|
{
|
|
ParaRange aParas = ImpGetSelectedParagraphs(false);
|
|
|
|
auto pStyle = pOwner->GetStyleSheetPool()->Find(rStyleName, SfxStyleFamily::Para);
|
|
if (!pStyle)
|
|
return;
|
|
|
|
for (sal_Int32 nPara = aParas.nStartPara; nPara <= aParas.nEndPara; nPara++)
|
|
pOwner->SetStyleSheet(nPara, static_cast<SfxStyleSheet*>(pStyle));
|
|
}
|
|
|
|
const SfxStyleSheet* OutlinerView::GetStyleSheet() const
|
|
{
|
|
return pEditView->GetStyleSheet();
|
|
}
|
|
|
|
SfxStyleSheet* OutlinerView::GetStyleSheet()
|
|
{
|
|
return pEditView->GetStyleSheet();
|
|
}
|
|
|
|
PointerStyle OutlinerView::GetPointer( const Point& rPosPixel )
|
|
{
|
|
MouseTarget eTarget;
|
|
ImpCheckMousePos( rPosPixel, eTarget );
|
|
|
|
PointerStyle ePointerStyle = PointerStyle::Arrow;
|
|
if ( eTarget == MouseTarget::Text )
|
|
{
|
|
ePointerStyle = GetOutliner()->IsVertical() ? PointerStyle::TextVertical : PointerStyle::Text;
|
|
}
|
|
else if ( eTarget == MouseTarget::Hypertext )
|
|
{
|
|
ePointerStyle = PointerStyle::RefHand;
|
|
}
|
|
else if ( eTarget == MouseTarget::Bullet )
|
|
{
|
|
ePointerStyle = PointerStyle::Move;
|
|
}
|
|
|
|
return ePointerStyle;
|
|
}
|
|
|
|
|
|
sal_Int32 OutlinerView::ImpInitPaste( sal_Int32& rStart )
|
|
{
|
|
pOwner->bPasting = true;
|
|
ESelection aSelection( pEditView->GetSelection() );
|
|
aSelection.Adjust();
|
|
rStart = aSelection.nStartPara;
|
|
sal_Int32 nSize = aSelection.nEndPara - aSelection.nStartPara + 1;
|
|
return nSize;
|
|
}
|
|
|
|
|
|
void OutlinerView::ImpPasted( sal_Int32 nStart, sal_Int32 nPrevParaCount, sal_Int32 nSize)
|
|
{
|
|
pOwner->bPasting = false;
|
|
sal_Int32 nCurParaCount = pOwner->pEditEngine->GetParagraphCount();
|
|
if( nCurParaCount < nPrevParaCount )
|
|
nSize = nSize - ( nPrevParaCount - nCurParaCount );
|
|
else
|
|
nSize = nSize + ( nCurParaCount - nPrevParaCount );
|
|
pOwner->ImpTextPasted( nStart, nSize );
|
|
}
|
|
|
|
bool OutlinerView::Command(const CommandEvent& rCEvt)
|
|
{
|
|
return pEditView->Command(rCEvt);
|
|
}
|
|
|
|
void OutlinerView::SelectRange( sal_Int32 nFirst, sal_Int32 nCount )
|
|
{
|
|
sal_Int32 nLast = nFirst+nCount;
|
|
nCount = pOwner->pParaList->GetParagraphCount();
|
|
if( nLast <= nCount )
|
|
nLast = nCount - 1;
|
|
ESelection aSel( nFirst, 0, nLast, EE_TEXTPOS_ALL );
|
|
pEditView->SetSelection( aSel );
|
|
}
|
|
|
|
|
|
sal_Int32 OutlinerView::ImpCalcSelectedPages( bool bIncludeFirstSelected )
|
|
{
|
|
ESelection aSel( pEditView->GetSelection() );
|
|
aSel.Adjust();
|
|
|
|
sal_Int32 nPages = 0;
|
|
sal_Int32 nFirstPage = EE_PARA_MAX_COUNT;
|
|
sal_Int32 nStartPara = aSel.nStartPara;
|
|
if ( !bIncludeFirstSelected )
|
|
nStartPara++; // All paragraphs after StartPara will be deleted
|
|
for ( sal_Int32 nPara = nStartPara; nPara <= aSel.nEndPara; nPara++ )
|
|
{
|
|
Paragraph* pPara = pOwner->pParaList->GetParagraph( nPara );
|
|
assert(pPara && "ImpCalcSelectedPages: invalid Selection?");
|
|
if( pPara->HasFlag(ParaFlag::ISPAGE) )
|
|
{
|
|
nPages++;
|
|
if( nFirstPage == EE_PARA_MAX_COUNT )
|
|
nFirstPage = nPara;
|
|
}
|
|
}
|
|
|
|
if( nPages )
|
|
{
|
|
pOwner->nDepthChangedHdlPrevDepth = nPages;
|
|
pOwner->mnFirstSelPage = nFirstPage;
|
|
}
|
|
|
|
return nPages;
|
|
}
|
|
|
|
|
|
void OutlinerView::ToggleBullets()
|
|
{
|
|
pOwner->UndoActionStart( OLUNDO_DEPTH );
|
|
|
|
ESelection aSel( pEditView->GetSelection() );
|
|
aSel.Adjust();
|
|
|
|
const bool bUpdate = pOwner->pEditEngine->SetUpdateLayout( false );
|
|
|
|
sal_Int16 nNewDepth = -2;
|
|
const SvxNumRule* pDefaultBulletNumRule = nullptr;
|
|
|
|
for ( sal_Int32 nPara = aSel.nStartPara; nPara <= aSel.nEndPara; nPara++ )
|
|
{
|
|
Paragraph* pPara = pOwner->pParaList->GetParagraph( nPara );
|
|
DBG_ASSERT(pPara, "OutlinerView::ToggleBullets(), illegal selection?");
|
|
|
|
if( pPara )
|
|
{
|
|
if( nNewDepth == -2 )
|
|
{
|
|
nNewDepth = (pOwner->GetDepth(nPara) == -1) ? 0 : -1;
|
|
if ( nNewDepth == 0 )
|
|
{
|
|
// determine default numbering rule for bullets
|
|
const ESelection aSelection(nPara, 0);
|
|
const SfxItemSet aTmpSet(pOwner->pEditEngine->GetAttribs(aSelection));
|
|
const SfxPoolItem& rPoolItem = aTmpSet.GetPool()->GetUserOrPoolDefaultItem( EE_PARA_NUMBULLET );
|
|
const SvxNumBulletItem* pNumBulletItem = dynamic_cast< const SvxNumBulletItem* >(&rPoolItem);
|
|
pDefaultBulletNumRule = pNumBulletItem ? &pNumBulletItem->GetNumRule() : nullptr;
|
|
}
|
|
}
|
|
|
|
pOwner->SetDepth( pPara, nNewDepth );
|
|
|
|
if( nNewDepth == -1 )
|
|
{
|
|
const SfxItemSet& rAttrs = pOwner->GetParaAttribs( nPara );
|
|
if ( rAttrs.GetItemState( EE_PARA_BULLETSTATE ) == SfxItemState::SET )
|
|
{
|
|
SfxItemSet aAttrs(rAttrs);
|
|
aAttrs.ClearItem( EE_PARA_BULLETSTATE );
|
|
pOwner->SetParaAttribs( nPara, aAttrs );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( pDefaultBulletNumRule )
|
|
{
|
|
const SvxNumberFormat* pFmt = pOwner ->GetNumberFormat( nPara );
|
|
if ( !pFmt
|
|
|| ( pFmt->GetNumberingType() != SVX_NUM_BITMAP
|
|
&& pFmt->GetNumberingType() != SVX_NUM_CHAR_SPECIAL ) )
|
|
{
|
|
SfxItemSet aAttrs( pOwner->GetParaAttribs( nPara ) );
|
|
SvxNumRule aNewNumRule( *pDefaultBulletNumRule );
|
|
aAttrs.Put( SvxNumBulletItem( std::move(aNewNumRule), EE_PARA_NUMBULLET ) );
|
|
pOwner->SetParaAttribs( nPara, aAttrs );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const sal_Int32 nParaCount = pOwner->pParaList->GetParagraphCount();
|
|
pOwner->ImplCheckParagraphs( aSel.nStartPara, nParaCount );
|
|
|
|
sal_Int32 nEndPara = (nParaCount > 0) ? nParaCount-1 : nParaCount;
|
|
pOwner->pEditEngine->QuickMarkInvalid( ESelection( aSel.nStartPara, 0, nEndPara, 0 ) );
|
|
|
|
pOwner->pEditEngine->SetUpdateLayout( bUpdate );
|
|
|
|
pOwner->UndoActionEnd();
|
|
}
|
|
|
|
|
|
void OutlinerView::ToggleBulletsNumbering(
|
|
const bool bToggle,
|
|
const bool bHandleBullets,
|
|
const SvxNumRule* pNumRule )
|
|
{
|
|
ESelection aSel( pEditView->GetSelection() );
|
|
aSel.Adjust();
|
|
|
|
bool bToggleOn = true;
|
|
if ( bToggle )
|
|
{
|
|
bToggleOn = false;
|
|
const sal_Int16 nBulletNumberingStatus( pOwner->GetBulletsNumberingStatus( aSel.nStartPara, aSel.nEndPara ) );
|
|
if ( nBulletNumberingStatus != 0 && bHandleBullets )
|
|
{
|
|
// not all paragraphs have bullets and method called to toggle bullets --> bullets on
|
|
bToggleOn = true;
|
|
}
|
|
else if ( nBulletNumberingStatus != 1 && !bHandleBullets )
|
|
{
|
|
// not all paragraphs have numbering and method called to toggle numberings --> numberings on
|
|
bToggleOn = true;
|
|
}
|
|
}
|
|
if ( bToggleOn )
|
|
{
|
|
// apply bullets/numbering for selected paragraphs
|
|
ApplyBulletsNumbering( bHandleBullets, pNumRule, bToggle, true );
|
|
}
|
|
else
|
|
{
|
|
// switch off bullets/numbering for selected paragraphs
|
|
SwitchOffBulletsNumbering( true );
|
|
}
|
|
}
|
|
|
|
void OutlinerView::EnsureNumberingIsOn()
|
|
{
|
|
pOwner->UndoActionStart(OLUNDO_DEPTH);
|
|
|
|
ESelection aSel(pEditView->GetSelection());
|
|
aSel.Adjust();
|
|
|
|
const bool bUpdate = pOwner->pEditEngine->IsUpdateLayout();
|
|
pOwner->pEditEngine->SetUpdateLayout(false);
|
|
|
|
for (sal_Int32 nPara = aSel.nStartPara; nPara <= aSel.nEndPara; nPara++)
|
|
{
|
|
Paragraph* pPara = pOwner->pParaList->GetParagraph(nPara);
|
|
DBG_ASSERT(pPara, "OutlinerView::EnableBullets(), illegal selection?");
|
|
|
|
if (pPara && pOwner->GetDepth(nPara) == -1)
|
|
pOwner->SetDepth(pPara, 0);
|
|
}
|
|
|
|
sal_Int32 nParaCount = pOwner->pParaList->GetParagraphCount();
|
|
pOwner->ImplCheckParagraphs(aSel.nStartPara, nParaCount);
|
|
|
|
const sal_Int32 nEndPara = (nParaCount > 0) ? nParaCount-1 : nParaCount;
|
|
pOwner->pEditEngine->QuickMarkInvalid(ESelection(aSel.nStartPara, 0, nEndPara, 0));
|
|
|
|
pOwner->pEditEngine->SetUpdateLayout(bUpdate);
|
|
|
|
pOwner->UndoActionEnd();
|
|
}
|
|
|
|
void OutlinerView::ApplyBulletsNumbering(
|
|
const bool bHandleBullets,
|
|
const SvxNumRule* pNewNumRule,
|
|
const bool bCheckCurrentNumRuleBeforeApplyingNewNumRule,
|
|
const bool bAtSelection )
|
|
{
|
|
if (!pOwner || !pOwner->pEditEngine || !pOwner->pParaList)
|
|
{
|
|
return;
|
|
}
|
|
|
|
pOwner->UndoActionStart(OLUNDO_DEPTH);
|
|
const bool bUpdate = pOwner->pEditEngine->SetUpdateLayout(false);
|
|
|
|
sal_Int32 nStartPara = 0;
|
|
sal_Int32 nEndPara = 0;
|
|
if ( bAtSelection )
|
|
{
|
|
ESelection aSel( pEditView->GetSelection() );
|
|
aSel.Adjust();
|
|
nStartPara = aSel.nStartPara;
|
|
nEndPara = aSel.nEndPara;
|
|
}
|
|
else
|
|
{
|
|
nStartPara = 0;
|
|
nEndPara = pOwner->pParaList->GetParagraphCount() - 1;
|
|
}
|
|
|
|
for (sal_Int32 nPara = nStartPara; nPara <= nEndPara; ++nPara)
|
|
{
|
|
Paragraph* pPara = pOwner->pParaList->GetParagraph(nPara);
|
|
DBG_ASSERT(pPara, "OutlinerView::ApplyBulletsNumbering(..), illegal selection?");
|
|
|
|
if (pPara)
|
|
{
|
|
const sal_Int16 nDepth = pOwner->GetDepth(nPara);
|
|
if ( nDepth == -1 )
|
|
{
|
|
pOwner->SetDepth( pPara, 0 );
|
|
}
|
|
|
|
const SfxItemSet& rAttrs = pOwner->GetParaAttribs(nPara);
|
|
SfxItemSet aAttrs(rAttrs);
|
|
aAttrs.Put(SfxBoolItem(EE_PARA_BULLETSTATE, true));
|
|
|
|
// apply new numbering rule
|
|
if ( pNewNumRule )
|
|
{
|
|
bool bApplyNumRule = false;
|
|
if ( !bCheckCurrentNumRuleBeforeApplyingNewNumRule )
|
|
{
|
|
bApplyNumRule = true;
|
|
}
|
|
else
|
|
{
|
|
const SvxNumberFormat* pFmt = pOwner ->GetNumberFormat(nPara);
|
|
if (!pFmt)
|
|
{
|
|
bApplyNumRule = true;
|
|
}
|
|
else
|
|
{
|
|
sal_Int16 nNumType = pFmt->GetNumberingType();
|
|
if ( bHandleBullets
|
|
&& nNumType != SVX_NUM_BITMAP && nNumType != SVX_NUM_CHAR_SPECIAL)
|
|
{
|
|
// Set to Normal bullet, old bullet type is Numbering bullet.
|
|
bApplyNumRule = true;
|
|
}
|
|
else if ( !bHandleBullets
|
|
&& (nNumType == SVX_NUM_BITMAP || nNumType == SVX_NUM_CHAR_SPECIAL))
|
|
{
|
|
// Set to Numbering bullet, old bullet type is Normal bullet.
|
|
bApplyNumRule = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( bApplyNumRule )
|
|
{
|
|
SvxNumRule aNewRule(*pNewNumRule);
|
|
|
|
// Get old bullet space.
|
|
{
|
|
const SvxNumBulletItem* pNumBulletItem = rAttrs.GetItemIfSet(EE_PARA_NUMBULLET, false);
|
|
if (pNumBulletItem)
|
|
{
|
|
// Use default value when has not contain bullet item.
|
|
ESelection aSelection(nPara, 0);
|
|
SfxItemSet aTmpSet(pOwner->pEditEngine->GetAttribs(aSelection));
|
|
pNumBulletItem = aTmpSet.GetItem(EE_PARA_NUMBULLET);
|
|
}
|
|
|
|
if (pNumBulletItem)
|
|
{
|
|
const sal_uInt16 nLevelCnt = std::min(pNumBulletItem->GetNumRule().GetLevelCount(), aNewRule.GetLevelCount());
|
|
for ( sal_uInt16 nLevel = 0; nLevel < nLevelCnt; ++nLevel )
|
|
{
|
|
const SvxNumberFormat* pOldFmt = pNumBulletItem->GetNumRule().Get(nLevel);
|
|
const SvxNumberFormat* pNewFmt = aNewRule.Get(nLevel);
|
|
if (pOldFmt && pNewFmt && (pOldFmt->GetFirstLineOffset() != pNewFmt->GetFirstLineOffset() || pOldFmt->GetAbsLSpace() != pNewFmt->GetAbsLSpace()))
|
|
{
|
|
SvxNumberFormat aNewFmtClone(*pNewFmt);
|
|
aNewFmtClone.SetFirstLineOffset(pOldFmt->GetFirstLineOffset());
|
|
aNewFmtClone.SetAbsLSpace(pOldFmt->GetAbsLSpace());
|
|
aNewRule.SetLevel(nLevel, &aNewFmtClone);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
aAttrs.Put(SvxNumBulletItem(std::move(aNewRule), EE_PARA_NUMBULLET));
|
|
}
|
|
}
|
|
pOwner->SetParaAttribs(nPara, aAttrs);
|
|
}
|
|
}
|
|
|
|
const sal_uInt16 nParaCount = static_cast<sal_uInt16>(pOwner->pParaList->GetParagraphCount());
|
|
pOwner->ImplCheckParagraphs( nStartPara, nParaCount );
|
|
pOwner->pEditEngine->QuickMarkInvalid( ESelection( nStartPara, 0, nParaCount, 0 ) );
|
|
|
|
pOwner->pEditEngine->SetUpdateLayout( bUpdate );
|
|
|
|
pOwner->UndoActionEnd();
|
|
}
|
|
|
|
|
|
void OutlinerView::SwitchOffBulletsNumbering(
|
|
const bool bAtSelection )
|
|
{
|
|
sal_Int32 nStartPara = 0;
|
|
sal_Int32 nEndPara = 0;
|
|
if ( bAtSelection )
|
|
{
|
|
ESelection aSel( pEditView->GetSelection() );
|
|
aSel.Adjust();
|
|
nStartPara = aSel.nStartPara;
|
|
nEndPara = aSel.nEndPara;
|
|
}
|
|
else
|
|
{
|
|
nStartPara = 0;
|
|
nEndPara = pOwner->pParaList->GetParagraphCount() - 1;
|
|
}
|
|
|
|
pOwner->UndoActionStart( OLUNDO_DEPTH );
|
|
const bool bUpdate = pOwner->pEditEngine->SetUpdateLayout( false );
|
|
|
|
for ( sal_Int32 nPara = nStartPara; nPara <= nEndPara; ++nPara )
|
|
{
|
|
Paragraph* pPara = pOwner->pParaList->GetParagraph( nPara );
|
|
DBG_ASSERT(pPara, "OutlinerView::SwitchOffBulletsNumbering(...), illegal paragraph index?");
|
|
|
|
if( pPara )
|
|
{
|
|
pOwner->SetDepth( pPara, -1 );
|
|
|
|
const SfxItemSet& rAttrs = pOwner->GetParaAttribs( nPara );
|
|
if (rAttrs.GetItemState( EE_PARA_BULLETSTATE ) == SfxItemState::SET)
|
|
{
|
|
SfxItemSet aAttrs(rAttrs);
|
|
aAttrs.ClearItem( EE_PARA_BULLETSTATE );
|
|
pOwner->SetParaAttribs( nPara, aAttrs );
|
|
}
|
|
}
|
|
}
|
|
|
|
const sal_uInt16 nParaCount = static_cast<sal_uInt16>(pOwner->pParaList->GetParagraphCount());
|
|
pOwner->ImplCheckParagraphs( nStartPara, nParaCount );
|
|
pOwner->pEditEngine->QuickMarkInvalid( ESelection( nStartPara, 0, nParaCount, 0 ) );
|
|
|
|
pOwner->pEditEngine->SetUpdateLayout( bUpdate );
|
|
pOwner->UndoActionEnd();
|
|
}
|
|
|
|
|
|
void OutlinerView::RemoveAttribsKeepLanguages( bool bRemoveParaAttribs )
|
|
{
|
|
RemoveAttribs( bRemoveParaAttribs, true /*keep language attribs*/ );
|
|
}
|
|
|
|
void OutlinerView::RemoveAttribs( bool bRemoveParaAttribs, bool bKeepLanguages )
|
|
{
|
|
bool bUpdate = pOwner->SetUpdateLayout( false );
|
|
pOwner->UndoActionStart( OLUNDO_ATTR );
|
|
if (bKeepLanguages)
|
|
pEditView->RemoveAttribsKeepLanguages( bRemoveParaAttribs );
|
|
else
|
|
pEditView->RemoveAttribs( bRemoveParaAttribs );
|
|
if ( bRemoveParaAttribs )
|
|
{
|
|
// Loop through all paragraphs and set indentation and level
|
|
ESelection aSel = pEditView->GetSelection();
|
|
aSel.Adjust();
|
|
for ( sal_Int32 nPara = aSel.nStartPara; nPara <= aSel.nEndPara; nPara++ )
|
|
{
|
|
Paragraph* pPara = pOwner->pParaList->GetParagraph( nPara );
|
|
pOwner->ImplInitDepth( nPara, pPara->GetDepth(), false );
|
|
}
|
|
}
|
|
pOwner->UndoActionEnd();
|
|
pOwner->SetUpdateLayout( bUpdate );
|
|
}
|
|
|
|
|
|
// ====================== Simple pass-through =======================
|
|
|
|
|
|
void OutlinerView::InsertText( const OUString& rNew, bool bSelect )
|
|
{
|
|
if( pOwner->bFirstParaIsEmpty )
|
|
pOwner->Insert( OUString() );
|
|
pEditView->InsertText( rNew, bSelect );
|
|
}
|
|
|
|
void OutlinerView::SetVisArea( const tools::Rectangle& rRect )
|
|
{
|
|
pEditView->SetVisArea( rRect );
|
|
}
|
|
|
|
|
|
void OutlinerView::SetSelection( const ESelection& rSel )
|
|
{
|
|
pEditView->SetSelection( rSel );
|
|
}
|
|
|
|
void OutlinerView::GetSelectionRectangles(std::vector<tools::Rectangle>& rLogicRects) const
|
|
{
|
|
pEditView->GetSelectionRectangles(rLogicRects);
|
|
}
|
|
|
|
void OutlinerView::SetReadOnly( bool bReadOnly )
|
|
{
|
|
pEditView->SetReadOnly( bReadOnly );
|
|
}
|
|
|
|
bool OutlinerView::IsReadOnly() const
|
|
{
|
|
return pEditView->IsReadOnly();
|
|
}
|
|
|
|
bool OutlinerView::HasSelection() const
|
|
{
|
|
return pEditView->HasSelection();
|
|
}
|
|
|
|
void OutlinerView::ShowCursor( bool bGotoCursor, bool bActivate )
|
|
{
|
|
pEditView->ShowCursor( bGotoCursor, /*bForceVisCursor=*/true, bActivate );
|
|
}
|
|
|
|
void OutlinerView::HideCursor(bool bDeactivate)
|
|
{
|
|
pEditView->HideCursor(bDeactivate);
|
|
}
|
|
|
|
bool OutlinerView::IsCursorVisible() const { return pEditView->IsCursorVisible(); }
|
|
|
|
void OutlinerView::SetWindow( vcl::Window* pWin )
|
|
{
|
|
pEditView->SetWindow( pWin );
|
|
}
|
|
|
|
vcl::Window* OutlinerView::GetWindow() const
|
|
{
|
|
return pEditView->GetWindow();
|
|
}
|
|
|
|
void OutlinerView::SetOutputArea( const tools::Rectangle& rRect )
|
|
{
|
|
pEditView->SetOutputArea( rRect );
|
|
}
|
|
|
|
tools::Rectangle const & OutlinerView::GetOutputArea() const
|
|
{
|
|
return pEditView->GetOutputArea();
|
|
}
|
|
|
|
OUString OutlinerView::GetSelected() const
|
|
{
|
|
return pEditView->GetSelected();
|
|
}
|
|
|
|
void OutlinerView::StartSpeller(weld::Widget* pDialogParent)
|
|
{
|
|
pEditView->StartSpeller(pDialogParent);
|
|
}
|
|
|
|
EESpellState OutlinerView::StartThesaurus(weld::Widget* pDialogParent)
|
|
{
|
|
return pEditView->StartThesaurus(pDialogParent);
|
|
}
|
|
|
|
void OutlinerView::StartTextConversion(weld::Widget* pDialogParent,
|
|
LanguageType nSrcLang, LanguageType nDestLang, const vcl::Font *pDestFont,
|
|
sal_Int32 nOptions, bool bIsInteractive, bool bMultipleDoc )
|
|
{
|
|
if (
|
|
(LANGUAGE_KOREAN == nSrcLang && LANGUAGE_KOREAN == nDestLang) ||
|
|
(LANGUAGE_CHINESE_SIMPLIFIED == nSrcLang && LANGUAGE_CHINESE_TRADITIONAL == nDestLang) ||
|
|
(LANGUAGE_CHINESE_TRADITIONAL == nSrcLang && LANGUAGE_CHINESE_SIMPLIFIED == nDestLang)
|
|
)
|
|
{
|
|
pEditView->StartTextConversion(pDialogParent, nSrcLang, nDestLang, pDestFont, nOptions, bIsInteractive, bMultipleDoc);
|
|
}
|
|
else
|
|
{
|
|
OSL_FAIL( "unexpected language" );
|
|
}
|
|
}
|
|
|
|
|
|
sal_Int32 OutlinerView::StartSearchAndReplace( const SvxSearchItem& rSearchItem )
|
|
{
|
|
return pEditView->StartSearchAndReplace( rSearchItem );
|
|
}
|
|
|
|
void OutlinerView::TransliterateText( TransliterationFlags nTransliterationMode )
|
|
{
|
|
pEditView->TransliterateText( nTransliterationMode );
|
|
}
|
|
|
|
ESelection OutlinerView::GetSelection() const
|
|
{
|
|
return pEditView->GetSelection();
|
|
}
|
|
|
|
|
|
void OutlinerView::Scroll( tools::Long nHorzScroll, tools::Long nVertScroll )
|
|
{
|
|
pEditView->Scroll( nHorzScroll, nVertScroll );
|
|
}
|
|
|
|
void OutlinerView::SetControlWord( EVControlBits nWord )
|
|
{
|
|
pEditView->SetControlWord( nWord );
|
|
}
|
|
|
|
EVControlBits OutlinerView::GetControlWord() const
|
|
{
|
|
return pEditView->GetControlWord();
|
|
}
|
|
|
|
void OutlinerView::SetAnchorMode( EEAnchorMode eMode )
|
|
{
|
|
pEditView->SetAnchorMode( eMode );
|
|
}
|
|
|
|
EEAnchorMode OutlinerView::GetAnchorMode() const
|
|
{
|
|
return pEditView->GetAnchorMode();
|
|
}
|
|
|
|
void OutlinerView::Copy()
|
|
{
|
|
pEditView->Copy();
|
|
}
|
|
|
|
void OutlinerView::InsertField( const SvxFieldItem& rFld )
|
|
{
|
|
pEditView->InsertField( rFld );
|
|
}
|
|
|
|
const SvxFieldItem* OutlinerView::GetFieldUnderMousePointer() const
|
|
{
|
|
return pEditView->GetFieldUnderMousePointer();
|
|
}
|
|
|
|
const SvxFieldItem* OutlinerView::GetFieldAtSelection(bool bAlsoCheckBeforeCursor) const
|
|
{
|
|
return pEditView->GetFieldAtSelection(bAlsoCheckBeforeCursor);
|
|
}
|
|
|
|
void OutlinerView::SelectFieldAtCursor()
|
|
{
|
|
pEditView->SelectFieldAtCursor();
|
|
}
|
|
|
|
void OutlinerView::SetInvalidateMore( sal_uInt16 nPixel )
|
|
{
|
|
pEditView->SetInvalidateMore( nPixel );
|
|
}
|
|
|
|
|
|
sal_uInt16 OutlinerView::GetInvalidateMore() const
|
|
{
|
|
return pEditView->GetInvalidateMore();
|
|
}
|
|
|
|
|
|
bool OutlinerView::IsCursorAtWrongSpelledWord()
|
|
{
|
|
return pEditView->IsCursorAtWrongSpelledWord();
|
|
}
|
|
|
|
|
|
bool OutlinerView::IsWrongSpelledWordAtPos( const Point& rPosPixel )
|
|
{
|
|
return pEditView->IsWrongSpelledWordAtPos( rPosPixel, /*bMarkIfWrong*/false );
|
|
}
|
|
|
|
void OutlinerView::ExecuteSpellPopup(const Point& rPosPixel, const Link<SpellCallbackInfo&,void>& rStartDlg)
|
|
{
|
|
pEditView->ExecuteSpellPopup(rPosPixel, rStartDlg);
|
|
}
|
|
|
|
void OutlinerView::Read( SvStream& rInput, EETextFormat eFormat, SvKeyValueIterator* pHTTPHeaderAttrs )
|
|
{
|
|
sal_Int32 nOldParaCount = pEditView->getEditEngine().GetParagraphCount();
|
|
ESelection aOldSel = pEditView->GetSelection();
|
|
aOldSel.Adjust();
|
|
|
|
pEditView->Read( rInput, eFormat, pHTTPHeaderAttrs );
|
|
|
|
tools::Long nParaDiff = pEditView->getEditEngine().GetParagraphCount() - nOldParaCount;
|
|
sal_Int32 nChangesStart = aOldSel.nStartPara;
|
|
sal_Int32 nChangesEnd = nChangesStart + nParaDiff + (aOldSel.nEndPara-aOldSel.nStartPara);
|
|
|
|
for ( sal_Int32 n = nChangesStart; n <= nChangesEnd; n++ )
|
|
{
|
|
if ( pOwner->GetOutlinerMode() == OutlinerMode::OutlineObject )
|
|
pOwner->ImplSetLevelDependentStyleSheet( n );
|
|
}
|
|
|
|
pOwner->ImpFilterIndents( nChangesStart, nChangesEnd );
|
|
}
|
|
|
|
void OutlinerView::SetBackgroundColor( const Color& rColor )
|
|
{
|
|
pEditView->SetBackgroundColor( rColor );
|
|
}
|
|
|
|
void OutlinerView::RegisterViewShell(OutlinerViewShell* pViewShell)
|
|
{
|
|
pEditView->RegisterViewShell(pViewShell);
|
|
}
|
|
|
|
Color const & OutlinerView::GetBackgroundColor() const
|
|
{
|
|
return pEditView->GetBackgroundColor();
|
|
}
|
|
|
|
SfxItemSet OutlinerView::GetAttribs()
|
|
{
|
|
return pEditView->GetAttribs();
|
|
}
|
|
|
|
SvtScriptType OutlinerView::GetSelectedScriptType() const
|
|
{
|
|
return pEditView->GetSelectedScriptType();
|
|
}
|
|
|
|
OUString OutlinerView::GetSurroundingText() const
|
|
{
|
|
return pEditView->GetSurroundingText();
|
|
}
|
|
|
|
Selection OutlinerView::GetSurroundingTextSelection() const
|
|
{
|
|
return pEditView->GetSurroundingTextSelection();
|
|
}
|
|
|
|
bool OutlinerView::DeleteSurroundingText(const Selection& rSelection)
|
|
{
|
|
return pEditView->DeleteSurroundingText(rSelection);
|
|
}
|
|
|
|
// ===== some code for thesaurus sub menu within context menu
|
|
|
|
namespace {
|
|
|
|
bool isSingleScriptType( SvtScriptType nScriptType )
|
|
{
|
|
sal_uInt8 nScriptCount = 0;
|
|
|
|
if (nScriptType & SvtScriptType::LATIN)
|
|
++nScriptCount;
|
|
if (nScriptType & SvtScriptType::ASIAN)
|
|
++nScriptCount;
|
|
if (nScriptType & SvtScriptType::COMPLEX)
|
|
++nScriptCount;
|
|
|
|
return nScriptCount == 1;
|
|
}
|
|
|
|
}
|
|
|
|
// returns: true if a word for thesaurus look-up was found at the current cursor position.
|
|
// The status string will be word + iso language string (e.g. "light#en-US")
|
|
bool GetStatusValueForThesaurusFromContext(
|
|
OUString &rStatusVal,
|
|
LanguageType &rLang,
|
|
const EditView &rEditView )
|
|
{
|
|
// get text and locale for thesaurus look up
|
|
OUString aText;
|
|
EditEngine& rEditEngine = rEditView.getEditEngine();
|
|
ESelection aTextSel( rEditView.GetSelection() );
|
|
if (!aTextSel.HasRange())
|
|
aTextSel = rEditEngine.GetWord( aTextSel, i18n::WordType::DICTIONARY_WORD );
|
|
aText = rEditEngine.GetText( aTextSel );
|
|
aTextSel.Adjust();
|
|
|
|
if (!isSingleScriptType(rEditEngine.GetScriptType(aTextSel)))
|
|
return false;
|
|
|
|
LanguageType nLang = rEditEngine.GetLanguage( aTextSel.nStartPara, aTextSel.nStartPos ).nLang;
|
|
OUString aLangText( LanguageTag::convertToBcp47( nLang ) );
|
|
|
|
// set word and locale to look up as status value
|
|
rStatusVal = aText + "#" + aLangText;
|
|
rLang = nLang;
|
|
|
|
return aText.getLength() > 0;
|
|
}
|
|
|
|
|
|
void ReplaceTextWithSynonym( EditView &rEditView, const OUString &rSynonmText )
|
|
{
|
|
// get selection to use
|
|
ESelection aCurSel( rEditView.GetSelection() );
|
|
if (!rEditView.HasSelection())
|
|
{
|
|
// select the same word that was used in GetStatusValueForThesaurusFromContext by calling GetWord.
|
|
// (In the end both functions will call ImpEditEngine::SelectWord)
|
|
rEditView.SelectCurrentWord( i18n::WordType::DICTIONARY_WORD );
|
|
aCurSel = rEditView.GetSelection();
|
|
}
|
|
|
|
// replace word ...
|
|
rEditView.InsertText( rSynonmText );
|
|
rEditView.ShowCursor( true, false );
|
|
}
|
|
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|