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 <Diagram.hxx>
23 #include <ChartModel.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 <osl/diagnose.h>
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() const
65 return m_aSelectedOID
.isValid();
68 OUString
const & Selection::getSelectedCID() const
70 return m_aSelectedOID
.getObjectCID();
73 uno::Reference
< drawing::XShape
> const & Selection::getSelectedAdditionalShape() const
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
)
142 SolarMutexGuard aSolarGuard
;
143 pDrawViewWrapper
->UnmarkAll();
145 SdrObject
* pObjectToSelect
= nullptr;
146 if ( m_aSelectedOID
.isAutoGeneratedObject() )
148 pObjectToSelect
= pDrawViewWrapper
->getNamedSdrObject( m_aSelectedOID
.getObjectCID() );
150 else if( m_aSelectedOID
.isAdditionalShape() )
152 pObjectToSelect
= DrawViewWrapper::getSdrObject( m_aSelectedOID
.getAdditionalShape() );
155 impl_selectObject( pObjectToSelect
, *pDrawViewWrapper
);
158 void Selection::adaptSelectionToNewPos( const Point
& rMousePos
, DrawViewWrapper
const * pDrawViewWrapper
159 , bool bIsRightMouse
, bool bWaitingForDoubleClick
)
161 if( !pDrawViewWrapper
)
164 //do not toggle multiclick selection if right clicked on the selected object or waiting for double click
165 bool bAllowMultiClickSelectionChange
= !bIsRightMouse
&& !bWaitingForDoubleClick
;
167 ObjectIdentifier
aLastSelectedObject( m_aSelectedOID
);
169 SolarMutexGuard aSolarGuard
;
171 //bAllowMultiClickSelectionChange==true -> a second click on the same object can lead to a changed selection (e.g. series -> single data point)
173 //get object to select:
175 m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
= ObjectIdentifier();
177 //the search for the object to select starts with the hit object deepest in the grouping hierarchy (a leaf in the tree)
178 //further we travel along the grouping hierarchy from child to parent
179 SdrObject
* pNewObj
= pDrawViewWrapper
->getHitObject(rMousePos
);
180 m_aSelectedOID
= ObjectIdentifier( lcl_getObjectName( pNewObj
) );//name of pNewObj
182 //ignore handle only objects for hit test
183 while( pNewObj
&& m_aSelectedOID
.getObjectCID().match( "HandlesOnly" ) )
185 pNewObj
->SetMarkProtect(true);
186 pNewObj
= pDrawViewWrapper
->getHitObject(rMousePos
);
187 m_aSelectedOID
= ObjectIdentifier( lcl_getObjectName( pNewObj
) );
190 //accept only named objects while searching for the object to select
191 //this call may change m_aSelectedOID
192 if ( SelectionHelper::findNamedParent( pNewObj
, m_aSelectedOID
, true ) )
194 //if the so far found object is a multi click object further steps are necessary
195 while( ObjectIdentifier::isMultiClickObject( m_aSelectedOID
.getObjectCID() ) )
197 bool bSameObjectAsLastSelected
= ( aLastSelectedObject
== m_aSelectedOID
);
198 if( bSameObjectAsLastSelected
)
200 //if the same child is clicked again don't go up further
203 if ( ObjectIdentifier::areSiblings( aLastSelectedObject
.getObjectCID(), m_aSelectedOID
.getObjectCID() ) )
205 //if a sibling of the last selected object is clicked don't go up further
208 ObjectIdentifier aLastChild
= m_aSelectedOID
;
209 if ( !SelectionHelper::findNamedParent( pNewObj
, m_aSelectedOID
, false ) )
211 //take the one found so far
214 //if the last selected object is found don't go up further
215 //but take the last child if selection change is allowed
216 if ( aLastSelectedObject
== m_aSelectedOID
)
218 if( bAllowMultiClickSelectionChange
)
220 m_aSelectedOID
= std::move(aLastChild
);
223 m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
= std::move(aLastChild
);
228 OSL_ENSURE(m_aSelectedOID
.isValid(), "somehow lost selected object");
232 //maybe an additional shape was hit
235 m_aSelectedOID
= ObjectIdentifier( uno::Reference
< drawing::XShape
>( pNewObj
->getUnoShape(), uno::UNO_QUERY
) );
239 m_aSelectedOID
= ObjectIdentifier();
243 if ( !m_aSelectedOID
.isAdditionalShape() )
245 OUString
aPageCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_PAGE
, u
"" ) );//@todo read CID from model
247 if ( !m_aSelectedOID
.isAutoGeneratedObject() )
249 m_aSelectedOID
= ObjectIdentifier( aPageCID
);
252 //check whether the diagram was hit but not selected (e.g. because it has no filling):
253 OUString aDiagramCID
= ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM
, OUString::number( 0 ) );
254 OUString
aWallCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM_WALL
, u
"" ) );//@todo read CID from model
255 bool bBackGroundHit
= m_aSelectedOID
.getObjectCID() == aPageCID
|| m_aSelectedOID
.getObjectCID() == aWallCID
|| !m_aSelectedOID
.isAutoGeneratedObject();
258 //todo: if more than one diagram is available in future do check the list of all diagrams here
259 SdrObject
* pDiagram
= pDrawViewWrapper
->getNamedSdrObject( aDiagramCID
);
262 if( DrawViewWrapper::IsObjectHit( pDiagram
, rMousePos
) )
264 m_aSelectedOID
= ObjectIdentifier( aDiagramCID
);
268 //check whether the legend was hit but not selected (e.g. because it has no filling):
269 if( bBackGroundHit
|| m_aSelectedOID
.getObjectCID() == aDiagramCID
)
271 OUString
aLegendCID( ObjectIdentifier::createClassifiedIdentifierForParticle( ObjectIdentifier::createParticleForLegend(nullptr) ) );//@todo read CID from model
272 SdrObject
* pLegend
= pDrawViewWrapper
->getNamedSdrObject( aLegendCID
);
275 if( DrawViewWrapper::IsObjectHit( pLegend
, rMousePos
) )
277 m_aSelectedOID
= ObjectIdentifier( aLegendCID
);
284 if ( bIsRightMouse
&& m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
.isValid() )
286 m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing
= ObjectIdentifier();
290 bool Selection::isResizeableObjectSelected() const
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 rtl::Reference
<::chart::ChartModel
>& xChartModel
) const
307 return SelectionHelper::isRotateableObject( m_aSelectedOID
.getObjectCID(), xChartModel
);
310 bool Selection::isDragableObjectSelected() const
312 return m_aSelectedOID
.isDragableObject();
315 bool Selection::isTitleObjectSelected() const
317 return m_aSelectedOID
.getObjectType() == OBJECTTYPE_TITLE
;
320 bool Selection::isAdditionalShapeSelected() const
322 return m_aSelectedOID
.isAdditionalShape();
325 bool SelectionHelper::findNamedParent( SdrObject
*& pInOutObject
327 , bool bGivenObjectMayBeResult
)
329 SolarMutexGuard aSolarGuard
;
330 //find the deepest named group
331 SdrObject
* pObj
= pInOutObject
;
333 if( bGivenObjectMayBeResult
)
334 aName
= lcl_getObjectName( pObj
);
336 while( pObj
&& !ObjectIdentifier::isCID( aName
) )
338 SdrObjList
* pObjList
= pObj
->getParentSdrObjListFromSdrObject();
341 SdrObject
* pOwner
= pObjList
->getSdrObjectFromSdrObjList();
345 aName
= lcl_getObjectName( pObj
);
358 bool SelectionHelper::findNamedParent( SdrObject
*& pInOutObject
359 , ObjectIdentifier
& rOutObject
360 , bool bGivenObjectMayBeResult
)
363 if ( findNamedParent( pInOutObject
, aName
, bGivenObjectMayBeResult
) )
365 rOutObject
= ObjectIdentifier( aName
);
371 bool SelectionHelper::isDragableObjectHitTwice( const Point
& rMPos
372 , const OUString
& rNameOfSelectedObject
373 , const DrawViewWrapper
& rDrawViewWrapper
)
375 if(rNameOfSelectedObject
.isEmpty())
377 if( !ObjectIdentifier::isDragableObject(rNameOfSelectedObject
) )
379 SolarMutexGuard aSolarGuard
;
380 SdrObject
* pObj
= rDrawViewWrapper
.getNamedSdrObject( rNameOfSelectedObject
);
381 return DrawViewWrapper::IsObjectHit( pObj
, rMPos
);
384 OUString
SelectionHelper::getHitObjectCID(
386 DrawViewWrapper
const & rDrawViewWrapper
,
387 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
, u
"" ) );//@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
== 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
, u
"" ) );//@todo read CID from model
436 if( aRet
== aWallCID
)
438 aRet
= ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM
, OUString::number( 0 ) );
447 bool SelectionHelper::isRotateableObject( std::u16string_view rCID
448 , const rtl::Reference
<::chart::ChartModel
>& xChartModel
)
450 if( !ObjectIdentifier::isRotateableObject( rCID
) )
453 sal_Int32 nDimensionCount
= xChartModel
->getFirstChartDiagram()->getDimension();
455 return nDimensionCount
== 3;
458 SelectionHelper::SelectionHelper( SdrObject
* pSelectedObj
)
459 : m_pSelectedObj( pSelectedObj
), m_pMarkObj(nullptr)
463 SelectionHelper::~SelectionHelper()
467 bool SelectionHelper::getFrameDragSingles()
469 //true == green == surrounding handles
470 return DynCastE3dObject( m_pSelectedObj
) == nullptr;
473 SdrObject
* SelectionHelper::getMarkHandlesObject( SdrObject
* pObj
)
477 OUString
aName( lcl_getObjectName( pObj
) );
478 if( aName
.match("MarkHandles") || aName
.match("HandlesOnly") )
480 if( !aName
.isEmpty() )//don't get the markhandles of a different object
483 //search for a child with name "MarkHandles" or "HandlesOnly"
484 SolarMutexGuard aSolarGuard
;
485 SdrObjList
* pSubList
= pObj
->GetSubList();
488 SdrObjListIter
aIterator(pSubList
, SdrIterMode::Flat
);
489 while (aIterator
.IsMore())
491 SdrObject
* pMarkHandles
= SelectionHelper::getMarkHandlesObject( aIterator
.Next() );
499 SdrObject
* SelectionHelper::getObjectToMark()
501 //return the selected object itself
502 //or a specific other object if that exists
503 SdrObject
* pObj
= m_pSelectedObj
;
506 //search for a child with name "MarkHandles" or "HandlesOnly"
509 SolarMutexGuard aSolarGuard
;
510 SdrObjList
* pSubList
= pObj
->GetSubList();
513 SdrObjListIter
aIterator(pSubList
, SdrIterMode::Flat
);
514 while (aIterator
.IsMore())
516 SdrObject
* pMarkHandles
= SelectionHelper::getMarkHandlesObject( aIterator
.Next() );
519 m_pMarkObj
= pMarkHandles
;
528 E3dScene
* SelectionHelper::getSceneToRotate( SdrObject
* pObj
)
530 //search whether the object or one of its children is a 3D object
531 //if so, return the accessory 3DScene
533 E3dObject
* pRotateable
= nullptr;
537 pRotateable
= DynCastE3dObject(pObj
);
540 SolarMutexGuard aSolarGuard
;
541 SdrObjList
* pSubList
= pObj
->GetSubList();
544 SdrObjListIter
aIterator(pSubList
, SdrIterMode::DeepWithGroups
);
545 while( aIterator
.IsMore() && !pRotateable
)
547 pRotateable
= DynCastE3dObject(aIterator
.Next());
553 E3dScene
* pScene(nullptr);
557 SolarMutexGuard aSolarGuard
;
558 pScene
= pRotateable
->getRootE3dSceneFromE3dObject();
564 bool SelectionHelper::getMarkHandles( SdrHdlList
& rHdlList
)
566 SolarMutexGuard aSolarGuard
;
568 //@todo -> more flexible handle creation
569 //2 scenarios possible:
570 //1. add an additional invisible shape as a child to the selected object
571 //this child needs to be named somehow and handles need to be generated there from...
572 //or 2. offer a central service per view where renderer and so can register for handle creation for a special shape
573 //.. or 3. feature from drawinglayer to create handles for each shape... (bad performance... ?) ?
575 //scenario 1 is now used:
576 //if a child with name MarkHandles exists
577 //this child is marked instead of the logical selected object
580 //if a special mark object was found
581 //that object should be used for marking only
582 if( m_pMarkObj != m_pSelectedObj)
585 //if a special mark object was found
586 //that object should be used to create handles from
587 if( m_pMarkObj
&& m_pMarkObj
!= m_pSelectedObj
)
590 if( auto pPathObj
= dynamic_cast<const SdrPathObj
*>( m_pMarkObj
) )
592 //if th object is a polygon
593 //from each point a handle is generated
594 const ::basegfx::B2DPolyPolygon
& rPolyPolygon
= pPathObj
->GetPathPoly();
595 for( sal_uInt32 nN
= 0; nN
< rPolyPolygon
.count(); nN
++)
597 const ::basegfx::B2DPolygon
& aPolygon(rPolyPolygon
.getB2DPolygon(nN
));
598 for( sal_uInt32 nM
= 0; nM
< aPolygon
.count(); nM
++)
600 const ::basegfx::B2DPoint
aPoint(aPolygon
.getB2DPoint(nM
));
601 rHdlList
.AddHdl(std::make_unique
<SdrHdl
>(Point(basegfx::fround
<tools::Long
>(aPoint
.getX()), basegfx::fround
<tools::Long
>(aPoint
.getY())), SdrHdlKind::Poly
));
607 return false; //use the special MarkObject for marking
611 //add and document good marking defaults ...
615 SdrObject
* pObj
= m_pSelectedObj
;
618 SdrObjList
* pSubList
= pObj
->GetSubList();
619 if( !pSubList
)//no group object !pObj->IsGroupObject()
622 OUString
aName( lcl_getObjectName( pObj
) );
623 ObjectType
eObjectType( ObjectIdentifier::getObjectType( aName
) );
624 if( eObjectType
== OBJECTTYPE_DATA_POINT
625 || eObjectType
== OBJECTTYPE_DATA_LABEL
626 || eObjectType
== OBJECTTYPE_LEGEND_ENTRY
627 || eObjectType
== OBJECTTYPE_AXIS_UNITLABEL
)
632 SdrObjListIter
aIterator(pSubList
, SdrIterMode::Flat
);
634 while (aIterator
.IsMore())
636 SdrObject
* pSubObj
= aIterator
.Next();
637 if( eObjectType
== OBJECTTYPE_DATA_SERIES
)
639 OUString
aSubName( lcl_getObjectName( pSubObj
) );
640 ObjectType
eSubObjectType( ObjectIdentifier::getObjectType( aSubName
) );
641 if( eSubObjectType
!=OBJECTTYPE_DATA_POINT
)
645 Point aPos
= pSubObj
->GetCurrentBoundRect().Center();
646 rHdlList
.AddHdl(std::make_unique
<SdrHdl
>(aPos
,SdrHdlKind::Poly
));
653 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */