diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
commit | 267c6f2ac71f92999e969232431ba04678e7437e (patch) | |
tree | 358c9467650e1d0a1d7227a21dac2e3d08b622b2 /chart2/source/controller/main/SelectionHelper.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip |
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'chart2/source/controller/main/SelectionHelper.cxx')
-rw-r--r-- | chart2/source/controller/main/SelectionHelper.cxx | 651 |
1 files changed, 651 insertions, 0 deletions
diff --git a/chart2/source/controller/main/SelectionHelper.cxx b/chart2/source/controller/main/SelectionHelper.cxx new file mode 100644 index 0000000000..11fc5d9fae --- /dev/null +++ b/chart2/source/controller/main/SelectionHelper.cxx @@ -0,0 +1,651 @@ +/* -*- 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 <SelectionHelper.hxx> +#include <ObjectIdentifier.hxx> +#include <DiagramHelper.hxx> +#include <Diagram.hxx> +#include <ChartModelHelper.hxx> +#include <ChartModel.hxx> + +#include <svx/svdpage.hxx> +#include <svx/svditer.hxx> +#include <svx/obj3d.hxx> +#include <svx/svdopath.hxx> +#include <vcl/svapp.hxx> +#include <basegfx/point/b2dpoint.hxx> +#include <osl/diagnose.h> + +namespace chart +{ +using namespace ::com::sun::star; + +namespace +{ + +OUString lcl_getObjectName( SdrObject const * pObj ) +{ + if(pObj) + return pObj->GetName(); + return OUString(); +} + +void impl_selectObject( SdrObject* pObjectToSelect, DrawViewWrapper& rDrawViewWrapper ) +{ + SolarMutexGuard aSolarGuard; + + if(pObjectToSelect) + { + SelectionHelper aSelectionHelper( pObjectToSelect ); + SdrObject* pMarkObj = aSelectionHelper.getObjectToMark(); + rDrawViewWrapper.setMarkHandleProvider(&aSelectionHelper); + rDrawViewWrapper.MarkObject(pMarkObj); + rDrawViewWrapper.setMarkHandleProvider(nullptr); + } +} + +}//anonymous namespace + +bool Selection::hasSelection() const +{ + return m_aSelectedOID.isValid(); +} + +OUString const & Selection::getSelectedCID() const +{ + return m_aSelectedOID.getObjectCID(); +} + +uno::Reference< drawing::XShape > const & Selection::getSelectedAdditionalShape() const +{ + return m_aSelectedOID.getAdditionalShape(); +} + +bool Selection::setSelection( const OUString& rCID ) +{ + if ( rCID != m_aSelectedOID.getObjectCID() ) + { + m_aSelectedOID = ObjectIdentifier( rCID ); + return true; + } + return false; +} + +bool Selection::setSelection( const uno::Reference< drawing::XShape >& xShape ) +{ + if ( !( xShape == m_aSelectedOID.getAdditionalShape() ) ) + { + clearSelection(); + m_aSelectedOID = ObjectIdentifier( xShape ); + return true; + } + return false; +} + +void Selection::clearSelection() +{ + m_aSelectedOID = ObjectIdentifier(); + m_aSelectedOID_beforeMouseDown = ObjectIdentifier(); + m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing = ObjectIdentifier(); +} + +bool Selection::maybeSwitchSelectionAfterSingleClickWasEnsured() +{ + if ( m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing.isValid() + && m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing != m_aSelectedOID ) + { + m_aSelectedOID = m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing; + m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing = ObjectIdentifier(); + return true; + } + return false; +} + +void Selection::resetPossibleSelectionAfterSingleClickWasEnsured() +{ + if ( m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing.isValid() ) + { + m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing = ObjectIdentifier(); + } +} + +void Selection::remindSelectionBeforeMouseDown() +{ + m_aSelectedOID_beforeMouseDown = m_aSelectedOID; +} + +bool Selection::isSelectionDifferentFromBeforeMouseDown() const +{ + return ( m_aSelectedOID != m_aSelectedOID_beforeMouseDown ); +} + +void Selection::applySelection( DrawViewWrapper* pDrawViewWrapper ) +{ + if( !pDrawViewWrapper ) + return; + + { + SolarMutexGuard aSolarGuard; + pDrawViewWrapper->UnmarkAll(); + } + SdrObject* pObjectToSelect = nullptr; + if ( m_aSelectedOID.isAutoGeneratedObject() ) + { + pObjectToSelect = pDrawViewWrapper->getNamedSdrObject( m_aSelectedOID.getObjectCID() ); + } + else if( m_aSelectedOID.isAdditionalShape() ) + { + pObjectToSelect = DrawViewWrapper::getSdrObject( m_aSelectedOID.getAdditionalShape() ); + } + + impl_selectObject( pObjectToSelect, *pDrawViewWrapper ); +} + +void Selection::adaptSelectionToNewPos( const Point& rMousePos, DrawViewWrapper const * pDrawViewWrapper + , bool bIsRightMouse, bool bWaitingForDoubleClick ) +{ + if( !pDrawViewWrapper ) + return; + + //do not toggle multiclick selection if right clicked on the selected object or waiting for double click + bool bAllowMultiClickSelectionChange = !bIsRightMouse && !bWaitingForDoubleClick; + + ObjectIdentifier aLastSelectedObject( m_aSelectedOID ); + + SolarMutexGuard aSolarGuard; + + //bAllowMultiClickSelectionChange==true -> a second click on the same object can lead to a changed selection (e.g. series -> single data point) + + //get object to select: + { + m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing = ObjectIdentifier(); + + //the search for the object to select starts with the hit object deepest in the grouping hierarchy (a leaf in the tree) + //further we travel along the grouping hierarchy from child to parent + SdrObject* pNewObj = pDrawViewWrapper->getHitObject(rMousePos); + m_aSelectedOID = ObjectIdentifier( lcl_getObjectName( pNewObj ) );//name of pNewObj + + //ignore handle only objects for hit test + while( pNewObj && m_aSelectedOID.getObjectCID().match( "HandlesOnly" ) ) + { + pNewObj->SetMarkProtect(true); + pNewObj = pDrawViewWrapper->getHitObject(rMousePos); + m_aSelectedOID = ObjectIdentifier( lcl_getObjectName( pNewObj ) ); + } + + //accept only named objects while searching for the object to select + //this call may change m_aSelectedOID + if ( SelectionHelper::findNamedParent( pNewObj, m_aSelectedOID, true ) ) + { + //if the so far found object is a multi click object further steps are necessary + while( ObjectIdentifier::isMultiClickObject( m_aSelectedOID.getObjectCID() ) ) + { + bool bSameObjectAsLastSelected = ( aLastSelectedObject == m_aSelectedOID ); + if( bSameObjectAsLastSelected ) + { + //if the same child is clicked again don't go up further + break; + } + if ( ObjectIdentifier::areSiblings( aLastSelectedObject.getObjectCID(), m_aSelectedOID.getObjectCID() ) ) + { + //if a sibling of the last selected object is clicked don't go up further + break; + } + ObjectIdentifier aLastChild = m_aSelectedOID; + if ( !SelectionHelper::findNamedParent( pNewObj, m_aSelectedOID, false ) ) + { + //take the one found so far + break; + } + //if the last selected object is found don't go up further + //but take the last child if selection change is allowed + if ( aLastSelectedObject == m_aSelectedOID ) + { + if( bAllowMultiClickSelectionChange ) + { + m_aSelectedOID = aLastChild; + } + else + m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing = aLastChild; + break; + } + } + + OSL_ENSURE(m_aSelectedOID.isValid(), "somehow lost selected object"); + } + else + { + //maybe an additional shape was hit + if ( pNewObj ) + { + m_aSelectedOID = ObjectIdentifier( uno::Reference< drawing::XShape >( pNewObj->getUnoShape(), uno::UNO_QUERY ) ); + } + else + { + m_aSelectedOID = ObjectIdentifier(); + } + } + + if ( !m_aSelectedOID.isAdditionalShape() ) + { + OUString aPageCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_PAGE, u"" ) );//@todo read CID from model + + if ( !m_aSelectedOID.isAutoGeneratedObject() ) + { + m_aSelectedOID = ObjectIdentifier( aPageCID ); + } + + //check whether the diagram was hit but not selected (e.g. because it has no filling): + OUString aDiagramCID = ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM, OUString::number( 0 ) ); + OUString aWallCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM_WALL, u"" ) );//@todo read CID from model + bool bBackGroundHit = m_aSelectedOID.getObjectCID() == aPageCID || m_aSelectedOID.getObjectCID() == aWallCID || !m_aSelectedOID.isAutoGeneratedObject(); + if( bBackGroundHit ) + { + //todo: if more than one diagram is available in future do check the list of all diagrams here + SdrObject* pDiagram = pDrawViewWrapper->getNamedSdrObject( aDiagramCID ); + if( pDiagram ) + { + if( DrawViewWrapper::IsObjectHit( pDiagram, rMousePos ) ) + { + m_aSelectedOID = ObjectIdentifier( aDiagramCID ); + } + } + } + //check whether the legend was hit but not selected (e.g. because it has no filling): + if( bBackGroundHit || m_aSelectedOID.getObjectCID() == aDiagramCID ) + { + OUString aLegendCID( ObjectIdentifier::createClassifiedIdentifierForParticle( ObjectIdentifier::createParticleForLegend(nullptr) ) );//@todo read CID from model + SdrObject* pLegend = pDrawViewWrapper->getNamedSdrObject( aLegendCID ); + if( pLegend ) + { + if( DrawViewWrapper::IsObjectHit( pLegend, rMousePos ) ) + { + m_aSelectedOID = ObjectIdentifier( aLegendCID ); + } + } + } + } + } + + if ( bIsRightMouse && m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing.isValid() ) + { + m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing = ObjectIdentifier(); + } +} + +bool Selection::isResizeableObjectSelected() const +{ + ObjectType eObjectType = m_aSelectedOID.getObjectType(); + switch( eObjectType ) + { + case OBJECTTYPE_DIAGRAM: + case OBJECTTYPE_DIAGRAM_WALL: + case OBJECTTYPE_SHAPE: + case OBJECTTYPE_LEGEND: + return true; + default: + return false; + } +} + +bool Selection::isRotateableObjectSelected( const rtl::Reference<::chart::ChartModel>& xChartModel ) const +{ + return SelectionHelper::isRotateableObject( m_aSelectedOID.getObjectCID(), xChartModel ); +} + +bool Selection::isDragableObjectSelected() const +{ + return m_aSelectedOID.isDragableObject(); +} + +bool Selection::isAdditionalShapeSelected() const +{ + return m_aSelectedOID.isAdditionalShape(); +} + +bool SelectionHelper::findNamedParent( SdrObject*& pInOutObject + , OUString& rOutName + , bool bGivenObjectMayBeResult ) +{ + SolarMutexGuard aSolarGuard; + //find the deepest named group + SdrObject* pObj = pInOutObject; + OUString aName; + if( bGivenObjectMayBeResult ) + aName = lcl_getObjectName( pObj ); + + while( pObj && !ObjectIdentifier::isCID( aName ) ) + { + SdrObjList* pObjList = pObj->getParentSdrObjListFromSdrObject(); + if( !pObjList ) + return false; + SdrObject* pOwner = pObjList->getSdrObjectFromSdrObjList(); + if( !pOwner ) + return false; + pObj = pOwner; + aName = lcl_getObjectName( pObj ); + } + + if(!pObj) + return false; + if(aName.isEmpty()) + return false; + + pInOutObject = pObj; + rOutName = aName; + return true; +} + +bool SelectionHelper::findNamedParent( SdrObject*& pInOutObject + , ObjectIdentifier& rOutObject + , bool bGivenObjectMayBeResult ) +{ + OUString aName; + if ( findNamedParent( pInOutObject, aName, bGivenObjectMayBeResult ) ) + { + rOutObject = ObjectIdentifier( aName ); + return true; + } + return false; +} + +bool SelectionHelper::isDragableObjectHitTwice( const Point& rMPos + , const OUString& rNameOfSelectedObject + , const DrawViewWrapper& rDrawViewWrapper ) +{ + if(rNameOfSelectedObject.isEmpty()) + return false; + if( !ObjectIdentifier::isDragableObject(rNameOfSelectedObject) ) + return false; + SolarMutexGuard aSolarGuard; + SdrObject* pObj = rDrawViewWrapper.getNamedSdrObject( rNameOfSelectedObject ); + return DrawViewWrapper::IsObjectHit( pObj, rMPos ); +} + +OUString SelectionHelper::getHitObjectCID( + const Point& rMPos, + DrawViewWrapper const & rDrawViewWrapper, + bool bGetDiagramInsteadOf_Wall ) +{ + SolarMutexGuard aSolarGuard; + OUString aRet; + + SdrObject* pNewObj = rDrawViewWrapper.getHitObject(rMPos); + aRet = lcl_getObjectName( pNewObj );//name of pNewObj + + //ignore handle only objects for hit test + while( pNewObj && aRet.match("HandlesOnly") ) + { + pNewObj->SetMarkProtect(true); + pNewObj = rDrawViewWrapper.getHitObject(rMPos); + aRet = lcl_getObjectName( pNewObj ); + } + + //accept only named objects while searching for the object to select + if( !findNamedParent( pNewObj, aRet, true ) ) + { + aRet.clear(); + } + + OUString aPageCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_PAGE, u"" ) );//@todo read CID from model + //get page when nothing was hit + if( aRet.isEmpty() && !pNewObj ) + { + aRet = aPageCID; + } + + //get diagram instead wall or page if hit inside diagram + if( !aRet.isEmpty() ) + { + if( aRet == aPageCID ) + { + OUString aDiagramCID = ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM, OUString::number( 0 ) ); + //todo: if more than one diagram is available in future do check the list of all diagrams here + SdrObject* pDiagram = rDrawViewWrapper.getNamedSdrObject( aDiagramCID ); + if( pDiagram ) + { + if( DrawViewWrapper::IsObjectHit( pDiagram, rMPos ) ) + { + aRet = aDiagramCID; + } + } + } + else if( bGetDiagramInsteadOf_Wall ) + { + OUString aWallCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM_WALL, u"" ) );//@todo read CID from model + + if( aRet == aWallCID ) + { + OUString aDiagramCID = ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM, OUString::number( 0 ) ); + aRet = aDiagramCID; + } + } + } + + return aRet; + // \\- solar mutex +} + +bool SelectionHelper::isRotateableObject( std::u16string_view rCID + , const rtl::Reference<::chart::ChartModel>& xChartModel ) +{ + if( !ObjectIdentifier::isRotateableObject( rCID ) ) + return false; + + sal_Int32 nDimensionCount = xChartModel->getFirstChartDiagram()->getDimension(); + + return nDimensionCount == 3; +} + +SelectionHelper::SelectionHelper( SdrObject* pSelectedObj ) + : m_pSelectedObj( pSelectedObj ), m_pMarkObj(nullptr) +{ + +} +SelectionHelper::~SelectionHelper() +{ +} + +bool SelectionHelper::getFrameDragSingles() +{ + //true == green == surrounding handles + return DynCastE3dObject( m_pSelectedObj) == nullptr; +} + +SdrObject* SelectionHelper::getMarkHandlesObject( SdrObject* pObj ) +{ + if(!pObj) + return nullptr; + OUString aName( lcl_getObjectName( pObj ) ); + if( aName.match("MarkHandles") || aName.match("HandlesOnly") ) + return pObj; + if( !aName.isEmpty() )//don't get the markhandles of a different object + return nullptr; + + //search for a child with name "MarkHandles" or "HandlesOnly" + SolarMutexGuard aSolarGuard; + SdrObjList* pSubList = pObj->GetSubList(); + if(pSubList) + { + SdrObjListIter aIterator(pSubList, SdrIterMode::Flat); + while (aIterator.IsMore()) + { + SdrObject* pMarkHandles = SelectionHelper::getMarkHandlesObject( aIterator.Next() ); + if( pMarkHandles ) + return pMarkHandles; + } + } + return nullptr; +} + +SdrObject* SelectionHelper::getObjectToMark() +{ + //return the selected object itself + //or a specific other object if that exists + SdrObject* pObj = m_pSelectedObj; + m_pMarkObj = pObj; + + //search for a child with name "MarkHandles" or "HandlesOnly" + if(pObj) + { + SolarMutexGuard aSolarGuard; + SdrObjList* pSubList = pObj->GetSubList(); + if(pSubList) + { + SdrObjListIter aIterator(pSubList, SdrIterMode::Flat); + while (aIterator.IsMore()) + { + SdrObject* pMarkHandles = SelectionHelper::getMarkHandlesObject( aIterator.Next() ); + if( pMarkHandles ) + { + m_pMarkObj = pMarkHandles; + break; + } + } + } + } + return m_pMarkObj; +} + +E3dScene* SelectionHelper::getSceneToRotate( SdrObject* pObj ) +{ + //search whether the object or one of its children is a 3D object + //if so, return the accessory 3DScene + + E3dObject* pRotateable = nullptr; + + if(pObj) + { + pRotateable = DynCastE3dObject(pObj); + if( !pRotateable ) + { + SolarMutexGuard aSolarGuard; + SdrObjList* pSubList = pObj->GetSubList(); + if(pSubList) + { + SdrObjListIter aIterator(pSubList, SdrIterMode::DeepWithGroups); + while( aIterator.IsMore() && !pRotateable ) + { + pRotateable = DynCastE3dObject(aIterator.Next()); + } + } + } + } + + E3dScene* pScene(nullptr); + + if(pRotateable) + { + SolarMutexGuard aSolarGuard; + pScene = pRotateable->getRootE3dSceneFromE3dObject(); + } + + return pScene; +} + +bool SelectionHelper::getMarkHandles( SdrHdlList& rHdlList ) +{ + SolarMutexGuard aSolarGuard; + + //@todo -> more flexible handle creation + //2 scenarios possible: + //1. add an additional invisible shape as a child to the selected object + //this child needs to be named somehow and handles need to be generated there from... + //or 2. offer a central service per view where renderer and so can register for handle creation for a special shape + //.. or 3. feature from drawinglayer to create handles for each shape... (bad performance... ?) ? + + //scenario 1 is now used: + //if a child with name MarkHandles exists + //this child is marked instead of the logical selected object + +/* + //if a special mark object was found + //that object should be used for marking only + if( m_pMarkObj != m_pSelectedObj) + return false; +*/ + //if a special mark object was found + //that object should be used to create handles from + if( m_pMarkObj && m_pMarkObj != m_pSelectedObj) + { + rHdlList.Clear(); + if( auto pPathObj = dynamic_cast<const SdrPathObj*>( m_pMarkObj) ) + { + //if th object is a polygon + //from each point a handle is generated + const ::basegfx::B2DPolyPolygon& rPolyPolygon = pPathObj->GetPathPoly(); + for( sal_uInt32 nN = 0; nN < rPolyPolygon.count(); nN++) + { + const ::basegfx::B2DPolygon& aPolygon(rPolyPolygon.getB2DPolygon(nN)); + for( sal_uInt32 nM = 0; nM < aPolygon.count(); nM++) + { + const ::basegfx::B2DPoint aPoint(aPolygon.getB2DPoint(nM)); + rHdlList.AddHdl(std::make_unique<SdrHdl>(Point(basegfx::fround(aPoint.getX()), basegfx::fround(aPoint.getY())), SdrHdlKind::Poly)); + } + } + return true; + } + else + return false; //use the special MarkObject for marking + } + + //@todo: + //add and document good marking defaults ... + + rHdlList.Clear(); + + SdrObject* pObj = m_pSelectedObj; + if(!pObj) + return false; + SdrObjList* pSubList = pObj->GetSubList(); + if( !pSubList )//no group object !pObj->IsGroupObject() + return false; + + OUString aName( lcl_getObjectName( pObj ) ); + ObjectType eObjectType( ObjectIdentifier::getObjectType( aName ) ); + if( eObjectType == OBJECTTYPE_DATA_POINT + || eObjectType == OBJECTTYPE_DATA_LABEL + || eObjectType == OBJECTTYPE_LEGEND_ENTRY + || eObjectType == OBJECTTYPE_AXIS_UNITLABEL ) + { + return false; + } + + SdrObjListIter aIterator(pSubList, SdrIterMode::Flat); + + while (aIterator.IsMore()) + { + SdrObject* pSubObj = aIterator.Next(); + if( eObjectType == OBJECTTYPE_DATA_SERIES ) + { + OUString aSubName( lcl_getObjectName( pSubObj ) ); + ObjectType eSubObjectType( ObjectIdentifier::getObjectType( aSubName ) ); + if( eSubObjectType!=OBJECTTYPE_DATA_POINT ) + return false; + } + + Point aPos = pSubObj->GetCurrentBoundRect().Center(); + rHdlList.AddHdl(std::make_unique<SdrHdl>(aPos,SdrHdlKind::Poly)); + } + return true; +} + +} //namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |