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 <comphelper/processfactory.hxx>
21 #include <comphelper/servicehelper.hxx>
22 #include <tools/diagnose_ex.h>
23 #include <com/sun/star/awt/MouseButton.hpp>
24 #include <com/sun/star/awt/SystemPointer.hpp>
25 #include <com/sun/star/presentation/XShapeEventListener.hpp>
26 #include <com/sun/star/system/SystemShellExecute.hpp>
27 #include <com/sun/star/system/SystemShellExecuteFlags.hpp>
28 #include <com/sun/star/system/XSystemShellExecute.hpp>
29 #include <svx/unoshape.hxx>
30 #include <svx/ImageMapInfo.hxx>
32 #include "shapemanagerimpl.hxx"
37 using namespace css::uno
;
38 using namespace css::drawing
;
39 using namespace css::system
;
41 namespace slideshow::internal
{
43 ShapeManagerImpl::ShapeManagerImpl( EventMultiplexer
& rMultiplexer
,
44 LayerManagerSharedPtr
const& rLayerManager
,
45 CursorManager
& rCursorManager
,
46 const ShapeEventListenerMap
& rGlobalListenersMap
,
47 const ShapeCursorMap
& rGlobalCursorMap
,
48 const Reference
<XDrawPage
>& xDrawPage
):
49 mrMultiplexer(rMultiplexer
),
50 mpLayerManager(rLayerManager
),
51 mrCursorManager(rCursorManager
),
52 mrGlobalListenersMap(rGlobalListenersMap
),
53 mrGlobalCursorMap(rGlobalCursorMap
),
62 void ShapeManagerImpl::activate()
69 // register this handler on EventMultiplexer.
70 // Higher prio (overrides other engine handlers)
71 mrMultiplexer
.addMouseMoveHandler( shared_from_this(), 2.0 );
72 mrMultiplexer
.addClickHandler( shared_from_this(), 2.0 );
73 mrMultiplexer
.addShapeListenerHandler( shared_from_this() );
76 for( const auto& rListener
: mrGlobalListenersMap
)
77 listenerAdded( rListener
.first
);
80 for( const auto& rListener
: mrGlobalCursorMap
)
81 cursorChanged( rListener
.first
, rListener
.second
);
84 mpLayerManager
->activate();
87 void ShapeManagerImpl::deactivate()
95 mpLayerManager
->deactivate();
97 maShapeListenerMap
.clear();
98 maShapeCursorMap
.clear();
100 mrMultiplexer
.removeShapeListenerHandler( shared_from_this() );
101 mrMultiplexer
.removeMouseMoveHandler( shared_from_this() );
102 mrMultiplexer
.removeClickHandler( shared_from_this() );
105 void ShapeManagerImpl::dispose()
107 // remove listeners (EventMultiplexer holds shared_ptr on us)
110 maHyperlinkShapes
.clear();
111 maShapeCursorMap
.clear();
112 maShapeListenerMap
.clear();
113 mpLayerManager
.reset();
116 bool ShapeManagerImpl::handleMousePressed( awt::MouseEvent
const& )
119 return false; // did not handle the event
122 bool ShapeManagerImpl::handleMouseReleased( awt::MouseEvent
const& e
)
124 if( !mbEnabled
|| e
.Buttons
!= awt::MouseButton::LEFT
)
127 basegfx::B2DPoint
const aPosition( e
.X
, e
.Y
);
129 // first check for hyperlinks, because these have
131 OUString
const hyperlink( checkForHyperlink(aPosition
) );
132 if( !hyperlink
.isEmpty() )
134 mrMultiplexer
.notifyHyperlinkClicked(hyperlink
);
135 return true; // event consumed
138 // tdf#74045 Handle ImageMaps
139 OUString
const imageMapLink(checkForImageMap(e
));
140 if (!imageMapLink
.isEmpty())
142 Reference
<XSystemShellExecute
> exec(
143 SystemShellExecute::create(comphelper::getProcessComponentContext()));
144 exec
->execute(imageMapLink
, OUString(), SystemShellExecuteFlags::URIS_ONLY
);
149 // find matching shape (scan reversely, to coarsely match
151 auto aCurrBroadcaster
= std::find_if(maShapeListenerMap
.rbegin(), maShapeListenerMap
.rend(),
152 [&aPosition
](const ShapeToListenersMap::value_type
& rBroadcaster
) {
153 // TODO(F2): Get proper geometry polygon from the
154 // shape, to avoid having areas outside the shape
155 // react on the mouse
156 return rBroadcaster
.first
->getBounds().isInside( aPosition
)
157 && rBroadcaster
.first
->isVisible();
159 if (aCurrBroadcaster
!= maShapeListenerMap
.rend())
161 // shape hit, and shape is visible. Raise
164 std::shared_ptr
<comphelper::OInterfaceContainerHelper2
> const pCont(
165 aCurrBroadcaster
->second
);
166 uno::Reference
<drawing::XShape
> const xShape(
167 aCurrBroadcaster
->first
->getXShape() );
169 // DON'T do anything with /this/ after this point!
170 pCont
->forEach
<presentation::XShapeEventListener
>(
171 [&xShape
, &e
]( const uno::Reference
< presentation::XShapeEventListener
>& rListener
)
172 { return rListener
->click( xShape
, e
); } );
174 return true; // handled this event
177 return false; // did not handle this event
180 bool ShapeManagerImpl::handleMouseDragged( const awt::MouseEvent
& )
183 return false; // did not handle the event
186 bool ShapeManagerImpl::handleMouseMoved( const awt::MouseEvent
& e
)
191 // find hit shape in map
192 const ::basegfx::B2DPoint
aPosition( e
.X
, e
.Y
);
193 sal_Int16
nNewCursor(-1);
195 if( !checkForHyperlink(aPosition
).isEmpty() || !checkForImageMap(e
).isEmpty() )
197 nNewCursor
= awt::SystemPointer::REFHAND
;
201 // find matching shape (scan reversely, to coarsely match
203 auto aCurrCursor
= std::find_if(maShapeCursorMap
.rbegin(), maShapeCursorMap
.rend(),
204 [&aPosition
](const ShapeToCursorMap::value_type
& rCursor
) {
205 // TODO(F2): Get proper geometry polygon from the
206 // shape, to avoid having areas outside the shape
207 // react on the mouse
208 return rCursor
.first
->getBounds().isInside( aPosition
)
209 && rCursor
.first
->isVisible();
211 if (aCurrCursor
!= maShapeCursorMap
.rend())
213 // shape found, and it's visible. set
214 // requested cursor to shape's
215 nNewCursor
= aCurrCursor
->second
;
219 if( nNewCursor
== -1 )
220 mrCursorManager
.resetCursor();
222 mrCursorManager
.requestCursor( nNewCursor
);
224 return false; // we don't /eat/ this event. Lower prio
225 // handler should see it, too.
228 bool ShapeManagerImpl::update()
230 if( mbEnabled
&& mpLayerManager
)
231 return mpLayerManager
->update();
236 bool ShapeManagerImpl::needsUpdate() const
238 if( mbEnabled
&& mpLayerManager
)
239 return mpLayerManager
->isUpdatePending();
244 void ShapeManagerImpl::enterAnimationMode( const AnimatableShapeSharedPtr
& rShape
)
246 if( mbEnabled
&& mpLayerManager
)
247 mpLayerManager
->enterAnimationMode(rShape
);
250 void ShapeManagerImpl::leaveAnimationMode( const AnimatableShapeSharedPtr
& rShape
)
252 if( mbEnabled
&& mpLayerManager
)
253 mpLayerManager
->leaveAnimationMode(rShape
);
256 void ShapeManagerImpl::notifyShapeUpdate( const ShapeSharedPtr
& rShape
)
258 if( mbEnabled
&& mpLayerManager
)
259 mpLayerManager
->notifyShapeUpdate(rShape
);
262 ShapeSharedPtr
ShapeManagerImpl::lookupShape( uno::Reference
< drawing::XShape
> const & xShape
) const
265 return mpLayerManager
->lookupShape(xShape
);
267 return ShapeSharedPtr();
270 const XShapeToShapeMap
& ShapeManagerImpl::getXShapeToShapeMap() const
272 assert( mpLayerManager
);
273 return mpLayerManager
->getXShapeToShapeMap();
276 void ShapeManagerImpl::addHyperlinkArea( const HyperlinkAreaSharedPtr
& rArea
)
278 maHyperlinkShapes
.insert(rArea
);
281 AttributableShapeSharedPtr
ShapeManagerImpl::getSubsetShape( const AttributableShapeSharedPtr
& rOrigShape
,
282 const DocTreeNode
& rTreeNode
)
285 return mpLayerManager
->getSubsetShape(rOrigShape
,rTreeNode
);
287 return AttributableShapeSharedPtr();
290 void ShapeManagerImpl::revokeSubset( const AttributableShapeSharedPtr
& rOrigShape
,
291 const AttributableShapeSharedPtr
& rSubsetShape
)
294 mpLayerManager
->revokeSubset(rOrigShape
,rSubsetShape
);
297 bool ShapeManagerImpl::listenerAdded(
298 const uno::Reference
<drawing::XShape
>& xShape
)
300 ShapeEventListenerMap::const_iterator aIter
;
301 if( (aIter
= mrGlobalListenersMap
.find( xShape
)) ==
302 mrGlobalListenersMap
.end() )
304 ENSURE_OR_RETURN_FALSE(false,
305 "ShapeManagerImpl::listenerAdded(): global "
306 "shape listener map inconsistency!");
309 // is this one of our shapes? other shapes are ignored.
310 ShapeSharedPtr
pShape( lookupShape(xShape
) );
313 maShapeListenerMap
.emplace(pShape
, aIter
->second
);
319 bool ShapeManagerImpl::listenerRemoved( const uno::Reference
<drawing::XShape
>& xShape
)
321 // shape really erased from map? maybe there are other listeners
322 // for the same shape pending...
323 if( mrGlobalListenersMap
.find(xShape
) == mrGlobalListenersMap
.end() )
325 // is this one of our shapes? other shapes are ignored.
326 ShapeSharedPtr
pShape( lookupShape(xShape
) );
328 maShapeListenerMap
.erase(pShape
);
334 void ShapeManagerImpl::cursorChanged( const uno::Reference
<drawing::XShape
>& xShape
,
337 ShapeSharedPtr
pShape( lookupShape(xShape
) );
339 // is this one of our shapes? other shapes are ignored.
343 if( mrGlobalCursorMap
.find(xShape
) == mrGlobalCursorMap
.end() )
345 // erased from global map - erase locally, too
346 maShapeCursorMap
.erase(pShape
);
350 // included in global map - update local one
351 ShapeToCursorMap::iterator aIter
;
352 if( (aIter
= maShapeCursorMap
.find(pShape
))
353 == maShapeCursorMap
.end() )
355 maShapeCursorMap
.emplace(pShape
, nCursor
);
359 aIter
->second
= nCursor
;
364 OUString
ShapeManagerImpl::checkForHyperlink( basegfx::B2DPoint
const& hitPos
) const
366 // find matching region (scan reversely, to coarsely match
367 // paint order): set is ordered by priority
368 AreaSet::const_reverse_iterator
iPos( maHyperlinkShapes
.rbegin() );
369 AreaSet::const_reverse_iterator
const iEnd( maHyperlinkShapes
.rend() );
370 for( ; iPos
!= iEnd
; ++iPos
)
372 HyperlinkAreaSharedPtr
const& pArea
= *iPos
;
374 HyperlinkArea::HyperlinkRegions
const linkRegions(
375 pArea
->getHyperlinkRegions() );
377 for( std::size_t i
= linkRegions
.size(); i
--; )
379 basegfx::B2DRange
const& region
= linkRegions
[i
].first
;
380 if( region
.isInside(hitPos
) )
381 return linkRegions
[i
].second
;
388 OUString
ShapeManagerImpl::checkForImageMap( awt::MouseEvent
const& evt
) const
390 for (sal_Int32 i
= 0; i
< mxDrawPage
->getCount(); i
++)
392 Reference
<XShape
> xShape(mxDrawPage
->getByIndex(i
), UNO_QUERY_THROW
);
393 SdrObject
* pObj
= SdrObject::getSdrObjectFromXShape(xShape
);
396 const IMapObject
* pIMapObj
= SvxIMapInfo::GetHitIMapObject(pObj
, Point(evt
.X
, evt
.Y
));
397 if (pIMapObj
&& !pIMapObj
->GetURL().isEmpty())
399 return pIMapObj
->GetURL();
405 void ShapeManagerImpl::addIntrinsicAnimationHandler( const IntrinsicAnimationEventHandlerSharedPtr
& rHandler
)
407 maIntrinsicAnimationEventHandlers
.add( rHandler
);
410 void ShapeManagerImpl::removeIntrinsicAnimationHandler( const IntrinsicAnimationEventHandlerSharedPtr
& rHandler
)
412 maIntrinsicAnimationEventHandlers
.remove( rHandler
);
415 void ShapeManagerImpl::notifyIntrinsicAnimationsEnabled()
417 maIntrinsicAnimationEventHandlers
.applyAll(
418 std::mem_fn(&IntrinsicAnimationEventHandler::enableAnimations
));
421 void ShapeManagerImpl::notifyIntrinsicAnimationsDisabled()
423 maIntrinsicAnimationEventHandlers
.applyAll(
424 std::mem_fn(&IntrinsicAnimationEventHandler::disableAnimations
));
428 } // namespace slideshow::internal
430 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */