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>
31 #include <com/sun/star/beans/XPropertySet.hpp>
35 using namespace ::com::sun::star
;
40 OUString
lcl_getObjectName( SdrObject
const * pObj
)
43 return pObj
->GetName();
47 void impl_selectObject( SdrObject
* pObjectToSelect
, DrawViewWrapper
& rDrawViewWrapper
)
49 SolarMutexGuard aSolarGuard
;
53 SelectionHelper
aSelectionHelper( pObjectToSelect
);
54 SdrObject
* pMarkObj
= aSelectionHelper
.getObjectToMark();
55 rDrawViewWrapper
.setMarkHandleProvider(&aSelectionHelper
);
56 rDrawViewWrapper
.MarkObject(pMarkObj
);
57 rDrawViewWrapper
.setMarkHandleProvider(nullptr);
61 }//anonymous namespace
63 bool Selection::hasSelection()
65 return m_aSelectedOID
.isValid();
68 OUString
const & Selection::getSelectedCID()
70 return m_aSelectedOID
.getObjectCID();
73 uno::Reference
< drawing::XShape
> const & Selection::getSelectedAdditionalShape()
75 return m_aSelectedOID
.getAdditionalShape();
78 bool Selection::setSelection( const OUString
& rCID
)
80 if ( rCID
!= m_aSelectedOID
.getObjectCID() )
82 m_aSelectedOID
= ObjectIdentifier( rCID
);
88 bool Selection::setSelection( const uno::Reference
< drawing::XShape
>& xShape
)
90 if ( !( xShape
== m_aSelectedOID
.getAdditionalShape() ) )
93 m_aSelectedOID
= ObjectIdentifier( xShape
);
99 void Selection::clearSelection()
101 m_aSelectedOID
= ObjectIdentifier();
102 m_aSelectedOID_beforeMouseDown
= ObjectIdentifier();
103 m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
= ObjectIdentifier();
106 bool Selection::maybeSwitchSelectionAfterSingleClickWasEnsured()
108 if ( m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
.isValid()
109 && m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
!= m_aSelectedOID
)
111 m_aSelectedOID
= m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
;
112 m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
= ObjectIdentifier();
118 void Selection::resetPossibleSelectionAfterSingleClickWasEnsured()
120 if ( m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
.isValid() )
122 m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
= ObjectIdentifier();
126 void Selection::remindSelectionBeforeMouseDown()
128 m_aSelectedOID_beforeMouseDown
= m_aSelectedOID
;
131 bool Selection::isSelectionDifferentFromBeforeMouseDown() const
133 return ( m_aSelectedOID
!= m_aSelectedOID_beforeMouseDown
);
136 void Selection::applySelection( DrawViewWrapper
* pDrawViewWrapper
)
138 if( pDrawViewWrapper
)
141 SolarMutexGuard aSolarGuard
;
142 pDrawViewWrapper
->UnmarkAll();
144 SdrObject
* pObjectToSelect
= nullptr;
145 if ( m_aSelectedOID
.isAutoGeneratedObject() )
147 pObjectToSelect
= pDrawViewWrapper
->getNamedSdrObject( m_aSelectedOID
.getObjectCID() );
149 else if( m_aSelectedOID
.isAdditionalShape() )
151 pObjectToSelect
= DrawViewWrapper::getSdrObject( m_aSelectedOID
.getAdditionalShape() );
154 impl_selectObject( pObjectToSelect
, *pDrawViewWrapper
);
158 void Selection::adaptSelectionToNewPos( const Point
& rMousePos
, DrawViewWrapper
const * pDrawViewWrapper
159 , bool bIsRightMouse
, bool bWaitingForDoubleClick
)
161 if( pDrawViewWrapper
)
163 //do not toggle multiclick selection if right clicked on the selected object or waiting for double click
164 bool bAllowMultiClickSelectionChange
= !bIsRightMouse
&& !bWaitingForDoubleClick
;
166 ObjectIdentifier
aLastSelectedObject( m_aSelectedOID
);
168 SolarMutexGuard aSolarGuard
;
170 //bAllowMultiClickSelectionChange==true -> a second click on the same object can lead to a changed selection (e.g. series -> single data point)
172 //get object to select:
174 m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
= ObjectIdentifier();
176 //the search for the object to select starts with the hit object deepest in the grouping hierarchy (a leaf in the tree)
177 //further we travel along the grouping hierarchy from child to parent
178 SdrObject
* pNewObj
= pDrawViewWrapper
->getHitObject(rMousePos
);
179 m_aSelectedOID
= ObjectIdentifier( lcl_getObjectName( pNewObj
) );//name of pNewObj
181 //ignore handle only objects for hit test
182 while( pNewObj
&& m_aSelectedOID
.getObjectCID().match( "HandlesOnly" ) )
184 pNewObj
->SetMarkProtect(true);
185 pNewObj
= pDrawViewWrapper
->getHitObject(rMousePos
);
186 m_aSelectedOID
= ObjectIdentifier( lcl_getObjectName( pNewObj
) );
189 //accept only named objects while searching for the object to select
190 //this call may change m_aSelectedOID
191 if ( SelectionHelper::findNamedParent( pNewObj
, m_aSelectedOID
, true ) )
193 //if the so far found object is a multi click object further steps are necessary
194 while( ObjectIdentifier::isMultiClickObject( m_aSelectedOID
.getObjectCID() ) )
196 bool bSameObjectAsLastSelected
= ( aLastSelectedObject
== m_aSelectedOID
);
197 if( bSameObjectAsLastSelected
)
199 //if the same child is clicked again don't go up further
202 if ( ObjectIdentifier::areSiblings( aLastSelectedObject
.getObjectCID(), m_aSelectedOID
.getObjectCID() ) )
204 //if a sibling of the last selected object is clicked don't go up further
207 ObjectIdentifier aLastChild
= m_aSelectedOID
;
208 if ( !SelectionHelper::findNamedParent( pNewObj
, m_aSelectedOID
, false ) )
210 //take the one found so far
213 //if the last selected object is found don't go up further
214 //but take the last child if selection change is allowed
215 if ( aLastSelectedObject
== m_aSelectedOID
)
217 if( bAllowMultiClickSelectionChange
)
219 m_aSelectedOID
= aLastChild
;
222 m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
= aLastChild
;
227 OSL_ENSURE(m_aSelectedOID
.isValid(), "somehow lost selected object");
231 //maybe an additional shape was hit
234 m_aSelectedOID
= ObjectIdentifier( uno::Reference
< drawing::XShape
>( pNewObj
->getUnoShape(), uno::UNO_QUERY
) );
238 m_aSelectedOID
= ObjectIdentifier();
242 if ( !m_aSelectedOID
.isAdditionalShape() )
244 OUString
aPageCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_PAGE
, OUString() ) );//@todo read CID from model
246 if ( !m_aSelectedOID
.isAutoGeneratedObject() )
248 m_aSelectedOID
= ObjectIdentifier( aPageCID
);
251 //check whether the diagram was hit but not selected (e.g. because it has no filling):
252 OUString aDiagramCID
= ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM
, OUString::number( 0 ) );
253 OUString
aWallCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM_WALL
, OUString() ) );//@todo read CID from model
254 bool bBackGroundHit
= m_aSelectedOID
.getObjectCID() == aPageCID
|| m_aSelectedOID
.getObjectCID() == aWallCID
|| !m_aSelectedOID
.isAutoGeneratedObject();
257 //todo: if more than one diagram is available in future do check the list of all diagrams here
258 SdrObject
* pDiagram
= pDrawViewWrapper
->getNamedSdrObject( aDiagramCID
);
261 if( DrawViewWrapper::IsObjectHit( pDiagram
, rMousePos
) )
263 m_aSelectedOID
= ObjectIdentifier( aDiagramCID
);
267 //check whether the legend was hit but not selected (e.g. because it has no filling):
268 if( bBackGroundHit
|| m_aSelectedOID
.getObjectCID() == aDiagramCID
)
270 OUString
aLegendCID( ObjectIdentifier::createClassifiedIdentifierForParticle( ObjectIdentifier::createParticleForLegend(nullptr) ) );//@todo read CID from model
271 SdrObject
* pLegend
= pDrawViewWrapper
->getNamedSdrObject( aLegendCID
);
274 if( DrawViewWrapper::IsObjectHit( pLegend
, rMousePos
) )
276 m_aSelectedOID
= ObjectIdentifier( aLegendCID
);
283 if ( bIsRightMouse
&& m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
.isValid() )
285 m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
= ObjectIdentifier();
290 bool Selection::isResizeableObjectSelected()
292 ObjectType eObjectType
= m_aSelectedOID
.getObjectType();
293 switch( eObjectType
)
295 case OBJECTTYPE_DIAGRAM
:
296 case OBJECTTYPE_DIAGRAM_WALL
:
297 case OBJECTTYPE_SHAPE
:
298 case OBJECTTYPE_LEGEND
:
305 bool Selection::isRotateableObjectSelected( const uno::Reference
< frame::XModel
>& xChartModel
)
307 return SelectionHelper::isRotateableObject( m_aSelectedOID
.getObjectCID(), xChartModel
);
310 bool Selection::isDragableObjectSelected()
312 return m_aSelectedOID
.isDragableObject();
315 bool Selection::isAdditionalShapeSelected() const
317 return m_aSelectedOID
.isAdditionalShape();
320 bool SelectionHelper::findNamedParent( SdrObject
*& pInOutObject
322 , bool bGivenObjectMayBeResult
)
324 SolarMutexGuard aSolarGuard
;
325 //find the deepest named group
326 SdrObject
* pObj
= pInOutObject
;
328 if( bGivenObjectMayBeResult
)
329 aName
= lcl_getObjectName( pObj
);
331 while( pObj
&& !ObjectIdentifier::isCID( aName
) )
333 SdrObjList
* pObjList
= pObj
->getParentOfSdrObject();
336 SdrObject
* pOwner
= pObjList
->getSdrObjectFromSdrObjList();
340 aName
= lcl_getObjectName( pObj
);
353 bool SelectionHelper::findNamedParent( SdrObject
*& pInOutObject
354 , ObjectIdentifier
& rOutObject
355 , bool bGivenObjectMayBeResult
)
358 if ( findNamedParent( pInOutObject
, aName
, bGivenObjectMayBeResult
) )
360 rOutObject
= ObjectIdentifier( aName
);
366 bool SelectionHelper::isDragableObjectHitTwice( const Point
& rMPos
367 , const OUString
& rNameOfSelectedObject
368 , const DrawViewWrapper
& rDrawViewWrapper
)
370 if(rNameOfSelectedObject
.isEmpty())
372 if( !ObjectIdentifier::isDragableObject(rNameOfSelectedObject
) )
374 SolarMutexGuard aSolarGuard
;
375 SdrObject
* pObj
= rDrawViewWrapper
.getNamedSdrObject( rNameOfSelectedObject
);
376 return DrawViewWrapper::IsObjectHit( pObj
, rMPos
);
379 OUString
SelectionHelper::getHitObjectCID(
381 DrawViewWrapper
const & rDrawViewWrapper
,
382 bool bGetDiagramInsteadOf_Wall
)
384 SolarMutexGuard aSolarGuard
;
387 SdrObject
* pNewObj
= rDrawViewWrapper
.getHitObject(rMPos
);
388 aRet
= lcl_getObjectName( pNewObj
);//name of pNewObj
390 //ignore handle only objects for hit test
391 while( pNewObj
&& aRet
.match("HandlesOnly") )
393 pNewObj
->SetMarkProtect(true);
394 pNewObj
= rDrawViewWrapper
.getHitObject(rMPos
);
395 aRet
= lcl_getObjectName( pNewObj
);
398 //accept only named objects while searching for the object to select
399 if( !findNamedParent( pNewObj
, aRet
, true ) )
404 OUString
aPageCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_PAGE
, OUString() ) );//@todo read CID from model
405 //get page when nothing was hit
406 if( aRet
.isEmpty() && !pNewObj
)
411 //get diagram instead wall or page if hit inside diagram
412 if( !aRet
.isEmpty() )
414 if( aRet
== aPageCID
)
416 OUString aDiagramCID
= ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM
, OUString::number( 0 ) );
417 //todo: if more than one diagram is available in future do check the list of all diagrams here
418 SdrObject
* pDiagram
= rDrawViewWrapper
.getNamedSdrObject( aDiagramCID
);
421 if( DrawViewWrapper::IsObjectHit( pDiagram
, rMPos
) )
427 else if( bGetDiagramInsteadOf_Wall
)
429 OUString
aWallCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM_WALL
, OUString() ) );//@todo read CID from model
431 if( aRet
== aWallCID
)
433 OUString aDiagramCID
= ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM
, OUString::number( 0 ) );
443 bool SelectionHelper::isRotateableObject( const OUString
& rCID
444 , const uno::Reference
< frame::XModel
>& xChartModel
)
446 if( !ObjectIdentifier::isRotateableObject( rCID
) )
449 sal_Int32 nDimensionCount
= DiagramHelper::getDimension( ChartModelHelper::findDiagram( xChartModel
) );
451 return nDimensionCount
== 3;
454 SelectionHelper::SelectionHelper( SdrObject
* pSelectedObj
)
455 : m_pSelectedObj( pSelectedObj
), m_pMarkObj(nullptr)
459 SelectionHelper::~SelectionHelper()
463 bool SelectionHelper::getFrameDragSingles()
465 bool bFrameDragSingles
= true;//true == green == surrounding handles
466 if( m_pSelectedObj
&& dynamic_cast<const E3dObject
*>( m_pSelectedObj
) != nullptr )
467 bFrameDragSingles
= false;
468 return bFrameDragSingles
;
471 SdrObject
* SelectionHelper::getMarkHandlesObject( SdrObject
* pObj
)
475 OUString
aName( lcl_getObjectName( pObj
) );
476 if( aName
.match("MarkHandles") || aName
.match("HandlesOnly") )
478 if( !aName
.isEmpty() )//don't get the markhandles of a different object
481 //search for a child with name "MarkHandles" or "HandlesOnly"
482 SolarMutexGuard aSolarGuard
;
483 SdrObjList
* pSubList
= pObj
->GetSubList();
486 SdrObjListIter
aIterator(pSubList
, SdrIterMode::Flat
);
487 while (aIterator
.IsMore())
489 SdrObject
* pMarkHandles
= SelectionHelper::getMarkHandlesObject( aIterator
.Next() );
497 SdrObject
* SelectionHelper::getObjectToMark()
499 //return the selected object itself
500 //or a specific other object if that exsists
501 SdrObject
* pObj
= m_pSelectedObj
;
504 //search for a child with name "MarkHandles" or "HandlesOnly"
507 SolarMutexGuard aSolarGuard
;
508 SdrObjList
* pSubList
= pObj
->GetSubList();
511 SdrObjListIter
aIterator(pSubList
, SdrIterMode::Flat
);
512 while (aIterator
.IsMore())
514 SdrObject
* pMarkHandles
= SelectionHelper::getMarkHandlesObject( aIterator
.Next() );
517 m_pMarkObj
= pMarkHandles
;
526 E3dScene
* SelectionHelper::getSceneToRotate( SdrObject
* pObj
)
528 //search whether the object or one of its children is a 3D object
529 //if so, return the accessory 3DScene
531 E3dObject
* pRotateable
= nullptr;
535 pRotateable
= dynamic_cast<E3dObject
*>(pObj
);
538 SolarMutexGuard aSolarGuard
;
539 SdrObjList
* pSubList
= pObj
->GetSubList();
542 SdrObjListIter
aIterator(pSubList
, SdrIterMode::DeepWithGroups
);
543 while( aIterator
.IsMore() && !pRotateable
)
545 SdrObject
* pSubObj
= aIterator
.Next();
546 pRotateable
= dynamic_cast<E3dObject
*>(pSubObj
);
552 E3dScene
* pScene
= nullptr;
555 SolarMutexGuard aSolarGuard
;
556 pScene
= pRotateable
->GetScene();
562 bool SelectionHelper::getMarkHandles( SdrHdlList
& rHdlList
)
564 SolarMutexGuard aSolarGuard
;
566 //@todo -> more flexible handle creation
567 //2 scenarios possible:
568 //1. add an additional invisible shape as a child to the selected object
569 //this child needs to be named somehow and handles need to be generated therefrom ...
570 //or 2. offer a central service per view where renderer and so can register for handle creation for a special shape
571 //.. or 3. feature from drawinglayer to create handles for each shape ... (bad performance ... ?) ?
573 //scenario 1 is now used:
574 //if a child with name MarkHandles exsists
575 //this child is marked instead of the logical selected object
578 //if a special mark object was found
579 //that object should be used for marking only
580 if( m_pMarkObj != m_pSelectedObj)
583 //if a special mark object was found
584 //that object should be used to create handles from
585 if( m_pMarkObj
&& m_pMarkObj
!= m_pSelectedObj
)
588 if( dynamic_cast<const SdrPathObj
*>( m_pMarkObj
) != nullptr )
590 //if th object is a polygon
591 //from each point a handle is generated
592 const ::basegfx::B2DPolyPolygon
& rPolyPolygon
= static_cast<SdrPathObj
*>(m_pMarkObj
)->GetPathPoly();
593 for( sal_uInt32 nN
= 0; nN
< rPolyPolygon
.count(); nN
++)
595 const ::basegfx::B2DPolygon
aPolygon(rPolyPolygon
.getB2DPolygon(nN
));
596 for( sal_uInt32 nM
= 0; nM
< aPolygon
.count(); nM
++)
598 const ::basegfx::B2DPoint
aPoint(aPolygon
.getB2DPoint(nM
));
599 SdrHdl
* pHdl
= new SdrHdl(Point(basegfx::fround(aPoint
.getX()), basegfx::fround(aPoint
.getY())), SdrHdlKind::Poly
);
600 rHdlList
.AddHdl(pHdl
);
606 return false; //use the special MarkObject for marking
610 //add and document good marking defaults ...
614 SdrObject
* pObj
= m_pSelectedObj
;
617 SdrObjList
* pSubList
= pObj
->GetSubList();
618 if( !pSubList
)//no group object !pObj->IsGroupObject()
621 OUString
aName( lcl_getObjectName( pObj
) );
622 ObjectType
eObjectType( ObjectIdentifier::getObjectType( aName
) );
623 if( eObjectType
== OBJECTTYPE_DATA_POINT
624 || eObjectType
== OBJECTTYPE_DATA_LABEL
625 || eObjectType
== OBJECTTYPE_LEGEND_ENTRY
626 || eObjectType
== OBJECTTYPE_AXIS_UNITLABEL
)
631 SdrObjListIter
aIterator(pSubList
, SdrIterMode::Flat
);
633 while (aIterator
.IsMore())
635 SdrObject
* pSubObj
= aIterator
.Next();
636 if( eObjectType
== OBJECTTYPE_DATA_SERIES
)
638 OUString
aSubName( lcl_getObjectName( pSubObj
) );
639 ObjectType
eSubObjectType( ObjectIdentifier::getObjectType( aSubName
) );
640 if( eSubObjectType
!=OBJECTTYPE_DATA_POINT
)
644 Point aPos
= pSubObj
->GetCurrentBoundRect().Center();
645 SdrHdl
* pHdl
= new SdrHdl(aPos
,SdrHdlKind::Poly
);
646 rHdlList
.AddHdl(pHdl
);
653 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */