1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "SelectionHelper.hxx"
21 #include "ObjectIdentifier.hxx"
23 #include "DiagramHelper.hxx"
24 #include "ChartModelHelper.hxx"
26 #include <svx/svdpage.hxx>
27 #include <svx/svditer.hxx>
28 #include "svx/obj3d.hxx"
29 #include <svx/svdopath.hxx>
30 #include <vcl/svapp.hxx>
31 #include <osl/mutex.hxx>
32 #include <basegfx/point/b2dpoint.hxx>
33 #include <com/sun/star/beans/XPropertySet.hpp>
37 using namespace ::com::sun::star
;
42 OUString
lcl_getObjectName( SdrObject
* pObj
)
45 return pObj
->GetName();
49 void impl_selectObject( SdrObject
* pObjectToSelect
, DrawViewWrapper
& rDrawViewWrapper
)
51 SolarMutexGuard aSolarGuard
;
55 SelectionHelper
aSelectionHelper( pObjectToSelect
);
56 SdrObject
* pMarkObj
= aSelectionHelper
.getObjectToMark();
57 rDrawViewWrapper
.setMarkHandleProvider(&aSelectionHelper
);
58 rDrawViewWrapper
.MarkObject(pMarkObj
);
59 rDrawViewWrapper
.setMarkHandleProvider(NULL
);
63 }//anonymous namespace
65 bool Selection::hasSelection()
67 return m_aSelectedOID
.isValid();
70 OUString
Selection::getSelectedCID()
72 return m_aSelectedOID
.getObjectCID();
75 uno::Reference
< drawing::XShape
> Selection::getSelectedAdditionalShape()
77 return m_aSelectedOID
.getAdditionalShape();
80 bool Selection::setSelection( const OUString
& rCID
)
82 if ( !rCID
.equals( m_aSelectedOID
.getObjectCID() ) )
84 m_aSelectedOID
= ObjectIdentifier( rCID
);
90 bool Selection::setSelection( const uno::Reference
< drawing::XShape
>& xShape
)
92 if ( !( xShape
== m_aSelectedOID
.getAdditionalShape() ) )
95 m_aSelectedOID
= ObjectIdentifier( xShape
);
101 void Selection::clearSelection()
103 m_aSelectedOID
= ObjectIdentifier();
104 m_aSelectedOID_beforeMouseDown
= ObjectIdentifier();
105 m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
= ObjectIdentifier();
108 bool Selection::maybeSwitchSelectionAfterSingleClickWasEnsured()
110 if ( m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
.isValid()
111 && m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
!= m_aSelectedOID
)
113 m_aSelectedOID
= m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
;
114 m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
= ObjectIdentifier();
120 void Selection::resetPossibleSelectionAfterSingleClickWasEnsured()
122 if ( m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
.isValid() )
124 m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
= ObjectIdentifier();
128 void Selection::remindSelectionBeforeMouseDown()
130 m_aSelectedOID_beforeMouseDown
= m_aSelectedOID
;
133 bool Selection::isSelectionDifferentFromBeforeMouseDown() const
135 return ( m_aSelectedOID
!= m_aSelectedOID_beforeMouseDown
);
138 void Selection::applySelection( DrawViewWrapper
* pDrawViewWrapper
)
140 if( pDrawViewWrapper
)
143 SolarMutexGuard aSolarGuard
;
144 pDrawViewWrapper
->UnmarkAll();
146 SdrObject
* pObjectToSelect
= 0;
147 if ( m_aSelectedOID
.isAutoGeneratedObject() )
149 pObjectToSelect
= pDrawViewWrapper
->getNamedSdrObject( m_aSelectedOID
.getObjectCID() );
151 else if( m_aSelectedOID
.isAdditionalShape() )
153 pObjectToSelect
= DrawViewWrapper::getSdrObject( m_aSelectedOID
.getAdditionalShape() );
156 impl_selectObject( pObjectToSelect
, *pDrawViewWrapper
);
160 void Selection::adaptSelectionToNewPos( const Point
& rMousePos
, DrawViewWrapper
* pDrawViewWrapper
161 , bool bIsRightMouse
, bool bWaitingForDoubleClick
)
163 if( pDrawViewWrapper
)
165 //do not toggel multiclick selection if right clicked on the selected object or waiting for double click
166 bool bAllowMultiClickSelectionChange
= !bIsRightMouse
&& !bWaitingForDoubleClick
;
168 ObjectIdentifier
aLastSelectedObject( m_aSelectedOID
);
170 SolarMutexGuard aSolarGuard
;
172 //bAllowMultiClickSelectionChange==true -> a second click on the same object can lead to a changed selection (e.g. series -> single data point)
174 //get object to select:
176 m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
= ObjectIdentifier();
178 //the search for the object to select starts with the hit object deepest in the grouping hierarchy (a leaf in the tree)
179 //further we travel along the grouping hierarchy from child to parent
180 SdrObject
* pNewObj
= pDrawViewWrapper
->getHitObject(rMousePos
);
181 m_aSelectedOID
= ObjectIdentifier( lcl_getObjectName( pNewObj
) );//name of pNewObj
183 //ignore handle only objects for hit test
184 while( pNewObj
&& m_aSelectedOID
.getObjectCID().match( "HandlesOnly" ) )
186 pNewObj
->SetMarkProtect(true);
187 pNewObj
= pDrawViewWrapper
->getHitObject(rMousePos
);
188 m_aSelectedOID
= ObjectIdentifier( lcl_getObjectName( pNewObj
) );
191 //accept only named objects while searching for the object to select
192 //this call may change m_aSelectedOID
193 if ( SelectionHelper::findNamedParent( pNewObj
, m_aSelectedOID
, true ) )
195 //if the so far found object is a multi click object further steps are necessary
196 while( ObjectIdentifier::isMultiClickObject( m_aSelectedOID
.getObjectCID() ) )
198 bool bSameObjectAsLastSelected
= ( aLastSelectedObject
== m_aSelectedOID
);
199 if( bSameObjectAsLastSelected
)
201 //if the same child is clicked again don't go up further
204 if ( ObjectIdentifier::areSiblings( aLastSelectedObject
.getObjectCID(), m_aSelectedOID
.getObjectCID() ) )
206 //if a sibling of the last selected object is clicked don't go up further
209 ObjectIdentifier aLastChild
= m_aSelectedOID
;
210 if ( !SelectionHelper::findNamedParent( pNewObj
, m_aSelectedOID
, false ) )
212 //take the one found so far
215 //if the last selected object is found don't go up further
216 //but take the last child if selection change is allowed
217 if ( aLastSelectedObject
== m_aSelectedOID
)
219 if( bAllowMultiClickSelectionChange
)
221 m_aSelectedOID
= aLastChild
;
224 m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
= aLastChild
;
229 OSL_ENSURE(m_aSelectedOID
.isValid(), "somehow lost selected object");
233 //maybe an additional shape was hit
236 m_aSelectedOID
= ObjectIdentifier( uno::Reference
< drawing::XShape
>( pNewObj
->getUnoShape(), uno::UNO_QUERY
) );
240 m_aSelectedOID
= ObjectIdentifier();
244 if ( !m_aSelectedOID
.isAdditionalShape() )
246 OUString
aPageCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_PAGE
, OUString() ) );//@todo read CID from model
248 if ( !m_aSelectedOID
.isAutoGeneratedObject() )
250 m_aSelectedOID
= ObjectIdentifier( aPageCID
);
253 //check whether the diagram was hit but not selected (e.g. because it has no filling):
254 OUString aDiagramCID
= ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM
, OUString::number( 0 ) );
255 OUString
aWallCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM_WALL
, OUString() ) );//@todo read CID from model
256 bool bBackGroundHit
= m_aSelectedOID
.getObjectCID().equals( aPageCID
) || m_aSelectedOID
.getObjectCID().equals( aWallCID
) || !m_aSelectedOID
.isAutoGeneratedObject();
259 //todo: if more than one diagram is available in future do check the list of all diagrams here
260 SdrObject
* pDiagram
= pDrawViewWrapper
->getNamedSdrObject( aDiagramCID
);
263 if( DrawViewWrapper::IsObjectHit( pDiagram
, rMousePos
) )
265 m_aSelectedOID
= ObjectIdentifier( aDiagramCID
);
269 //check whether the legend was hit but not selected (e.g. because it has no filling):
270 if( bBackGroundHit
|| m_aSelectedOID
.getObjectCID().equals( aDiagramCID
) )
272 OUString
aLegendCID( ObjectIdentifier::createClassifiedIdentifierForParticle( ObjectIdentifier::createParticleForLegend(0,0) ) );//@todo read CID from model
273 SdrObject
* pLegend
= pDrawViewWrapper
->getNamedSdrObject( aLegendCID
);
276 if( DrawViewWrapper::IsObjectHit( pLegend
, rMousePos
) )
278 m_aSelectedOID
= ObjectIdentifier( aLegendCID
);
285 if ( bIsRightMouse
&& m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
.isValid() )
287 m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
= ObjectIdentifier();
292 bool Selection::isResizeableObjectSelected()
294 ObjectType eObjectType
= m_aSelectedOID
.getObjectType();
295 switch( eObjectType
)
297 case OBJECTTYPE_DIAGRAM
:
298 case OBJECTTYPE_DIAGRAM_WALL
:
299 case OBJECTTYPE_SHAPE
:
300 case OBJECTTYPE_LEGEND
:
307 bool Selection::isRotateableObjectSelected( const uno::Reference
< frame::XModel
>& xChartModel
)
309 return SelectionHelper::isRotateableObject( m_aSelectedOID
.getObjectCID(), xChartModel
);
312 bool Selection::isDragableObjectSelected()
314 return m_aSelectedOID
.isDragableObject();
317 bool Selection::isAdditionalShapeSelected() const
319 return m_aSelectedOID
.isAdditionalShape();
322 bool SelectionHelper::findNamedParent( SdrObject
*& pInOutObject
324 , bool bGivenObjectMayBeResult
)
326 SolarMutexGuard aSolarGuard
;
327 //find the deepest named group
328 SdrObject
* pObj
= pInOutObject
;
330 if( bGivenObjectMayBeResult
)
331 aName
= lcl_getObjectName( pObj
);
333 while( pObj
&& !ObjectIdentifier::isCID( aName
) )
335 SdrObjList
* pObjList
= pObj
->GetObjList();
338 SdrObject
* pOwner
= pObjList
->GetOwnerObj();
342 aName
= lcl_getObjectName( pObj
);
355 bool SelectionHelper::findNamedParent( SdrObject
*& pInOutObject
356 , ObjectIdentifier
& rOutObject
357 , bool bGivenObjectMayBeResult
)
360 if ( findNamedParent( pInOutObject
, aName
, bGivenObjectMayBeResult
) )
362 rOutObject
= ObjectIdentifier( aName
);
368 bool SelectionHelper::isDragableObjectHitTwice( const Point
& rMPos
369 , const OUString
& rNameOfSelectedObject
370 , const DrawViewWrapper
& rDrawViewWrapper
)
372 if(rNameOfSelectedObject
.isEmpty())
374 if( !ObjectIdentifier::isDragableObject(rNameOfSelectedObject
) )
376 SolarMutexGuard aSolarGuard
;
377 SdrObject
* pObj
= rDrawViewWrapper
.getNamedSdrObject( rNameOfSelectedObject
);
378 if( !DrawViewWrapper::IsObjectHit( pObj
, rMPos
) )
383 OUString
SelectionHelper::getHitObjectCID(
385 DrawViewWrapper
& rDrawViewWrapper
,
386 bool bGetDiagramInsteadOf_Wall
)
389 SolarMutexGuard aSolarGuard
;
392 SdrObject
* pNewObj
= rDrawViewWrapper
.getHitObject(rMPos
);
393 aRet
= lcl_getObjectName( pNewObj
);//name of pNewObj
395 //ignore handle only objects for hit test
396 while( pNewObj
&& aRet
.match("HandlesOnly") )
398 pNewObj
->SetMarkProtect(true);
399 pNewObj
= rDrawViewWrapper
.getHitObject(rMPos
);
400 aRet
= lcl_getObjectName( pNewObj
);
403 //accept only named objects while searching for the object to select
404 if( !findNamedParent( pNewObj
, aRet
, true ) )
409 OUString
aPageCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_PAGE
, OUString() ) );//@todo read CID from model
410 //get page when nothing was hit
411 if( aRet
.isEmpty() && !pNewObj
)
416 //get diagram instead wall or page if hit inside diagram
417 if( !aRet
.isEmpty() )
419 if( aRet
.equals( aPageCID
) )
421 OUString aDiagramCID
= ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM
, OUString::number( 0 ) );
422 //todo: if more than one diagram is available in future do check the list of all diagrams here
423 SdrObject
* pDiagram
= rDrawViewWrapper
.getNamedSdrObject( aDiagramCID
);
426 if( DrawViewWrapper::IsObjectHit( pDiagram
, rMPos
) )
432 else if( bGetDiagramInsteadOf_Wall
)
434 OUString
aWallCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM_WALL
, OUString() ) );//@todo read CID from model
436 if( aRet
.equals( aWallCID
) )
438 OUString aDiagramCID
= ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM
, OUString::number( 0 ) );
448 bool SelectionHelper::isRotateableObject( const OUString
& rCID
449 , const uno::Reference
< frame::XModel
>& xChartModel
)
451 if( !ObjectIdentifier::isRotateableObject( rCID
) )
454 sal_Int32 nDimensionCount
= DiagramHelper::getDimension( ChartModelHelper::findDiagram( xChartModel
) );
456 if( nDimensionCount
== 3 )
461 SelectionHelper::SelectionHelper( SdrObject
* pSelectedObj
)
462 : m_pSelectedObj( pSelectedObj
), m_pMarkObj(NULL
)
466 SelectionHelper::~SelectionHelper()
470 bool SelectionHelper::getFrameDragSingles()
472 bool bFrameDragSingles
= true;//true == green == surrounding handles
473 if( m_pSelectedObj
&& m_pSelectedObj
->ISA(E3dObject
) )
474 bFrameDragSingles
= false;
475 return bFrameDragSingles
;
478 SdrObject
* SelectionHelper::getMarkHandlesObject( SdrObject
* pObj
)
482 OUString
aName( lcl_getObjectName( pObj
) );
483 if( aName
.match("MarkHandles") || aName
.match("HandlesOnly") )
485 if( !aName
.isEmpty() )//dont't get the markhandles of a different object
488 //search for a child with name "MarkHandles" or "HandlesOnly"
489 SolarMutexGuard aSolarGuard
;
490 SdrObjList
* pSubList
= pObj
->GetSubList();
493 SdrObjListIter
aIterator(*pSubList
, IM_FLAT
);
494 while (aIterator
.IsMore())
496 SdrObject
* pMarkHandles
= SelectionHelper::getMarkHandlesObject( aIterator
.Next() );
504 SdrObject
* SelectionHelper::getObjectToMark()
506 //return the selected object itself
507 //or a specific other object if that exsists
508 SdrObject
* pObj
= m_pSelectedObj
;
511 //search for a child with name "MarkHandles" or "HandlesOnly"
514 SolarMutexGuard aSolarGuard
;
515 SdrObjList
* pSubList
= pObj
->GetSubList();
518 SdrObjListIter
aIterator(*pSubList
, IM_FLAT
);
519 while (aIterator
.IsMore())
521 SdrObject
* pMarkHandles
= SelectionHelper::getMarkHandlesObject( aIterator
.Next() );
524 m_pMarkObj
= pMarkHandles
;
533 E3dScene
* SelectionHelper::getSceneToRotate( SdrObject
* pObj
)
535 //search whether the object or one of its children is a 3D object
536 //if so, return the accessory 3DScene
538 E3dObject
* pRotateable
= 0;
542 pRotateable
= dynamic_cast<E3dObject
*>(pObj
);
545 SolarMutexGuard aSolarGuard
;
546 SdrObjList
* pSubList
= pObj
->GetSubList();
549 SdrObjListIter
aIterator(*pSubList
, IM_DEEPWITHGROUPS
);
550 while( aIterator
.IsMore() && !pRotateable
)
552 SdrObject
* pSubObj
= aIterator
.Next();
553 pRotateable
= dynamic_cast<E3dObject
*>(pSubObj
);
559 E3dScene
* pScene
= 0;
562 SolarMutexGuard aSolarGuard
;
563 pScene
= pRotateable
->GetScene();
569 bool SelectionHelper::getMarkHandles( SdrHdlList
& rHdlList
)
571 SolarMutexGuard aSolarGuard
;
573 //@todo -> more flexible handle creation
574 //2 scenarios possible:
575 //1. add an additional invisible shape as a child to the selected object
576 //this child needs to be named somehow and handles need to be generated therefrom ...
577 //or 2. offer a central service per view where renderer and so can register for handle creation for a special shape
578 //.. or 3. feature from drawinglayer to create handles for each shape ... (bad performance ... ?) ?
580 //scenario 1 is now used:
581 //if a child with name MarkHandles exsists
582 //this child is marked instead of the logical selected object
585 //if a special mark object was found
586 //that object should be used for marking only
587 if( m_pMarkObj != m_pSelectedObj)
590 //if a special mark object was found
591 //that object should be used to create handles from
592 if( m_pMarkObj
&& m_pMarkObj
!= m_pSelectedObj
)
595 if( m_pMarkObj
->ISA(SdrPathObj
) )
597 //if th object is a polygon
598 //from each point a handle is generated
599 const ::basegfx::B2DPolyPolygon
& rPolyPolygon
= static_cast<SdrPathObj
*>(m_pMarkObj
)->GetPathPoly();
600 for( sal_uInt32 nN
= 0L; nN
< rPolyPolygon
.count(); nN
++)
602 const ::basegfx::B2DPolygon
aPolygon(rPolyPolygon
.getB2DPolygon(nN
));
603 for( sal_uInt32 nM
= 0L; nM
< aPolygon
.count(); nM
++)
605 const ::basegfx::B2DPoint
aPoint(aPolygon
.getB2DPoint(nM
));
606 SdrHdl
* pHdl
= new SdrHdl(Point(basegfx::fround(aPoint
.getX()), basegfx::fround(aPoint
.getY())), HDL_POLY
);
607 rHdlList
.AddHdl(pHdl
);
613 return false; //use the special MarkObject for marking
617 //add and document good marking defaults ...
621 SdrObject
* pObj
= m_pSelectedObj
;
624 SdrObjList
* pSubList
= pObj
->GetSubList();
625 if( !pSubList
)//no group object !pObj->IsGroupObject()
628 OUString
aName( lcl_getObjectName( pObj
) );
629 ObjectType
eObjectType( ObjectIdentifier::getObjectType( aName
) );
630 if( OBJECTTYPE_DATA_POINT
== eObjectType
631 || OBJECTTYPE_DATA_LABEL
== eObjectType
632 || OBJECTTYPE_LEGEND_ENTRY
== eObjectType
633 || OBJECTTYPE_AXIS_UNITLABEL
== eObjectType
)
638 SdrObjListIter
aIterator(*pSubList
, IM_FLAT
);
640 while (aIterator
.IsMore())
642 SdrObject
* pSubObj
= aIterator
.Next();
643 if( OBJECTTYPE_DATA_SERIES
== eObjectType
)
645 OUString
aSubName( lcl_getObjectName( pSubObj
) );
646 ObjectType
eSubObjectType( ObjectIdentifier::getObjectType( aSubName
) );
647 if( eSubObjectType
!=OBJECTTYPE_DATA_POINT
)
651 Point aPos
= pSubObj
->GetCurrentBoundRect().Center();
652 SdrHdl
* pHdl
= new SdrHdl(aPos
,HDL_POLY
);
653 rHdlList
.AddHdl(pHdl
);
660 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */