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>
22 #include <DiagramHelper.hxx>
23 #include <ChartModelHelper.hxx>
25 #include <svx/svdpage.hxx>
26 #include <svx/svditer.hxx>
27 #include <svx/obj3d.hxx>
28 #include <svx/svdopath.hxx>
29 #include <vcl/svapp.hxx>
30 #include <basegfx/point/b2dpoint.hxx>
34 using namespace ::com::sun::star
;
39 OUString
lcl_getObjectName( SdrObject
const * pObj
)
42 return pObj
->GetName();
46 void impl_selectObject( SdrObject
* pObjectToSelect
, DrawViewWrapper
& rDrawViewWrapper
)
48 SolarMutexGuard aSolarGuard
;
52 SelectionHelper
aSelectionHelper( pObjectToSelect
);
53 SdrObject
* pMarkObj
= aSelectionHelper
.getObjectToMark();
54 rDrawViewWrapper
.setMarkHandleProvider(&aSelectionHelper
);
55 rDrawViewWrapper
.MarkObject(pMarkObj
);
56 rDrawViewWrapper
.setMarkHandleProvider(nullptr);
60 }//anonymous namespace
62 bool Selection::hasSelection() const
64 return m_aSelectedOID
.isValid();
67 OUString
const & Selection::getSelectedCID() const
69 return m_aSelectedOID
.getObjectCID();
72 uno::Reference
< drawing::XShape
> const & Selection::getSelectedAdditionalShape() const
74 return m_aSelectedOID
.getAdditionalShape();
77 bool Selection::setSelection( const OUString
& rCID
)
79 if ( rCID
!= m_aSelectedOID
.getObjectCID() )
81 m_aSelectedOID
= ObjectIdentifier( rCID
);
87 bool Selection::setSelection( const uno::Reference
< drawing::XShape
>& xShape
)
89 if ( !( xShape
== m_aSelectedOID
.getAdditionalShape() ) )
92 m_aSelectedOID
= ObjectIdentifier( xShape
);
98 void Selection::clearSelection()
100 m_aSelectedOID
= ObjectIdentifier();
101 m_aSelectedOID_beforeMouseDown
= ObjectIdentifier();
102 m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
= ObjectIdentifier();
105 bool Selection::maybeSwitchSelectionAfterSingleClickWasEnsured()
107 if ( m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
.isValid()
108 && m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
!= m_aSelectedOID
)
110 m_aSelectedOID
= m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
;
111 m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
= ObjectIdentifier();
117 void Selection::resetPossibleSelectionAfterSingleClickWasEnsured()
119 if ( m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
.isValid() )
121 m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
= ObjectIdentifier();
125 void Selection::remindSelectionBeforeMouseDown()
127 m_aSelectedOID_beforeMouseDown
= m_aSelectedOID
;
130 bool Selection::isSelectionDifferentFromBeforeMouseDown() const
132 return ( m_aSelectedOID
!= m_aSelectedOID_beforeMouseDown
);
135 void Selection::applySelection( DrawViewWrapper
* pDrawViewWrapper
)
137 if( pDrawViewWrapper
)
140 SolarMutexGuard aSolarGuard
;
141 pDrawViewWrapper
->UnmarkAll();
143 SdrObject
* pObjectToSelect
= nullptr;
144 if ( m_aSelectedOID
.isAutoGeneratedObject() )
146 pObjectToSelect
= pDrawViewWrapper
->getNamedSdrObject( m_aSelectedOID
.getObjectCID() );
148 else if( m_aSelectedOID
.isAdditionalShape() )
150 pObjectToSelect
= DrawViewWrapper::getSdrObject( m_aSelectedOID
.getAdditionalShape() );
153 impl_selectObject( pObjectToSelect
, *pDrawViewWrapper
);
157 void Selection::adaptSelectionToNewPos( const Point
& rMousePos
, DrawViewWrapper
const * pDrawViewWrapper
158 , bool bIsRightMouse
, bool bWaitingForDoubleClick
)
160 if( pDrawViewWrapper
)
162 //do not toggle multiclick selection if right clicked on the selected object or waiting for double click
163 bool bAllowMultiClickSelectionChange
= !bIsRightMouse
&& !bWaitingForDoubleClick
;
165 ObjectIdentifier
aLastSelectedObject( m_aSelectedOID
);
167 SolarMutexGuard aSolarGuard
;
169 //bAllowMultiClickSelectionChange==true -> a second click on the same object can lead to a changed selection (e.g. series -> single data point)
171 //get object to select:
173 m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
= ObjectIdentifier();
175 //the search for the object to select starts with the hit object deepest in the grouping hierarchy (a leaf in the tree)
176 //further we travel along the grouping hierarchy from child to parent
177 SdrObject
* pNewObj
= pDrawViewWrapper
->getHitObject(rMousePos
);
178 m_aSelectedOID
= ObjectIdentifier( lcl_getObjectName( pNewObj
) );//name of pNewObj
180 //ignore handle only objects for hit test
181 while( pNewObj
&& m_aSelectedOID
.getObjectCID().match( "HandlesOnly" ) )
183 pNewObj
->SetMarkProtect(true);
184 pNewObj
= pDrawViewWrapper
->getHitObject(rMousePos
);
185 m_aSelectedOID
= ObjectIdentifier( lcl_getObjectName( pNewObj
) );
188 //accept only named objects while searching for the object to select
189 //this call may change m_aSelectedOID
190 if ( SelectionHelper::findNamedParent( pNewObj
, m_aSelectedOID
, true ) )
192 //if the so far found object is a multi click object further steps are necessary
193 while( ObjectIdentifier::isMultiClickObject( m_aSelectedOID
.getObjectCID() ) )
195 bool bSameObjectAsLastSelected
= ( aLastSelectedObject
== m_aSelectedOID
);
196 if( bSameObjectAsLastSelected
)
198 //if the same child is clicked again don't go up further
201 if ( ObjectIdentifier::areSiblings( aLastSelectedObject
.getObjectCID(), m_aSelectedOID
.getObjectCID() ) )
203 //if a sibling of the last selected object is clicked don't go up further
206 ObjectIdentifier aLastChild
= m_aSelectedOID
;
207 if ( !SelectionHelper::findNamedParent( pNewObj
, m_aSelectedOID
, false ) )
209 //take the one found so far
212 //if the last selected object is found don't go up further
213 //but take the last child if selection change is allowed
214 if ( aLastSelectedObject
== m_aSelectedOID
)
216 if( bAllowMultiClickSelectionChange
)
218 m_aSelectedOID
= aLastChild
;
221 m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
= aLastChild
;
226 OSL_ENSURE(m_aSelectedOID
.isValid(), "somehow lost selected object");
230 //maybe an additional shape was hit
233 m_aSelectedOID
= ObjectIdentifier( uno::Reference
< drawing::XShape
>( pNewObj
->getUnoShape(), uno::UNO_QUERY
) );
237 m_aSelectedOID
= ObjectIdentifier();
241 if ( !m_aSelectedOID
.isAdditionalShape() )
243 OUString
aPageCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_PAGE
, OUString() ) );//@todo read CID from model
245 if ( !m_aSelectedOID
.isAutoGeneratedObject() )
247 m_aSelectedOID
= ObjectIdentifier( aPageCID
);
250 //check whether the diagram was hit but not selected (e.g. because it has no filling):
251 OUString aDiagramCID
= ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM
, OUString::number( 0 ) );
252 OUString
aWallCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM_WALL
, OUString() ) );//@todo read CID from model
253 bool bBackGroundHit
= m_aSelectedOID
.getObjectCID() == aPageCID
|| m_aSelectedOID
.getObjectCID() == aWallCID
|| !m_aSelectedOID
.isAutoGeneratedObject();
256 //todo: if more than one diagram is available in future do check the list of all diagrams here
257 SdrObject
* pDiagram
= pDrawViewWrapper
->getNamedSdrObject( aDiagramCID
);
260 if( DrawViewWrapper::IsObjectHit( pDiagram
, rMousePos
) )
262 m_aSelectedOID
= ObjectIdentifier( aDiagramCID
);
266 //check whether the legend was hit but not selected (e.g. because it has no filling):
267 if( bBackGroundHit
|| m_aSelectedOID
.getObjectCID() == aDiagramCID
)
269 OUString
aLegendCID( ObjectIdentifier::createClassifiedIdentifierForParticle( ObjectIdentifier::createParticleForLegend(nullptr) ) );//@todo read CID from model
270 SdrObject
* pLegend
= pDrawViewWrapper
->getNamedSdrObject( aLegendCID
);
273 if( DrawViewWrapper::IsObjectHit( pLegend
, rMousePos
) )
275 m_aSelectedOID
= ObjectIdentifier( aLegendCID
);
282 if ( bIsRightMouse
&& m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
.isValid() )
284 m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
= ObjectIdentifier();
289 bool Selection::isResizeableObjectSelected() const
291 ObjectType eObjectType
= m_aSelectedOID
.getObjectType();
292 switch( eObjectType
)
294 case OBJECTTYPE_DIAGRAM
:
295 case OBJECTTYPE_DIAGRAM_WALL
:
296 case OBJECTTYPE_SHAPE
:
297 case OBJECTTYPE_LEGEND
:
304 bool Selection::isRotateableObjectSelected( const uno::Reference
< frame::XModel
>& xChartModel
) const
306 return SelectionHelper::isRotateableObject( m_aSelectedOID
.getObjectCID(), xChartModel
);
309 bool Selection::isDragableObjectSelected() const
311 return m_aSelectedOID
.isDragableObject();
314 bool Selection::isAdditionalShapeSelected() const
316 return m_aSelectedOID
.isAdditionalShape();
319 bool SelectionHelper::findNamedParent( SdrObject
*& pInOutObject
321 , bool bGivenObjectMayBeResult
)
323 SolarMutexGuard aSolarGuard
;
324 //find the deepest named group
325 SdrObject
* pObj
= pInOutObject
;
327 if( bGivenObjectMayBeResult
)
328 aName
= lcl_getObjectName( pObj
);
330 while( pObj
&& !ObjectIdentifier::isCID( aName
) )
332 SdrObjList
* pObjList
= pObj
->getParentSdrObjListFromSdrObject();
335 SdrObject
* pOwner
= pObjList
->getSdrObjectFromSdrObjList();
339 aName
= lcl_getObjectName( pObj
);
352 bool SelectionHelper::findNamedParent( SdrObject
*& pInOutObject
353 , ObjectIdentifier
& rOutObject
354 , bool bGivenObjectMayBeResult
)
357 if ( findNamedParent( pInOutObject
, aName
, bGivenObjectMayBeResult
) )
359 rOutObject
= ObjectIdentifier( aName
);
365 bool SelectionHelper::isDragableObjectHitTwice( const Point
& rMPos
366 , const OUString
& rNameOfSelectedObject
367 , const DrawViewWrapper
& rDrawViewWrapper
)
369 if(rNameOfSelectedObject
.isEmpty())
371 if( !ObjectIdentifier::isDragableObject(rNameOfSelectedObject
) )
373 SolarMutexGuard aSolarGuard
;
374 SdrObject
* pObj
= rDrawViewWrapper
.getNamedSdrObject( rNameOfSelectedObject
);
375 return DrawViewWrapper::IsObjectHit( pObj
, rMPos
);
378 OUString
SelectionHelper::getHitObjectCID(
380 DrawViewWrapper
const & rDrawViewWrapper
,
381 bool bGetDiagramInsteadOf_Wall
)
383 SolarMutexGuard aSolarGuard
;
386 SdrObject
* pNewObj
= rDrawViewWrapper
.getHitObject(rMPos
);
387 aRet
= lcl_getObjectName( pNewObj
);//name of pNewObj
389 //ignore handle only objects for hit test
390 while( pNewObj
&& aRet
.match("HandlesOnly") )
392 pNewObj
->SetMarkProtect(true);
393 pNewObj
= rDrawViewWrapper
.getHitObject(rMPos
);
394 aRet
= lcl_getObjectName( pNewObj
);
397 //accept only named objects while searching for the object to select
398 if( !findNamedParent( pNewObj
, aRet
, true ) )
403 OUString
aPageCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_PAGE
, OUString() ) );//@todo read CID from model
404 //get page when nothing was hit
405 if( aRet
.isEmpty() && !pNewObj
)
410 //get diagram instead wall or page if hit inside diagram
411 if( !aRet
.isEmpty() )
413 if( aRet
== aPageCID
)
415 OUString aDiagramCID
= ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM
, OUString::number( 0 ) );
416 //todo: if more than one diagram is available in future do check the list of all diagrams here
417 SdrObject
* pDiagram
= rDrawViewWrapper
.getNamedSdrObject( aDiagramCID
);
420 if( DrawViewWrapper::IsObjectHit( pDiagram
, rMPos
) )
426 else if( bGetDiagramInsteadOf_Wall
)
428 OUString
aWallCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM_WALL
, OUString() ) );//@todo read CID from model
430 if( aRet
== aWallCID
)
432 OUString aDiagramCID
= ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM
, OUString::number( 0 ) );
442 bool SelectionHelper::isRotateableObject( const OUString
& rCID
443 , const uno::Reference
< frame::XModel
>& xChartModel
)
445 if( !ObjectIdentifier::isRotateableObject( rCID
) )
448 sal_Int32 nDimensionCount
= DiagramHelper::getDimension( ChartModelHelper::findDiagram( xChartModel
) );
450 return nDimensionCount
== 3;
453 SelectionHelper::SelectionHelper( SdrObject
* pSelectedObj
)
454 : m_pSelectedObj( pSelectedObj
), m_pMarkObj(nullptr)
458 SelectionHelper::~SelectionHelper()
462 bool SelectionHelper::getFrameDragSingles()
464 //true == green == surrounding handles
465 return dynamic_cast<const E3dObject
*>( m_pSelectedObj
) == nullptr;
468 SdrObject
* SelectionHelper::getMarkHandlesObject( SdrObject
* pObj
)
472 OUString
aName( lcl_getObjectName( pObj
) );
473 if( aName
.match("MarkHandles") || aName
.match("HandlesOnly") )
475 if( !aName
.isEmpty() )//don't get the markhandles of a different object
478 //search for a child with name "MarkHandles" or "HandlesOnly"
479 SolarMutexGuard aSolarGuard
;
480 SdrObjList
* pSubList
= pObj
->GetSubList();
483 SdrObjListIter
aIterator(pSubList
, SdrIterMode::Flat
);
484 while (aIterator
.IsMore())
486 SdrObject
* pMarkHandles
= SelectionHelper::getMarkHandlesObject( aIterator
.Next() );
494 SdrObject
* SelectionHelper::getObjectToMark()
496 //return the selected object itself
497 //or a specific other object if that exists
498 SdrObject
* pObj
= m_pSelectedObj
;
501 //search for a child with name "MarkHandles" or "HandlesOnly"
504 SolarMutexGuard aSolarGuard
;
505 SdrObjList
* pSubList
= pObj
->GetSubList();
508 SdrObjListIter
aIterator(pSubList
, SdrIterMode::Flat
);
509 while (aIterator
.IsMore())
511 SdrObject
* pMarkHandles
= SelectionHelper::getMarkHandlesObject( aIterator
.Next() );
514 m_pMarkObj
= pMarkHandles
;
523 E3dScene
* SelectionHelper::getSceneToRotate( SdrObject
* pObj
)
525 //search whether the object or one of its children is a 3D object
526 //if so, return the accessory 3DScene
528 E3dObject
* pRotateable
= nullptr;
532 pRotateable
= dynamic_cast<E3dObject
*>(pObj
);
535 SolarMutexGuard aSolarGuard
;
536 SdrObjList
* pSubList
= pObj
->GetSubList();
539 SdrObjListIter
aIterator(pSubList
, SdrIterMode::DeepWithGroups
);
540 while( aIterator
.IsMore() && !pRotateable
)
542 SdrObject
* pSubObj
= aIterator
.Next();
543 pRotateable
= dynamic_cast<E3dObject
*>(pSubObj
);
549 E3dScene
* pScene(nullptr);
553 SolarMutexGuard aSolarGuard
;
554 pScene
= pRotateable
->getRootE3dSceneFromE3dObject();
560 bool SelectionHelper::getMarkHandles( SdrHdlList
& rHdlList
)
562 SolarMutexGuard aSolarGuard
;
564 //@todo -> more flexible handle creation
565 //2 scenarios possible:
566 //1. add an additional invisible shape as a child to the selected object
567 //this child needs to be named somehow and handles need to be generated there from...
568 //or 2. offer a central service per view where renderer and so can register for handle creation for a special shape
569 //.. or 3. feature from drawinglayer to create handles for each shape... (bad performance... ?) ?
571 //scenario 1 is now used:
572 //if a child with name MarkHandles exists
573 //this child is marked instead of the logical selected object
576 //if a special mark object was found
577 //that object should be used for marking only
578 if( m_pMarkObj != m_pSelectedObj)
581 //if a special mark object was found
582 //that object should be used to create handles from
583 if( m_pMarkObj
&& m_pMarkObj
!= m_pSelectedObj
)
586 if( dynamic_cast<const SdrPathObj
*>( m_pMarkObj
) != nullptr )
588 //if th object is a polygon
589 //from each point a handle is generated
590 const ::basegfx::B2DPolyPolygon
& rPolyPolygon
= static_cast<SdrPathObj
*>(m_pMarkObj
)->GetPathPoly();
591 for( sal_uInt32 nN
= 0; nN
< rPolyPolygon
.count(); nN
++)
593 const ::basegfx::B2DPolygon
& aPolygon(rPolyPolygon
.getB2DPolygon(nN
));
594 for( sal_uInt32 nM
= 0; nM
< aPolygon
.count(); nM
++)
596 const ::basegfx::B2DPoint
aPoint(aPolygon
.getB2DPoint(nM
));
597 rHdlList
.AddHdl(std::make_unique
<SdrHdl
>(Point(basegfx::fround(aPoint
.getX()), basegfx::fround(aPoint
.getY())), SdrHdlKind::Poly
));
603 return false; //use the special MarkObject for marking
607 //add and document good marking defaults ...
611 SdrObject
* pObj
= m_pSelectedObj
;
614 SdrObjList
* pSubList
= pObj
->GetSubList();
615 if( !pSubList
)//no group object !pObj->IsGroupObject()
618 OUString
aName( lcl_getObjectName( pObj
) );
619 ObjectType
eObjectType( ObjectIdentifier::getObjectType( aName
) );
620 if( eObjectType
== OBJECTTYPE_DATA_POINT
621 || eObjectType
== OBJECTTYPE_DATA_LABEL
622 || eObjectType
== OBJECTTYPE_LEGEND_ENTRY
623 || eObjectType
== OBJECTTYPE_AXIS_UNITLABEL
)
628 SdrObjListIter
aIterator(pSubList
, SdrIterMode::Flat
);
630 while (aIterator
.IsMore())
632 SdrObject
* pSubObj
= aIterator
.Next();
633 if( eObjectType
== OBJECTTYPE_DATA_SERIES
)
635 OUString
aSubName( lcl_getObjectName( pSubObj
) );
636 ObjectType
eSubObjectType( ObjectIdentifier::getObjectType( aSubName
) );
637 if( eSubObjectType
!=OBJECTTYPE_DATA_POINT
)
641 Point aPos
= pSubObj
->GetCurrentBoundRect().Center();
642 rHdlList
.AddHdl(std::make_unique
<SdrHdl
>(aPos
,SdrHdlKind::Poly
));
649 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */