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 <eventdispatcher.hxx>
23 #include <mutationevent.hxx>
24 #include <uievent.hxx>
25 #include <mouseevent.hxx>
27 #include "../dom/document.hxx"
29 #include <osl/mutex.hxx>
31 using namespace css::uno
;
32 using namespace css::xml::dom
;
33 using namespace css::xml::dom::events
;
35 namespace DOM::events
{
37 void CEventDispatcher::addListener(xmlNodePtr pNode
, const OUString
& aType
, const Reference
<XEventListener
>& aListener
, bool bCapture
)
39 TypeListenerMap
*const pTMap
= bCapture
40 ? (& m_CaptureListeners
) : (& m_TargetListeners
);
42 // get the multimap for the specified type
43 ListenerMap
*pMap
= nullptr;
44 auto tIter
= pTMap
->find(aType
);
45 if (tIter
== pTMap
->end()) {
46 // the map has to be created
47 auto const pair
= pTMap
->emplace(aType
, ListenerMap());
48 pMap
= & pair
.first
->second
;
50 pMap
= & tIter
->second
;
52 assert(pMap
!= nullptr);
53 pMap
->emplace(pNode
, aListener
);
56 void CEventDispatcher::removeListener(xmlNodePtr pNode
, const OUString
& aType
, const Reference
<XEventListener
>& aListener
, bool bCapture
)
58 TypeListenerMap
*const pTMap
= bCapture
59 ? (& m_CaptureListeners
) : (& m_TargetListeners
);
61 // get the multimap for the specified type
62 auto tIter
= pTMap
->find(aType
);
63 if (tIter
== pTMap
->end())
66 ListenerMap
& rMap
= tIter
->second
;
67 // find listeners of specified type for specified node
68 ListenerMap::iterator iter
= rMap
.find(pNode
);
69 while (iter
!= rMap
.end() && iter
->first
== pNode
)
71 // erase all references to specified listener
72 if (iter
->second
.is() && iter
->second
== aListener
)
74 iter
= rMap
.erase(iter
);
81 CEventDispatcher::~CEventDispatcher()
85 void CEventDispatcher::callListeners(
86 TypeListenerMap
const& rTMap
,
87 xmlNodePtr
const pNode
,
88 const OUString
& aType
, Reference
< XEvent
> const& xEvent
)
90 // get the multimap for the specified type
91 TypeListenerMap::const_iterator tIter
= rTMap
.find(aType
);
92 if (tIter
!= rTMap
.end()) {
93 ListenerMap
const& rMap
= tIter
->second
;
94 auto iterRange
= rMap
.equal_range(pNode
);
95 for( auto iter
= iterRange
.first
; iter
!= iterRange
.second
; ++iter
)
98 (iter
->second
)->handleEvent(xEvent
);
103 void CEventDispatcher::dispatchEvent(
104 DOM::CDocument
& rDocument
, ::osl::Mutex
& rMutex
,
105 xmlNodePtr
const pNode
, Reference
<XNode
> const& xNode
,
106 Reference
< XEvent
> const& i_xEvent
) const
108 TypeListenerMap captureListeners
;
109 TypeListenerMap targetListeners
;
111 ::osl::MutexGuard
g(rMutex
);
113 captureListeners
= m_CaptureListeners
;
114 targetListeners
= m_TargetListeners
;
117 if (captureListeners
.empty() && targetListeners
.empty())
120 rtl::Reference
<CEvent
> pEvent
; // pointer to internal event representation
122 OUString
const aType
= i_xEvent
->getType();
123 if (aType
== "DOMSubtreeModified" ||
124 aType
== "DOMNodeInserted" ||
125 aType
== "DOMNodeRemoved" ||
126 aType
== "DOMNodeRemovedFromDocument" ||
127 aType
== "DOMNodeInsertedIntoDocument" ||
128 aType
== "DOMAttrModified" ||
129 aType
== "DOMCharacterDataModified" )
131 Reference
< XMutationEvent
> const aMEvent(i_xEvent
,
133 // dispatch a mutation event
134 // we need to clone the event in order to have complete control
135 // over the implementation
136 rtl::Reference
<CMutationEvent
> pMEvent
= new CMutationEvent
;
137 pMEvent
->initMutationEvent(
138 aType
, aMEvent
->getBubbles(), aMEvent
->getCancelable(),
139 aMEvent
->getRelatedNode(), aMEvent
->getPrevValue(),
140 aMEvent
->getNewValue(), aMEvent
->getAttrName(),
141 aMEvent
->getAttrChange());
143 } else if ( // UIEvent
144 aType
== "DOMFocusIn" ||
145 aType
== "DOMFocusOut" ||
146 aType
== "DOMActivate" )
148 Reference
< XUIEvent
> const aUIEvent(i_xEvent
, UNO_QUERY_THROW
);
149 rtl::Reference
<CUIEvent
> pUIEvent
= new CUIEvent
;
150 pUIEvent
->initUIEvent(aType
,
151 aUIEvent
->getBubbles(), aUIEvent
->getCancelable(),
152 aUIEvent
->getView(), aUIEvent
->getDetail());
154 } else if ( // MouseEvent
156 aType
== "mousedown" ||
157 aType
== "mouseup" ||
158 aType
== "mouseover" ||
159 aType
== "mousemove" ||
160 aType
== "mouseout" )
162 Reference
< XMouseEvent
> const aMouseEvent(i_xEvent
,
164 rtl::Reference
<CMouseEvent
> pMouseEvent
= new CMouseEvent
;
165 pMouseEvent
->initMouseEvent(aType
,
166 aMouseEvent
->getBubbles(), aMouseEvent
->getCancelable(),
167 aMouseEvent
->getView(), aMouseEvent
->getDetail(),
168 aMouseEvent
->getScreenX(), aMouseEvent
->getScreenY(),
169 aMouseEvent
->getClientX(), aMouseEvent
->getClientY(),
170 aMouseEvent
->getCtrlKey(), aMouseEvent
->getAltKey(),
171 aMouseEvent
->getShiftKey(), aMouseEvent
->getMetaKey(),
172 aMouseEvent
->getButton(), aMouseEvent
->getRelatedTarget());
173 pEvent
= pMouseEvent
;
175 else // generic event
179 aType
, i_xEvent
->getBubbles(), i_xEvent
->getCancelable());
181 pEvent
->m_target
.set(xNode
, UNO_QUERY_THROW
);
182 pEvent
->m_currentTarget
= i_xEvent
->getCurrentTarget();
183 pEvent
->m_time
= i_xEvent
->getTimeStamp();
185 // create the reference to the private event implementation
186 // that will be dispatched to the listeners
187 Reference
< XEvent
> const xEvent(pEvent
);
189 // build the path from target node to the root
190 typedef std::vector
< ::std::pair
<Reference
<XEventTarget
>, xmlNodePtr
> >
192 NodeVector_t captureVector
;
194 ::osl::MutexGuard
g(rMutex
);
196 xmlNodePtr cur
= pNode
;
197 while (cur
!= nullptr)
199 Reference
< XEventTarget
> const xRef(
200 rDocument
.GetCNode(cur
));
201 captureVector
.emplace_back(xRef
, cur
);
206 // the capture vector now holds the node path from target to root
207 // first we must search for capture listeners in order root to
208 // to target. after that, any target listeners have to be called
209 // then bubbeling phase listeners are called in target to root
212 NodeVector_t::const_reverse_iterator rinode
=
213 const_cast<NodeVector_t
const&>(captureVector
).rbegin();
214 if (rinode
== const_cast<NodeVector_t
const&>(captureVector
).rend())
218 pEvent
->m_phase
= PhaseType_CAPTURING_PHASE
;
220 const_cast<NodeVector_t
const&>(captureVector
).rend())
222 pEvent
->m_currentTarget
= rinode
->first
;
223 callListeners(captureListeners
, rinode
->second
, aType
, xEvent
);
224 if (pEvent
->m_canceled
) return;
228 NodeVector_t::const_iterator inode
= captureVector
.begin();
231 pEvent
->m_phase
= PhaseType_AT_TARGET
;
232 pEvent
->m_currentTarget
= inode
->first
;
233 callListeners(targetListeners
, inode
->second
, aType
, xEvent
);
234 if (pEvent
->m_canceled
) return;
237 if (i_xEvent
->getBubbles()) {
238 pEvent
->m_phase
= PhaseType_BUBBLING_PHASE
;
239 while (inode
!= captureVector
.end())
241 pEvent
->m_currentTarget
= inode
->first
;
242 callListeners(targetListeners
,
243 inode
->second
, aType
, xEvent
);
244 if (pEvent
->m_canceled
) return;
251 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */