summaryrefslogtreecommitdiffstats
path: root/unoxml/source/events/eventdispatcher.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'unoxml/source/events/eventdispatcher.cxx')
-rw-r--r--unoxml/source/events/eventdispatcher.cxx250
1 files changed, 250 insertions, 0 deletions
diff --git a/unoxml/source/events/eventdispatcher.cxx b/unoxml/source/events/eventdispatcher.cxx
new file mode 100644
index 000000000..ef07dff5a
--- /dev/null
+++ b/unoxml/source/events/eventdispatcher.cxx
@@ -0,0 +1,250 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <eventdispatcher.hxx>
+
+#include <event.hxx>
+#include <mutationevent.hxx>
+#include <uievent.hxx>
+#include <mouseevent.hxx>
+
+#include "../dom/document.hxx"
+
+#include <osl/mutex.hxx>
+
+using namespace css::uno;
+using namespace css::xml::dom;
+using namespace css::xml::dom::events;
+
+namespace DOM::events {
+
+ void CEventDispatcher::addListener(xmlNodePtr pNode, const OUString& aType, const Reference<XEventListener>& aListener, bool bCapture)
+ {
+ TypeListenerMap *const pTMap = bCapture
+ ? (& m_CaptureListeners) : (& m_TargetListeners);
+
+ // get the multimap for the specified type
+ ListenerMap *pMap = nullptr;
+ auto tIter = pTMap->find(aType);
+ if (tIter == pTMap->end()) {
+ // the map has to be created
+ auto const pair = pTMap->emplace(aType, ListenerMap());
+ pMap = & pair.first->second;
+ } else {
+ pMap = & tIter->second;
+ }
+ assert(pMap != nullptr);
+ pMap->emplace(pNode, aListener);
+ }
+
+ void CEventDispatcher::removeListener(xmlNodePtr pNode, const OUString& aType, const Reference<XEventListener>& aListener, bool bCapture)
+ {
+ TypeListenerMap *const pTMap = bCapture
+ ? (& m_CaptureListeners) : (& m_TargetListeners);
+
+ // get the multimap for the specified type
+ auto tIter = pTMap->find(aType);
+ if (tIter != pTMap->end()) {
+ ListenerMap & rMap = tIter->second;
+ // find listeners of specified type for specified node
+ ListenerMap::iterator iter = rMap.find(pNode);
+ while (iter != rMap.end() && iter->first == pNode)
+ {
+ // erase all references to specified listener
+ if (iter->second.is() && iter->second == aListener)
+ {
+ iter = rMap.erase(iter);
+ }
+ else
+ ++iter;
+ }
+ }
+ }
+
+ CEventDispatcher::~CEventDispatcher()
+ {
+ }
+
+ void CEventDispatcher::callListeners(
+ TypeListenerMap const& rTMap,
+ xmlNodePtr const pNode,
+ const OUString& aType, Reference< XEvent > const& xEvent)
+ {
+ // get the multimap for the specified type
+ TypeListenerMap::const_iterator tIter = rTMap.find(aType);
+ if (tIter != rTMap.end()) {
+ ListenerMap const& rMap = tIter->second;
+ auto iterRange = rMap.equal_range(pNode);
+ for( auto iter = iterRange.first; iter != iterRange.second; ++iter )
+ {
+ if(iter->second.is())
+ (iter->second)->handleEvent(xEvent);
+ }
+ }
+ }
+
+ void CEventDispatcher::dispatchEvent(
+ DOM::CDocument & rDocument, ::osl::Mutex & rMutex,
+ xmlNodePtr const pNode, Reference<XNode> const& xNode,
+ Reference< XEvent > const& i_xEvent) const
+ {
+ TypeListenerMap captureListeners;
+ TypeListenerMap targetListeners;
+ {
+ ::osl::MutexGuard g(rMutex);
+
+ captureListeners = m_CaptureListeners;
+ targetListeners = m_TargetListeners;
+ }
+
+ if (captureListeners.empty() && targetListeners.empty())
+ return;
+
+ CEvent *pEvent = nullptr; // pointer to internal event representation
+
+ OUString const aType = i_xEvent->getType();
+ if (aType == "DOMSubtreeModified" ||
+ aType == "DOMNodeInserted" ||
+ aType == "DOMNodeRemoved" ||
+ aType == "DOMNodeRemovedFromDocument" ||
+ aType == "DOMNodeInsertedIntoDocument" ||
+ aType == "DOMAttrModified" ||
+ aType == "DOMCharacterDataModified" )
+ {
+ Reference< XMutationEvent > const aMEvent(i_xEvent,
+ UNO_QUERY_THROW);
+ // dispatch a mutation event
+ // we need to clone the event in order to have complete control
+ // over the implementation
+ CMutationEvent* pMEvent = new CMutationEvent;
+ pMEvent->initMutationEvent(
+ aType, aMEvent->getBubbles(), aMEvent->getCancelable(),
+ aMEvent->getRelatedNode(), aMEvent->getPrevValue(),
+ aMEvent->getNewValue(), aMEvent->getAttrName(),
+ aMEvent->getAttrChange());
+ pEvent = pMEvent;
+ } else if ( // UIEvent
+ aType == "DOMFocusIn" ||
+ aType == "DOMFocusOut" ||
+ aType == "DOMActivate" )
+ {
+ Reference< XUIEvent > const aUIEvent(i_xEvent, UNO_QUERY_THROW);
+ CUIEvent* pUIEvent = new CUIEvent;
+ pUIEvent->initUIEvent(aType,
+ aUIEvent->getBubbles(), aUIEvent->getCancelable(),
+ aUIEvent->getView(), aUIEvent->getDetail());
+ pEvent = pUIEvent;
+ } else if ( // MouseEvent
+ aType == "click" ||
+ aType == "mousedown" ||
+ aType == "mouseup" ||
+ aType == "mouseover" ||
+ aType == "mousemove" ||
+ aType == "mouseout" )
+ {
+ Reference< XMouseEvent > const aMouseEvent(i_xEvent,
+ UNO_QUERY_THROW);
+ CMouseEvent *pMouseEvent = new CMouseEvent;
+ pMouseEvent->initMouseEvent(aType,
+ aMouseEvent->getBubbles(), aMouseEvent->getCancelable(),
+ aMouseEvent->getView(), aMouseEvent->getDetail(),
+ aMouseEvent->getScreenX(), aMouseEvent->getScreenY(),
+ aMouseEvent->getClientX(), aMouseEvent->getClientY(),
+ aMouseEvent->getCtrlKey(), aMouseEvent->getAltKey(),
+ aMouseEvent->getShiftKey(), aMouseEvent->getMetaKey(),
+ aMouseEvent->getButton(), aMouseEvent->getRelatedTarget());
+ pEvent = pMouseEvent;
+ }
+ else // generic event
+ {
+ pEvent = new CEvent;
+ pEvent->initEvent(
+ aType, i_xEvent->getBubbles(), i_xEvent->getCancelable());
+ }
+ pEvent->m_target.set(xNode, UNO_QUERY_THROW);
+ pEvent->m_currentTarget = i_xEvent->getCurrentTarget();
+ pEvent->m_time = i_xEvent->getTimeStamp();
+
+ // create the reference to the private event implementation
+ // that will be dispatched to the listeners
+ Reference< XEvent > const xEvent(pEvent);
+
+ // build the path from target node to the root
+ typedef std::vector< ::std::pair<Reference<XEventTarget>, xmlNodePtr> >
+ NodeVector_t;
+ NodeVector_t captureVector;
+ {
+ ::osl::MutexGuard g(rMutex);
+
+ xmlNodePtr cur = pNode;
+ while (cur != nullptr)
+ {
+ Reference< XEventTarget > const xRef(
+ rDocument.GetCNode(cur).get());
+ captureVector.emplace_back(xRef, cur);
+ cur = cur->parent;
+ }
+ }
+
+ // the capture vector now holds the node path from target to root
+ // first we must search for capture listeners in order root to
+ // to target. after that, any target listeners have to be called
+ // then bubbeling phase listeners are called in target to root
+ // order
+ // start at the root
+ NodeVector_t::const_reverse_iterator rinode =
+ const_cast<NodeVector_t const&>(captureVector).rbegin();
+ if (rinode != const_cast<NodeVector_t const&>(captureVector).rend())
+ {
+ // capturing phase:
+ pEvent->m_phase = PhaseType_CAPTURING_PHASE;
+ while (rinode !=
+ const_cast<NodeVector_t const&>(captureVector).rend())
+ {
+ pEvent->m_currentTarget = rinode->first;
+ callListeners(captureListeners, rinode->second, aType, xEvent);
+ if (pEvent->m_canceled) return;
+ ++rinode;
+ }
+
+ NodeVector_t::const_iterator inode = captureVector.begin();
+
+ // target phase
+ pEvent->m_phase = PhaseType_AT_TARGET;
+ pEvent->m_currentTarget = inode->first;
+ callListeners(targetListeners, inode->second, aType, xEvent);
+ if (pEvent->m_canceled) return;
+ // bubbeling phase
+ ++inode;
+ if (i_xEvent->getBubbles()) {
+ pEvent->m_phase = PhaseType_BUBBLING_PHASE;
+ while (inode != captureVector.end())
+ {
+ pEvent->m_currentTarget = inode->first;
+ callListeners(targetListeners,
+ inode->second, aType, xEvent);
+ if (pEvent->m_canceled) return;
+ ++inode;
+ }
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */