Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / chart2 / source / controller / main / SelectionHelper.cxx
blobc73bcb093a0a49fd2d502eb433edef7a46fd34ce
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
32 namespace chart
34 using namespace ::com::sun::star;
36 namespace
39 OUString lcl_getObjectName( SdrObject const * pObj )
41 if(pObj)
42 return pObj->GetName();
43 return OUString();
46 void impl_selectObject( SdrObject* pObjectToSelect, DrawViewWrapper& rDrawViewWrapper )
48 SolarMutexGuard aSolarGuard;
50 if(pObjectToSelect)
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 );
82 return true;
84 return false;
87 bool Selection::setSelection( const uno::Reference< drawing::XShape >& xShape )
89 if ( !( xShape == m_aSelectedOID.getAdditionalShape() ) )
91 clearSelection();
92 m_aSelectedOID = ObjectIdentifier( xShape );
93 return true;
95 return false;
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();
112 return true;
114 return false;
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
199 break;
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
204 break;
206 ObjectIdentifier aLastChild = m_aSelectedOID;
207 if ( !SelectionHelper::findNamedParent( pNewObj, m_aSelectedOID, false ) )
209 //take the one found so far
210 break;
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;
220 else
221 m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing = aLastChild;
222 break;
226 OSL_ENSURE(m_aSelectedOID.isValid(), "somehow lost selected object");
228 else
230 //maybe an additional shape was hit
231 if ( pNewObj )
233 m_aSelectedOID = ObjectIdentifier( uno::Reference< drawing::XShape >( pNewObj->getUnoShape(), uno::UNO_QUERY ) );
235 else
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();
254 if( bBackGroundHit )
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 );
258 if( pDiagram )
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 );
271 if( pLegend )
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:
298 return true;
299 default:
300 return false;
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
320 , OUString& rOutName
321 , bool bGivenObjectMayBeResult )
323 SolarMutexGuard aSolarGuard;
324 //find the deepest named group
325 SdrObject* pObj = pInOutObject;
326 OUString aName;
327 if( bGivenObjectMayBeResult )
328 aName = lcl_getObjectName( pObj );
330 while( pObj && !ObjectIdentifier::isCID( aName ) )
332 SdrObjList* pObjList = pObj->getParentSdrObjListFromSdrObject();
333 if( !pObjList )
334 return false;
335 SdrObject* pOwner = pObjList->getSdrObjectFromSdrObjList();
336 if( !pOwner )
337 return false;
338 pObj = pOwner;
339 aName = lcl_getObjectName( pObj );
342 if(!pObj)
343 return false;
344 if(aName.isEmpty())
345 return false;
347 pInOutObject = pObj;
348 rOutName = aName;
349 return true;
352 bool SelectionHelper::findNamedParent( SdrObject*& pInOutObject
353 , ObjectIdentifier& rOutObject
354 , bool bGivenObjectMayBeResult )
356 OUString aName;
357 if ( findNamedParent( pInOutObject, aName, bGivenObjectMayBeResult ) )
359 rOutObject = ObjectIdentifier( aName );
360 return true;
362 return false;
365 bool SelectionHelper::isDragableObjectHitTwice( const Point& rMPos
366 , const OUString& rNameOfSelectedObject
367 , const DrawViewWrapper& rDrawViewWrapper )
369 if(rNameOfSelectedObject.isEmpty())
370 return false;
371 if( !ObjectIdentifier::isDragableObject(rNameOfSelectedObject) )
372 return false;
373 SolarMutexGuard aSolarGuard;
374 SdrObject* pObj = rDrawViewWrapper.getNamedSdrObject( rNameOfSelectedObject );
375 return DrawViewWrapper::IsObjectHit( pObj, rMPos );
378 OUString SelectionHelper::getHitObjectCID(
379 const Point& rMPos,
380 DrawViewWrapper const & rDrawViewWrapper,
381 bool bGetDiagramInsteadOf_Wall )
383 SolarMutexGuard aSolarGuard;
384 OUString aRet;
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 ) )
400 aRet.clear();
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 )
407 aRet = aPageCID;
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 );
418 if( pDiagram )
420 if( DrawViewWrapper::IsObjectHit( pDiagram, rMPos ) )
422 aRet = aDiagramCID;
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 ) );
433 aRet = aDiagramCID;
438 return aRet;
439 // \\- solar mutex
442 bool SelectionHelper::isRotateableObject( const OUString& rCID
443 , const uno::Reference< frame::XModel >& xChartModel )
445 if( !ObjectIdentifier::isRotateableObject( rCID ) )
446 return false;
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 )
470 if(!pObj)
471 return nullptr;
472 OUString aName( lcl_getObjectName( pObj ) );
473 if( aName.match("MarkHandles") || aName.match("HandlesOnly") )
474 return pObj;
475 if( !aName.isEmpty() )//don't get the markhandles of a different object
476 return nullptr;
478 //search for a child with name "MarkHandles" or "HandlesOnly"
479 SolarMutexGuard aSolarGuard;
480 SdrObjList* pSubList = pObj->GetSubList();
481 if(pSubList)
483 SdrObjListIter aIterator(pSubList, SdrIterMode::Flat);
484 while (aIterator.IsMore())
486 SdrObject* pMarkHandles = SelectionHelper::getMarkHandlesObject( aIterator.Next() );
487 if( pMarkHandles )
488 return pMarkHandles;
491 return nullptr;
494 SdrObject* SelectionHelper::getObjectToMark()
496 //return the selected object itself
497 //or a specific other object if that exists
498 SdrObject* pObj = m_pSelectedObj;
499 m_pMarkObj = pObj;
501 //search for a child with name "MarkHandles" or "HandlesOnly"
502 if(pObj)
504 SolarMutexGuard aSolarGuard;
505 SdrObjList* pSubList = pObj->GetSubList();
506 if(pSubList)
508 SdrObjListIter aIterator(pSubList, SdrIterMode::Flat);
509 while (aIterator.IsMore())
511 SdrObject* pMarkHandles = SelectionHelper::getMarkHandlesObject( aIterator.Next() );
512 if( pMarkHandles )
514 m_pMarkObj = pMarkHandles;
515 break;
520 return m_pMarkObj;
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;
530 if(pObj)
532 pRotateable = dynamic_cast<E3dObject*>(pObj);
533 if( !pRotateable )
535 SolarMutexGuard aSolarGuard;
536 SdrObjList* pSubList = pObj->GetSubList();
537 if(pSubList)
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);
551 if(pRotateable)
553 SolarMutexGuard aSolarGuard;
554 pScene = pRotateable->getRootE3dSceneFromE3dObject();
557 return pScene;
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)
579 return false;
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)
585 rHdlList.Clear();
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));
600 return true;
602 else
603 return false; //use the special MarkObject for marking
606 //@todo:
607 //add and document good marking defaults ...
609 rHdlList.Clear();
611 SdrObject* pObj = m_pSelectedObj;
612 if(!pObj)
613 return false;
614 SdrObjList* pSubList = pObj->GetSubList();
615 if( !pSubList )//no group object !pObj->IsGroupObject()
616 return false;
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 )
625 return false;
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 )
638 return false;
641 Point aPos = pSubObj->GetCurrentBoundRect().Center();
642 rHdlList.AddHdl(std::make_unique<SdrHdl>(aPos,SdrHdlKind::Poly));
644 return true;
647 } //namespace chart
649 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */