blob: fc3c09073f199c8eaa0c20c356eb99518ac097a7 [file] [log] [blame]
/**************************************************************
*
* 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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_chart2.hxx"
#include "SelectionHelper.hxx"
#include "ObjectIdentifier.hxx"
//for C2U
#include "macros.hxx"
#include "DiagramHelper.hxx"
#include "ChartModelHelper.hxx"
// header for class SdrObjList
#include <svx/svdpage.hxx>
#include <svx/svditer.hxx>
#include "svx/obj3d.hxx"
// header for class SdrPathObj
#include <svx/svdopath.hxx>
#include <vcl/svapp.hxx>
#include <vos/mutex.hxx>
#include <basegfx/point/b2dpoint.hxx>
#include <com/sun/star/beans/XPropertySet.hpp>
//.............................................................................
namespace chart
{
//.............................................................................
using namespace ::com::sun::star;
//using namespace ::com::sun::star::chart2;
namespace
{
rtl::OUString lcl_getObjectName( SdrObject* pObj )
{
if(pObj)
return pObj->GetName();
return rtl::OUString();
}
void impl_selectObject( SdrObject* pObjectToSelect, DrawViewWrapper& rDrawViewWrapper )
{
::vos::OGuard aSolarGuard( Application::GetSolarMutex());
if(pObjectToSelect)
{
SelectionHelper aSelectionHelper( pObjectToSelect );
SdrObject* pMarkObj = aSelectionHelper.getObjectToMark();
rDrawViewWrapper.setMarkHandleProvider(&aSelectionHelper);
rDrawViewWrapper.MarkObject(pMarkObj);
rDrawViewWrapper.setMarkHandleProvider(NULL);
}
}
}//anonymous namespace
bool Selection::hasSelection()
{
return m_aSelectedOID.isValid();
}
rtl::OUString Selection::getSelectedCID()
{
return m_aSelectedOID.getObjectCID();
}
uno::Reference< drawing::XShape > Selection::getSelectedAdditionalShape()
{
return m_aSelectedOID.getAdditionalShape();
}
ObjectIdentifier Selection::getSelectedOID() const
{
return m_aSelectedOID;
}
bool Selection::setSelection( const ::rtl::OUString& rCID )
{
if ( !rCID.equals( 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()
{
return ( m_aSelectedOID != m_aSelectedOID_beforeMouseDown );
}
void Selection::applySelection( DrawViewWrapper* pDrawViewWrapper )
{
if( pDrawViewWrapper )
{
{
::vos::OGuard aSolarGuard( Application::GetSolarMutex());
pDrawViewWrapper->UnmarkAll();
}
SdrObject* pObjectToSelect = 0;
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* pDrawViewWrapper
, bool bIsRightMouse, bool bWaitingForDoubleClick )
{
if( pDrawViewWrapper )
{
//do not toggel multiclick selection if right clicked on the selected object or waiting for double click
bool bAllowMultiClickSelectionChange = !bIsRightMouse && !bWaitingForDoubleClick;
ObjectIdentifier aLastSelectedObject( m_aSelectedOID );
::vos::OGuard aSolarGuard( Application::GetSolarMutex());
//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:
SdrObject* pNewObj = 0;
{
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
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( C2U( "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;
}
SdrObject* pLastChild = pNewObj;
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 )
{
pNewObj = pLastChild;
m_aSelectedOID = aLastChild;
}
else
m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing = aLastChild;
break;
}
}
DBG_ASSERT( pNewObj && 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() )
{
rtl::OUString aPageCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_PAGE, rtl::OUString() ) );//@todo read CID from model
if ( !m_aSelectedOID.isAutoGeneratedObject() )
{
m_aSelectedOID = ObjectIdentifier( aPageCID );
}
//check wether the diagram was hit but not selected (e.g. because it has no filling):
rtl::OUString aDiagramCID = ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM, rtl::OUString::valueOf( sal_Int32(0) ) );
rtl::OUString aWallCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM_WALL, rtl::OUString() ) );//@todo read CID from model
bool bBackGroundHit = m_aSelectedOID.getObjectCID().equals( aPageCID ) || m_aSelectedOID.getObjectCID().equals( aWallCID ) || !m_aSelectedOID.isAutoGeneratedObject();
if( bBackGroundHit )
{
//todo: if more than one diagram is available in future do chack the list of all diagrams here
SdrObject* pDiagram = pDrawViewWrapper->getNamedSdrObject( aDiagramCID );
if( pDiagram )
{
if( pDrawViewWrapper->IsObjectHit( pDiagram, rMousePos ) )
{
m_aSelectedOID = ObjectIdentifier( aDiagramCID );
pNewObj = pDiagram;
}
}
}
//check wether the legend was hit but not selected (e.g. because it has no filling):
if( bBackGroundHit || m_aSelectedOID.getObjectCID().equals( aDiagramCID ) )
{
rtl::OUString aLegendCID( ObjectIdentifier::createClassifiedIdentifierForParticle( ObjectIdentifier::createParticleForLegend(0,0) ) );//@todo read CID from model
SdrObject* pLegend = pDrawViewWrapper->getNamedSdrObject( aLegendCID );
if( pLegend )
{
if( pDrawViewWrapper->IsObjectHit( pLegend, rMousePos ) )
{
m_aSelectedOID = ObjectIdentifier( aLegendCID );
pNewObj = pLegend;
}
}
}
}
}
if ( bIsRightMouse && m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing.isValid() )
{
m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing = ObjectIdentifier();
}
}
}
bool Selection::isResizeableObjectSelected()
{
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;
}
return false;
}
bool Selection::isRotateableObjectSelected( const uno::Reference< frame::XModel >& xChartModel )
{
return SelectionHelper::isRotateableObject( m_aSelectedOID.getObjectCID(), xChartModel );
}
bool Selection::isDragableObjectSelected()
{
return m_aSelectedOID.isDragableObject();
}
bool Selection::isAdditionalShapeSelected() const
{
return m_aSelectedOID.isAdditionalShape();
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool SelectionHelper::findNamedParent( SdrObject*& pInOutObject
, rtl::OUString& rOutName
, bool bGivenObjectMayBeResult )
{
::vos::OGuard aSolarGuard( Application::GetSolarMutex());
//find the deepest named group
SdrObject* pObj = pInOutObject;
rtl::OUString aName;
if( bGivenObjectMayBeResult )
aName = lcl_getObjectName( pObj );
while( pObj && !ObjectIdentifier::isCID( aName ) )
{
SdrObjList* pObjList = pObj->GetObjList();
if( !pObjList )
return false;;
SdrObject* pOwner = pObjList->GetOwnerObj();
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 )
{
rtl::OUString aName;
if ( findNamedParent( pInOutObject, aName, bGivenObjectMayBeResult ) )
{
rOutObject = ObjectIdentifier( aName );
return true;
}
return false;
}
bool SelectionHelper::isDragableObjectHitTwice( const Point& rMPos
, const rtl::OUString& rNameOfSelectedObject
, const DrawViewWrapper& rDrawViewWrapper )
{
if( rNameOfSelectedObject.isEmpty() )
return false;
if( !ObjectIdentifier::isDragableObject(rNameOfSelectedObject) )
return false;
::vos::OGuard aSolarGuard( Application::GetSolarMutex());
SdrObject* pObj = rDrawViewWrapper.getNamedSdrObject( rNameOfSelectedObject );
if( !rDrawViewWrapper.IsObjectHit( pObj, rMPos ) )
return false;
return true;
}
::rtl::OUString SelectionHelper::getHitObjectCID(
const Point& rMPos,
DrawViewWrapper& rDrawViewWrapper,
bool bGetDiagramInsteadOf_Wall )
{
// //- solar mutex
::vos::OGuard aSolarGuard( Application::GetSolarMutex());
rtl::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(C2U("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 = ::rtl::OUString();
}
rtl::OUString aPageCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_PAGE, rtl::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.equals( aPageCID ) )
{
rtl::OUString aDiagramCID = ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM, rtl::OUString::valueOf( sal_Int32(0) ) );
//todo: if more than one diagram is available in future do chack the list of all diagrams here
SdrObject* pDiagram = rDrawViewWrapper.getNamedSdrObject( aDiagramCID );
if( pDiagram )
{
if( rDrawViewWrapper.IsObjectHit( pDiagram, rMPos ) )
{
aRet = aDiagramCID;
}
}
}
else if( bGetDiagramInsteadOf_Wall )
{
rtl::OUString aWallCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM_WALL, rtl::OUString() ) );//@todo read CID from model
if( aRet.equals( aWallCID ) )
{
rtl::OUString aDiagramCID = ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM, rtl::OUString::valueOf( sal_Int32(0) ) );
aRet = aDiagramCID;
}
}
}
return aRet;
// \\- solar mutex
}
bool SelectionHelper::isRotateableObject( const ::rtl::OUString& rCID
, const uno::Reference< frame::XModel >& xChartModel )
{
if( !ObjectIdentifier::isRotateableObject( rCID ) )
return false;
sal_Int32 nDimensionCount = DiagramHelper::getDimension( ChartModelHelper::findDiagram( xChartModel ) );
if( nDimensionCount == 3 )
return true;
return false;
}
SelectionHelper::SelectionHelper( SdrObject* pSelectedObj )
: m_pSelectedObj( pSelectedObj )
{
}
SelectionHelper::~SelectionHelper()
{
}
//virtual
bool SelectionHelper::getFrameDragSingles()
{
bool bFrameDragSingles = true;//true == green == surrounding handles
if( m_pSelectedObj && m_pSelectedObj->ISA(E3dObject) )
bFrameDragSingles = false;
return bFrameDragSingles;
}
SdrObject* SelectionHelper::getMarkHandlesObject( SdrObject* pObj )
{
if(!pObj)
return 0;
rtl::OUString aName( lcl_getObjectName( pObj ) );
if( aName.match(C2U("MarkHandles")) || aName.match(C2U("HandlesOnly")) )
return pObj;
if( !aName.isEmpty() )//dont't get the markhandles of a different object
return 0;
//search for a child with name "MarkHandles" or "HandlesOnly"
::vos::OGuard aSolarGuard( Application::GetSolarMutex());
SdrObjList* pSubList = pObj->GetSubList();
if(pSubList)
{
SdrObjListIter aIterator(*pSubList, IM_FLAT);
while (aIterator.IsMore())
{
SdrObject* pMarkHandles = SelectionHelper::getMarkHandlesObject( aIterator.Next() );
if( pMarkHandles )
return pMarkHandles;
}
}
return 0;
}
SdrObject* SelectionHelper::getObjectToMark()
{
//return the selected object itself
//or a specific other object if that exsists
SdrObject* pObj = m_pSelectedObj;
m_pMarkObj = pObj;
//search for a child with name "MarkHandles" or "HandlesOnly"
if(pObj)
{
::vos::OGuard aSolarGuard( Application::GetSolarMutex());
SdrObjList* pSubList = pObj->GetSubList();
if(pSubList)
{
SdrObjListIter aIterator(*pSubList, IM_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 wether the object or one of its children is a 3D object
//if so, return the accessory 3DScene
E3dObject* pRotateable = 0;
if(pObj)
{
pRotateable = dynamic_cast<E3dObject*>(pObj);
if( !pRotateable )
{
::vos::OGuard aSolarGuard( Application::GetSolarMutex());
SdrObjList* pSubList = pObj->GetSubList();
if(pSubList)
{
SdrObjListIter aIterator(*pSubList, IM_DEEPWITHGROUPS);
while( aIterator.IsMore() && !pRotateable )
{
SdrObject* pSubObj = aIterator.Next();
pRotateable = dynamic_cast<E3dObject*>(pSubObj);
}
}
}
}
E3dScene* pScene = 0;
if(pRotateable)
{
::vos::OGuard aSolarGuard( Application::GetSolarMutex());
pScene = pRotateable->GetScene();
}
return pScene;
}
//virtual
bool SelectionHelper::getMarkHandles( SdrHdlList& rHdlList )
{
::vos::OGuard aSolarGuard( Application::GetSolarMutex());
//@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 therefrom ...
//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 exsists
//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( m_pMarkObj->ISA(SdrPathObj) )
{
//if th object is a polygon
//from each point a handle is generated
const ::basegfx::B2DPolyPolygon& rPolyPolygon = ((SdrPathObj*)m_pMarkObj)->GetPathPoly();
for( sal_uInt32 nN = 0L; nN < rPolyPolygon.count(); nN++)
{
const ::basegfx::B2DPolygon aPolygon(rPolyPolygon.getB2DPolygon(nN));
for( sal_uInt32 nM = 0L; nM < aPolygon.count(); nM++)
{
const ::basegfx::B2DPoint aPoint(aPolygon.getB2DPoint(nM));
SdrHdl* pHdl = new SdrHdl(Point(basegfx::fround(aPoint.getX()), basegfx::fround(aPoint.getY())), HDL_POLY);
rHdlList.AddHdl(pHdl);
}
}
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;
rtl::OUString aName( lcl_getObjectName( pObj ) );
ObjectType eObjectType( ObjectIdentifier::getObjectType( aName ) );
if( OBJECTTYPE_DATA_POINT == eObjectType
|| OBJECTTYPE_DATA_LABEL == eObjectType
|| OBJECTTYPE_LEGEND_ENTRY == eObjectType
|| OBJECTTYPE_AXIS_UNITLABEL == eObjectType )
{
return false;
}
SdrObjListIter aIterator(*pSubList, IM_FLAT);
while (aIterator.IsMore())
{
SdrObject* pSubObj = aIterator.Next();
if( OBJECTTYPE_DATA_SERIES == eObjectType )
{
rtl::OUString aSubName( lcl_getObjectName( pSubObj ) );
ObjectType eSubObjectType( ObjectIdentifier::getObjectType( aSubName ) );
if( eSubObjectType!=OBJECTTYPE_DATA_POINT )
return false;
}
Point aPos = pSubObj->GetCurrentBoundRect().Center();
SdrHdl* pHdl = new SdrHdl(aPos,HDL_POLY);
rHdlList.AddHdl(pHdl);
}
return true;
}
//.............................................................................
} //namespace chart
//.............................................................................