tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / chart2 / source / controller / main / SelectionHelper.cxx
blob8a6305c6f6c2df33ae49e5534ed1320986c96411
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 <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>
33 namespace chart
35 using namespace ::com::sun::star;
37 namespace
40 OUString lcl_getObjectName( SdrObject const * pObj )
42 if(pObj)
43 return pObj->GetName();
44 return OUString();
47 void impl_selectObject( SdrObject* pObjectToSelect, DrawViewWrapper& rDrawViewWrapper )
49 SolarMutexGuard aSolarGuard;
51 if(pObjectToSelect)
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 );
83 return true;
85 return false;
88 bool Selection::setSelection( const uno::Reference< drawing::XShape >& xShape )
90 if ( !( xShape == m_aSelectedOID.getAdditionalShape() ) )
92 clearSelection();
93 m_aSelectedOID = ObjectIdentifier( xShape );
94 return true;
96 return false;
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();
113 return true;
115 return false;
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 )
139 return;
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 )
162 return;
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
201 break;
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
206 break;
208 ObjectIdentifier aLastChild = m_aSelectedOID;
209 if ( !SelectionHelper::findNamedParent( pNewObj, m_aSelectedOID, false ) )
211 //take the one found so far
212 break;
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);
222 else
223 m_aSelectedOID_selectOnlyIfNoDoubleClickIsFollowing = std::move(aLastChild);
224 break;
228 OSL_ENSURE(m_aSelectedOID.isValid(), "somehow lost selected object");
230 else
232 //maybe an additional shape was hit
233 if ( pNewObj )
235 m_aSelectedOID = ObjectIdentifier( uno::Reference< drawing::XShape >( pNewObj->getUnoShape(), uno::UNO_QUERY ) );
237 else
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();
256 if( bBackGroundHit )
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 );
260 if( pDiagram )
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 );
273 if( pLegend )
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:
299 return true;
300 default:
301 return false;
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
326 , OUString& rOutName
327 , bool bGivenObjectMayBeResult )
329 SolarMutexGuard aSolarGuard;
330 //find the deepest named group
331 SdrObject* pObj = pInOutObject;
332 OUString aName;
333 if( bGivenObjectMayBeResult )
334 aName = lcl_getObjectName( pObj );
336 while( pObj && !ObjectIdentifier::isCID( aName ) )
338 SdrObjList* pObjList = pObj->getParentSdrObjListFromSdrObject();
339 if( !pObjList )
340 return false;
341 SdrObject* pOwner = pObjList->getSdrObjectFromSdrObjList();
342 if( !pOwner )
343 return false;
344 pObj = pOwner;
345 aName = lcl_getObjectName( pObj );
348 if(!pObj)
349 return false;
350 if(aName.isEmpty())
351 return false;
353 pInOutObject = pObj;
354 rOutName = aName;
355 return true;
358 bool SelectionHelper::findNamedParent( SdrObject*& pInOutObject
359 , ObjectIdentifier& rOutObject
360 , bool bGivenObjectMayBeResult )
362 OUString aName;
363 if ( findNamedParent( pInOutObject, aName, bGivenObjectMayBeResult ) )
365 rOutObject = ObjectIdentifier( aName );
366 return true;
368 return false;
371 bool SelectionHelper::isDragableObjectHitTwice( const Point& rMPos
372 , const OUString& rNameOfSelectedObject
373 , const DrawViewWrapper& rDrawViewWrapper )
375 if(rNameOfSelectedObject.isEmpty())
376 return false;
377 if( !ObjectIdentifier::isDragableObject(rNameOfSelectedObject) )
378 return false;
379 SolarMutexGuard aSolarGuard;
380 SdrObject* pObj = rDrawViewWrapper.getNamedSdrObject( rNameOfSelectedObject );
381 return DrawViewWrapper::IsObjectHit( pObj, rMPos );
384 OUString SelectionHelper::getHitObjectCID(
385 const Point& rMPos,
386 DrawViewWrapper const & rDrawViewWrapper,
387 bool bGetDiagramInsteadOf_Wall )
389 SolarMutexGuard aSolarGuard;
390 OUString aRet;
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 ) )
406 aRet.clear();
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 )
413 aRet = aPageCID;
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 );
424 if( pDiagram )
426 if( DrawViewWrapper::IsObjectHit( pDiagram, rMPos ) )
428 aRet = aDiagramCID;
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 ) );
443 return aRet;
444 // \\- solar mutex
447 bool SelectionHelper::isRotateableObject( std::u16string_view rCID
448 , const rtl::Reference<::chart::ChartModel>& xChartModel )
450 if( !ObjectIdentifier::isRotateableObject( rCID ) )
451 return false;
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 )
475 if(!pObj)
476 return nullptr;
477 OUString aName( lcl_getObjectName( pObj ) );
478 if( aName.match("MarkHandles") || aName.match("HandlesOnly") )
479 return pObj;
480 if( !aName.isEmpty() )//don't get the markhandles of a different object
481 return nullptr;
483 //search for a child with name "MarkHandles" or "HandlesOnly"
484 SolarMutexGuard aSolarGuard;
485 SdrObjList* pSubList = pObj->GetSubList();
486 if(pSubList)
488 SdrObjListIter aIterator(pSubList, SdrIterMode::Flat);
489 while (aIterator.IsMore())
491 SdrObject* pMarkHandles = SelectionHelper::getMarkHandlesObject( aIterator.Next() );
492 if( pMarkHandles )
493 return pMarkHandles;
496 return nullptr;
499 SdrObject* SelectionHelper::getObjectToMark()
501 //return the selected object itself
502 //or a specific other object if that exists
503 SdrObject* pObj = m_pSelectedObj;
504 m_pMarkObj = pObj;
506 //search for a child with name "MarkHandles" or "HandlesOnly"
507 if(pObj)
509 SolarMutexGuard aSolarGuard;
510 SdrObjList* pSubList = pObj->GetSubList();
511 if(pSubList)
513 SdrObjListIter aIterator(pSubList, SdrIterMode::Flat);
514 while (aIterator.IsMore())
516 SdrObject* pMarkHandles = SelectionHelper::getMarkHandlesObject( aIterator.Next() );
517 if( pMarkHandles )
519 m_pMarkObj = pMarkHandles;
520 break;
525 return m_pMarkObj;
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;
535 if(pObj)
537 pRotateable = DynCastE3dObject(pObj);
538 if( !pRotateable )
540 SolarMutexGuard aSolarGuard;
541 SdrObjList* pSubList = pObj->GetSubList();
542 if(pSubList)
544 SdrObjListIter aIterator(pSubList, SdrIterMode::DeepWithGroups);
545 while( aIterator.IsMore() && !pRotateable )
547 pRotateable = DynCastE3dObject(aIterator.Next());
553 E3dScene* pScene(nullptr);
555 if(pRotateable)
557 SolarMutexGuard aSolarGuard;
558 pScene = pRotateable->getRootE3dSceneFromE3dObject();
561 return pScene;
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)
583 return false;
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)
589 rHdlList.Clear();
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));
604 return true;
606 else
607 return false; //use the special MarkObject for marking
610 //@todo:
611 //add and document good marking defaults ...
613 rHdlList.Clear();
615 SdrObject* pObj = m_pSelectedObj;
616 if(!pObj)
617 return false;
618 SdrObjList* pSubList = pObj->GetSubList();
619 if( !pSubList )//no group object !pObj->IsGroupObject()
620 return false;
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 )
629 return false;
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 )
642 return false;
645 Point aPos = pSubObj->GetCurrentBoundRect().Center();
646 rHdlList.AddHdl(std::make_unique<SdrHdl>(aPos,SdrHdlKind::Poly));
648 return true;
651 } //namespace chart
653 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */