diff options
Diffstat (limited to 'chart2/source/controller/main/SelectionHelper.cxx')
-rw-r--r-- | chart2/source/controller/main/SelectionHelper.cxx | 685 |
1 files changed, 685 insertions, 0 deletions
diff --git a/chart2/source/controller/main/SelectionHelper.cxx b/chart2/source/controller/main/SelectionHelper.cxx new file mode 100644 index 000000000000..567a36473a5b --- /dev/null +++ b/chart2/source/controller/main/SelectionHelper.cxx @@ -0,0 +1,685 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 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 aWallCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM_WALL, rtl::OUString() ) );//@todo read CID from model + if ( m_aSelectedOID.getObjectCID().equals( aPageCID ) || m_aSelectedOID.getObjectCID().equals( aWallCID ) || !m_aSelectedOID.isAutoGeneratedObject() ) + { + 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 = pDrawViewWrapper->getNamedSdrObject( aDiagramCID ); + if( pDiagram ) + { + if( pDrawViewWrapper->IsObjectHit( pDiagram, rMousePos ) ) + { + m_aSelectedOID = ObjectIdentifier( aDiagramCID ); + pNewObj = pDiagram; + } + } + } + } + } + + 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: + 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(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +//static +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.getLength()) + return false; + + pInOutObject = pObj; + rOutName = aName; + return true; +} + +//static +bool SelectionHelper::findNamedParent( SdrObject*& pInOutObject + , ObjectIdentifier& rOutObject + , bool bGivenObjectMayBeResult ) +{ + rtl::OUString aName; + if ( findNamedParent( pInOutObject, aName, bGivenObjectMayBeResult ) ) + { + rOutObject = ObjectIdentifier( aName ); + return true; + } + return false; +} + +//static +bool SelectionHelper::isDragableObjectHitTwice( const Point& rMPos + , const rtl::OUString& rNameOfSelectedObject + , const DrawViewWrapper& rDrawViewWrapper ) +{ + if(!rNameOfSelectedObject.getLength()) + 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; +} + +// static +::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.getLength() == 0 && !pNewObj ) + { + aRet = aPageCID; + } + + //get diagram instead wall or page if hit inside diagram + if( aRet.getLength() != 0 ) + { + 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 +} + +//static +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; +} + +//static +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.getLength() )//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; +} + +//static +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 +//............................................................................. |