/* -*- 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 #include #include #include #include #include "../dom/document.hxx" #include 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& 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& 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 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, 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(captureVector).rbegin(); if (rinode != const_cast(captureVector).rend()) { // capturing phase: pEvent->m_phase = PhaseType_CAPTURING_PHASE; while (rinode != const_cast(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: */