LibreOffice Module slideshow (master) 1
shapemanagerimpl.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
22#include <com/sun/star/awt/MouseButton.hpp>
23#include <com/sun/star/awt/SystemPointer.hpp>
24#include <com/sun/star/system/SystemShellExecute.hpp>
25#include <com/sun/star/system/SystemShellExecuteFlags.hpp>
26#include <com/sun/star/system/XSystemShellExecute.hpp>
27#include <svx/ImageMapInfo.hxx>
28
29#include "shapemanagerimpl.hxx"
30
31#include <functional>
32#include <utility>
33
34using namespace css;
35using namespace css::uno;
36using namespace css::drawing;
37using namespace css::system;
38
39namespace slideshow::internal {
40
41ShapeManagerImpl::ShapeManagerImpl( EventMultiplexer& rMultiplexer,
42 LayerManagerSharedPtr xLayerManager,
43 CursorManager& rCursorManager,
44 const ShapeEventListenerMap& rGlobalListenersMap,
45 const ShapeCursorMap& rGlobalCursorMap,
46 const Reference<XDrawPage>& xDrawPage ):
47 mrMultiplexer(rMultiplexer),
48 mpLayerManager(std::move(xLayerManager)),
49 mrCursorManager(rCursorManager),
50 mrGlobalListenersMap(rGlobalListenersMap),
51 mrGlobalCursorMap(rGlobalCursorMap),
52 maShapeListenerMap(),
53 maShapeCursorMap(),
54 maHyperlinkShapes(),
55 mbEnabled(false),
56 mxDrawPage(xDrawPage)
57{
58}
59
60void ShapeManagerImpl::activate()
61{
62 if( mbEnabled )
63 return;
64
65 mbEnabled = true;
66
67 // register this handler on EventMultiplexer.
68 // Higher prio (overrides other engine handlers)
69 mrMultiplexer.addMouseMoveHandler( shared_from_this(), 2.0 );
70 mrMultiplexer.addClickHandler( shared_from_this(), 2.0 );
71 mrMultiplexer.addShapeListenerHandler( shared_from_this() );
72
73 // clone listener map
74 for( const auto& rListener : mrGlobalListenersMap )
75 listenerAdded( rListener.first );
76
77 // clone cursor map
78 for( const auto& rListener : mrGlobalCursorMap )
79 cursorChanged( rListener.first, rListener.second );
80
81 if( mpLayerManager )
82 mpLayerManager->activate();
83}
84
85void ShapeManagerImpl::deactivate()
86{
87 if( !mbEnabled )
88 return;
89
90 mbEnabled = false;
91
92 if( mpLayerManager )
93 mpLayerManager->deactivate();
94
95 maShapeListenerMap.clear();
96 maShapeCursorMap.clear();
97
98 mrMultiplexer.removeShapeListenerHandler( shared_from_this() );
99 mrMultiplexer.removeMouseMoveHandler( shared_from_this() );
100 mrMultiplexer.removeClickHandler( shared_from_this() );
101}
102
103void ShapeManagerImpl::dispose()
104{
105 // remove listeners (EventMultiplexer holds shared_ptr on us)
106 deactivate();
107
108 maHyperlinkShapes.clear();
109 maShapeCursorMap.clear();
110 maShapeListenerMap.clear();
111 mpLayerManager.reset();
112}
113
114bool ShapeManagerImpl::handleMousePressed( awt::MouseEvent const& )
115{
116 // not used here
117 return false; // did not handle the event
118}
119
120bool ShapeManagerImpl::handleMouseReleased( awt::MouseEvent const& e )
121{
122 if( !mbEnabled || e.Buttons != awt::MouseButton::LEFT)
123 return false;
124
125 basegfx::B2DPoint const aPosition( e.X, e.Y );
126
127 // first check for hyperlinks, because these have
128 // highest prio:
129 OUString const hyperlink( checkForHyperlink(aPosition) );
130 if( !hyperlink.isEmpty() )
131 {
132 mrMultiplexer.notifyHyperlinkClicked(hyperlink);
133 return true; // event consumed
134 }
135
136 // tdf#74045 Handle ImageMaps
137 OUString const imageMapLink(checkForImageMap(e));
138 if (!imageMapLink.isEmpty())
139 {
140 Reference<XSystemShellExecute> exec(
141 SystemShellExecute::create(comphelper::getProcessComponentContext()));
142 exec->execute(imageMapLink, OUString(), SystemShellExecuteFlags::URIS_ONLY);
143
144 return true;
145 }
146
147 // find matching shape (scan reversely, to coarsely match
148 // paint order)
149 auto aCurrBroadcaster = std::find_if(maShapeListenerMap.rbegin(), maShapeListenerMap.rend(),
150 [&aPosition](const ShapeToListenersMap::value_type& rBroadcaster) {
151 // TODO(F2): Get proper geometry polygon from the
152 // shape, to avoid having areas outside the shape
153 // react on the mouse
154 return rBroadcaster.first->getBounds().isInside( aPosition )
155 && rBroadcaster.first->isVisible();
156 });
157 if (aCurrBroadcaster != maShapeListenerMap.rend())
158 {
159 // shape hit, and shape is visible. Raise
160 // event.
161
162 std::shared_ptr<comphelper::OInterfaceContainerHelper3<css::presentation::XShapeEventListener>> const & pCont =
163 aCurrBroadcaster->second;
165 aCurrBroadcaster->first->getXShape() );
166
167 // DON'T do anything with /this/ after this point!
168 pCont->forEach(
169 [&xShape, &e]( const uno::Reference< presentation::XShapeEventListener >& rListener )
170 { return rListener->click( xShape, e ); } );
171
172 return true; // handled this event
173 }
174
175 return false; // did not handle this event
176}
177
178bool ShapeManagerImpl::handleMouseDragged( const awt::MouseEvent& )
179{
180 // not used here
181 return false; // did not handle the event
182}
183
184bool ShapeManagerImpl::handleMouseMoved( const awt::MouseEvent& e )
185{
186 if( !mbEnabled )
187 return false;
188
189 // find hit shape in map
190 const ::basegfx::B2DPoint aPosition( e.X, e.Y );
191 sal_Int16 nNewCursor(-1);
192
193 if( !checkForHyperlink(aPosition).isEmpty() || !checkForImageMap(e).isEmpty() )
194 {
195 nNewCursor = awt::SystemPointer::REFHAND;
196 }
197 else
198 {
199 // find matching shape (scan reversely, to coarsely match
200 // paint order)
201 auto aCurrCursor = std::find_if(maShapeCursorMap.rbegin(), maShapeCursorMap.rend(),
202 [&aPosition](const ShapeToCursorMap::value_type& rCursor) {
203 // TODO(F2): Get proper geometry polygon from the
204 // shape, to avoid having areas outside the shape
205 // react on the mouse
206 return rCursor.first->getBounds().isInside( aPosition )
207 && rCursor.first->isVisible();
208 });
209 if (aCurrCursor != maShapeCursorMap.rend())
210 {
211 // shape found, and it's visible. set
212 // requested cursor to shape's
213 nNewCursor = aCurrCursor->second;
214 }
215 }
216
217 if( nNewCursor == -1 )
218 mrCursorManager.resetCursor();
219 else
220 mrCursorManager.requestCursor( nNewCursor );
221
222 return false; // we don't /eat/ this event. Lower prio
223 // handler should see it, too.
224}
225
226bool ShapeManagerImpl::update()
227{
228 if( mbEnabled && mpLayerManager )
229 return mpLayerManager->update();
230
231 return false;
232}
233
234bool ShapeManagerImpl::needsUpdate() const
235{
236 if( mbEnabled && mpLayerManager )
237 return mpLayerManager->isUpdatePending();
238
239 return false;
240}
241
242void ShapeManagerImpl::enterAnimationMode( const AnimatableShapeSharedPtr& rShape )
243{
244 if( mbEnabled && mpLayerManager )
245 mpLayerManager->enterAnimationMode(rShape);
246}
247
248void ShapeManagerImpl::leaveAnimationMode( const AnimatableShapeSharedPtr& rShape )
249{
250 if( mbEnabled && mpLayerManager )
251 mpLayerManager->leaveAnimationMode(rShape);
252}
253
254void ShapeManagerImpl::notifyShapeUpdate( const ShapeSharedPtr& rShape )
255{
256 if( mbEnabled && mpLayerManager )
257 mpLayerManager->notifyShapeUpdate(rShape);
258}
259
260ShapeSharedPtr ShapeManagerImpl::lookupShape( uno::Reference< drawing::XShape > const & xShape ) const
261{
262 if( mpLayerManager )
263 return mpLayerManager->lookupShape(xShape);
264
265 return ShapeSharedPtr();
266}
267
268const XShapeToShapeMap& ShapeManagerImpl::getXShapeToShapeMap() const
269{
270 assert( mpLayerManager );
271 return mpLayerManager->getXShapeToShapeMap();
272}
273
274void ShapeManagerImpl::addHyperlinkArea( const HyperlinkAreaSharedPtr& rArea )
275{
276 maHyperlinkShapes.insert(rArea);
277}
278
279AttributableShapeSharedPtr ShapeManagerImpl::getSubsetShape( const AttributableShapeSharedPtr& rOrigShape,
280 const DocTreeNode& rTreeNode )
281{
282 if( mpLayerManager )
283 return mpLayerManager->getSubsetShape(rOrigShape,rTreeNode);
284
286}
287
288void ShapeManagerImpl::revokeSubset( const AttributableShapeSharedPtr& rOrigShape,
289 const AttributableShapeSharedPtr& rSubsetShape )
290{
291 if( mpLayerManager )
292 mpLayerManager->revokeSubset(rOrigShape,rSubsetShape);
293}
294
295bool ShapeManagerImpl::listenerAdded(
296 const uno::Reference<drawing::XShape>& xShape )
297{
298 ShapeEventListenerMap::const_iterator aIter = mrGlobalListenersMap.find( xShape );
299 if( aIter == mrGlobalListenersMap.end() )
300 {
302 "ShapeManagerImpl::listenerAdded(): global "
303 "shape listener map inconsistency!");
304 }
305
306 // is this one of our shapes? other shapes are ignored.
307 ShapeSharedPtr pShape( lookupShape(xShape) );
308 if( pShape )
309 {
310 maShapeListenerMap.emplace(pShape, aIter->second);
311 }
312
313 return true;
314}
315
316bool ShapeManagerImpl::listenerRemoved( const uno::Reference<drawing::XShape>& xShape )
317{
318 // shape really erased from map? maybe there are other listeners
319 // for the same shape pending...
320 if( mrGlobalListenersMap.find(xShape) == mrGlobalListenersMap.end() )
321 {
322 // is this one of our shapes? other shapes are ignored.
323 ShapeSharedPtr pShape( lookupShape(xShape) );
324 if( pShape )
325 maShapeListenerMap.erase(pShape);
326 }
327
328 return true;
329}
330
331void ShapeManagerImpl::cursorChanged( const uno::Reference<drawing::XShape>& xShape,
332 sal_Int16 nCursor )
333{
334 ShapeSharedPtr pShape( lookupShape(xShape) );
335
336 // is this one of our shapes? other shapes are ignored.
337 if( !pShape )
338 return;
339
340 if( mrGlobalCursorMap.find(xShape) == mrGlobalCursorMap.end() )
341 {
342 // erased from global map - erase locally, too
343 maShapeCursorMap.erase(pShape);
344 }
345 else
346 {
347 // included in global map - update local one
348 ShapeToCursorMap::iterator aIter;
349 if( (aIter = maShapeCursorMap.find(pShape))
350 == maShapeCursorMap.end() )
351 {
352 maShapeCursorMap.emplace(pShape, nCursor);
353 }
354 else
355 {
356 aIter->second = nCursor;
357 }
358 }
359}
360
361OUString ShapeManagerImpl::checkForHyperlink( basegfx::B2DPoint const& hitPos ) const
362{
363 // find matching region (scan reversely, to coarsely match
364 // paint order): set is ordered by priority
365 AreaSet::const_reverse_iterator iPos( maHyperlinkShapes.rbegin() );
366 AreaSet::const_reverse_iterator const iEnd( maHyperlinkShapes.rend() );
367 for( ; iPos != iEnd; ++iPos )
368 {
369 HyperlinkAreaSharedPtr const& pArea = *iPos;
370
371 HyperlinkArea::HyperlinkRegions const linkRegions(
372 pArea->getHyperlinkRegions() );
373
374 for( std::size_t i = linkRegions.size(); i--; )
375 {
376 basegfx::B2DRange const& region = linkRegions[i].first;
377 if( region.isInside(hitPos) )
378 return linkRegions[i].second;
379 }
380 }
381
382 return OUString();
383}
384
385OUString ShapeManagerImpl::checkForImageMap( awt::MouseEvent const& evt ) const
386{
387 for (sal_Int32 i = 0; i < mxDrawPage->getCount(); i++)
388 {
389 Reference<XShape> xShape(mxDrawPage->getByIndex(i), UNO_QUERY_THROW);
391 if (!pObj)
392 continue;
393 const IMapObject* pIMapObj = SvxIMapInfo::GetHitIMapObject(pObj, Point(evt.X, evt.Y));
394 if (pIMapObj && !pIMapObj->GetURL().isEmpty())
395 {
396 return pIMapObj->GetURL();
397 }
398 }
399 return OUString();
400}
401
402void ShapeManagerImpl::addIntrinsicAnimationHandler( const IntrinsicAnimationEventHandlerSharedPtr& rHandler )
403{
404 maIntrinsicAnimationEventHandlers.add( rHandler );
405}
406
407void ShapeManagerImpl::removeIntrinsicAnimationHandler( const IntrinsicAnimationEventHandlerSharedPtr& rHandler )
408{
409 maIntrinsicAnimationEventHandlers.remove( rHandler );
410}
411
412void ShapeManagerImpl::notifyIntrinsicAnimationsEnabled()
413{
414 maIntrinsicAnimationEventHandlers.applyAll(
415 std::mem_fn(&IntrinsicAnimationEventHandler::enableAnimations));
416}
417
418void ShapeManagerImpl::notifyIntrinsicAnimationsDisabled()
419{
420 maIntrinsicAnimationEventHandlers.applyAll(
421 std::mem_fn(&IntrinsicAnimationEventHandler::disableAnimations));
422}
423
424
425} // namespace slideshow::internal
426
427/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const OUString & GetURL() const
static SdrObject * getSdrObjectFromXShape(const css::uno::Reference< css::uno::XInterface > &xInt)
static IMapObject * GetHitIMapObject(const SdrObject *pObj, const Point &rWinPoint, const OutputDevice *pCmpWnd=nullptr)
bool isInside(const Tuple2D< TYPE > &rTuple) const
This class represents kind of a DOM tree node for shape text.
Definition: doctreenode.hxx:45
std::vector< HyperlinkRegion > HyperlinkRegions
ShapeManagerImpl(EventMultiplexer &rMultiplexer, LayerManagerSharedPtr xLayerManager, CursorManager &rCursorManager, const ShapeEventListenerMap &rGlobalListenersMap, const ShapeCursorMap &rGlobalCursorMap, const css::uno::Reference< css::drawing::XDrawPage > &xDrawPage)
Create a shape event broadcaster.
#define ENSURE_OR_RETURN_FALSE(c, m)
Reference< XComponentContext > getProcessComponentContext()
int i
::std::shared_ptr< AnimatableShape > AnimatableShapeSharedPtr
std::shared_ptr< HyperlinkArea > HyperlinkAreaSharedPtr
::std::shared_ptr< LayerManager > LayerManagerSharedPtr
::std::map< css::uno::Reference< css::drawing::XShape >, sal_Int16 > ShapeCursorMap
Maps XShape to mouse cursor.
Definition: shapemaps.hxx:43
::std::shared_ptr< IntrinsicAnimationEventHandler > IntrinsicAnimationEventHandlerSharedPtr
::std::map< css::uno::Reference< css::drawing::XShape >, std::shared_ptr< ::comphelper::OInterfaceContainerHelper3< css::presentation::XShapeEventListener > > > ShapeEventListenerMap
Maps XShape to shape listener.
Definition: shapemaps.hxx:39
::std::shared_ptr< AttributableShape > AttributableShapeSharedPtr
std::unordered_map< css::uno::Reference< css::drawing::XShape >, ShapeSharedPtr, hash< css::uno::Reference< css::drawing::XShape > > > XShapeToShapeMap
A hash map which maps the XShape to the corresponding Shape object.
::std::shared_ptr< Shape > ShapeSharedPtr
CursorManager & mrCursorManager
parent cursor manager
Definition: slideimpl.cxx:206
uno::Reference< drawing::XDrawPage > mxDrawPage
The page model object.
Definition: slideimpl.cxx:193
LayerManagerSharedPtr mpLayerManager
Definition: slideimpl.cxx:197
EventMultiplexer & mrMultiplexer