Files
loongoffice/chart2/source/controller/main/SelectionHelper.cxx
Stephan Bergmann b512ce255f Make upcasting css::uno::Reference ctor require complete types
The main reason for the "home-grown" UpCast introduced with
904b3d1fceee5827076758ed2a81f80cb73493ca "Up-cast conversion constructor for
css::uno::Reference" in 2013 was probably that we could not yet rely on C++11
std::is_base_of back then.  A (welcome) side effect was that the derived class
could be incomplete.

However, specializations of UpCast relying on whether or not T2 is incomplete
are obviously an ODR violation if the type is incomplete in some TUs and
complete (and derived from T1) in others.  And even if UpCast had internal
linkage, it would still be brittle that its behavior depends on the completeness
of T2 at the point of the template's instantiation, and not necessarily at the
point of use.

That means we should better base that ctor on std::is_base_of (which we can do
now since 39a1edd6fec902ef378acce8af42c4d7fba280d0 "Make css::uno::Reference
upcast ctor LIBO_INTERNAL_ONLY"), which causes a compilation error at least on
Clang and GCC if the completeness requirements are not met.  This change fixes
all the cases where types need to be complete now, plus any resulting
loplugin:referencecasting warnings ("the source reference is already a subtype
of the destination reference").

Change-Id: Ieb9e3552e90adbf2c5a5af933dcb872e20661a2f
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/92950
Tested-by: Jenkins
Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2020-04-27 07:19:30 +02:00

651 lines
21 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 <SelectionHelper.hxx>
#include <ObjectIdentifier.hxx>
#include <DiagramHelper.hxx>
#include <ChartModelHelper.hxx>
#include <com/sun/star/frame/XModel.hpp>
#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>
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, OUString() ) );//@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, OUString() ) );//@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 uno::Reference< frame::XModel >& 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, OUString() ) );//@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, OUString() ) );//@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( const OUString& rCID
, const uno::Reference< frame::XModel >& xChartModel )
{
if( !ObjectIdentifier::isRotateableObject( rCID ) )
return false;
sal_Int32 nDimensionCount = DiagramHelper::getDimension( ChartModelHelper::findDiagram( xChartModel ) );
return nDimensionCount == 3;
}
SelectionHelper::SelectionHelper( SdrObject* pSelectedObj )
: m_pSelectedObj( pSelectedObj ), m_pMarkObj(nullptr)
{
}
SelectionHelper::~SelectionHelper()
{
}
bool SelectionHelper::getFrameDragSingles()
{
//true == green == surrounding handles
return dynamic_cast<const E3dObject*>( 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 = dynamic_cast<E3dObject*>(pObj);
if( !pRotateable )
{
SolarMutexGuard aSolarGuard;
SdrObjList* pSubList = pObj->GetSubList();
if(pSubList)
{
SdrObjListIter aIterator(pSubList, SdrIterMode::DeepWithGroups);
while( aIterator.IsMore() && !pRotateable )
{
SdrObject* pSubObj = aIterator.Next();
pRotateable = dynamic_cast<E3dObject*>(pSubObj);
}
}
}
}
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( dynamic_cast<const SdrPathObj*>( m_pMarkObj) != nullptr )
{
//if th object is a polygon
//from each point a handle is generated
const ::basegfx::B2DPolyPolygon& rPolyPolygon = static_cast<SdrPathObj*>(m_pMarkObj)->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: */