/* KMix -- KDE's full featured mini mixer Copyright (C) 2012 Christian Esken This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "ControlManager.h" #include "core/GlobalConfig.h" #include #include ControlManager ControlManager::instanceSingleton; ControlManager& ControlManager::instance() { return instanceSingleton; } ControlManager::ControlManager() { listenersChanged = false; } /** * Announce a change for one or all mixers. * * @param mixerId The mixerId. Use an empty QString() to announce a change for all mixers * @param changeType A bit array of ControlChangeType flags * @param sourceId Only for logging * */ void ControlManager::announce(QString mixerId, ControlChangeType::Type changeType, QString sourceId) { bool listenersModified = false; QSet processedListeners; do { listenersModified = false; QList::iterator it; for (it = listeners.begin(); it != listeners.end(); ++it) { Listener& listener = *it; if ( &listener == 0 ) { kWarning() << "null Listener detected ... skipping"; continue; } bool mixerIsOfInterest = listener.getMixerId().isEmpty() || mixerId.isEmpty() || listener.getMixerId() == mixerId; bool listenerAlreadyProcesed = processedListeners.contains(&listener); if ( listenerAlreadyProcesed ) { if (GlobalConfig::instance().data.debugControlManager) kDebug() << "Skipping already processed listener"; continue; } if (mixerIsOfInterest && listener.getChangeType() == changeType) { bool success = QMetaObject::invokeMethod(listener.getTarget(), "controlsChange", Qt::DirectConnection, Q_ARG(int, changeType)); if (GlobalConfig::instance().data.debugControlManager) { kDebug() << "Listener " << listener.getSourceId() <<" is interested in " << mixerId << ", " << ControlChangeType::toString(changeType); } if (!success) { kError() << "Listener Failed to send to " << listener.getTarget()->metaObject()->className(); } processedListeners.insert(&listener); if (listenersChanged) { // The invokeMethod() above has changed the listeners => my Iterator is invalid => restart loop if (GlobalConfig::instance().data.debugControlManager) kDebug() << "Listeners modified => restart loop"; listenersChanged = false; listenersModified = true; break; // break inner loop => restart via outer loop } } } } while ( listenersModified); if (GlobalConfig::instance().data.debugControlManager) { kDebug() << "Announcing " << ControlChangeType::toString(changeType) << " for " << (mixerId.isEmpty() ? "all cards" : mixerId) << " by " << sourceId; } } /** * Adds a listener for the given mixerId and changeType. * Listeners are informed about all corresponding changes via a signal. * Listeners are not informed about changes that originate from oneself (according to sourceId). * * @param mixerId The id of the Mixer you are interested in * @param changetType The changeType of interest * @param target The QObject, where the notification signal is sent to. It must implement the SLOT controlChanged(QString mixerId, ControlChangeType::Type changeType). * @param sourceId Only for logging */ void ControlManager::addListener(QString mixerId, ControlChangeType::Type changeType, QObject* target, QString sourceId) { if (GlobalConfig::instance().data.debugControlManager) { kDebug() << "Listening to " << ControlChangeType::toString(changeType) << " for " << (mixerId.isEmpty() ? "all cards" : mixerId) << " by " << sourceId << ". Announcements are sent to " << target; } for ( ControlChangeType::Type ct = ControlChangeType::TypeFirst; ct != ControlChangeType::TypeLast; ct = (ControlChangeType::Type)(ct << 1)) { if ( changeType & ct ) { // Add all listeners. Listener listener = Listener(mixerId, ct, target, sourceId); listeners.append(listener); listenersChanged = true; } } if (GlobalConfig::instance().data.debugControlManager) { kDebug() << "We now have" << listeners.size() << "listeners"; } } /** * Removes all listeners of the given target. * @param target The QObject that was used to register via addListener() */ void ControlManager::removeListener(QObject* target) { ControlManager::instance().removeListener(target, target->metaObject()->className()); } /** * Removes all listeners of the given target. * @param target The QObject that was used to register via addListener() * @param sourceId Optional: Only for logging */ void ControlManager::removeListener(QObject* target, QString sourceId) { QMutableListIterator it(listeners); while ( it.hasNext()) { Listener& listener = it.next(); if (listener.getTarget() == target) { if (GlobalConfig::instance().data.debugControlManager) kDebug() << "Stop Listening of " << listener.getSourceId() << " requested by " << sourceId << " from " << target; it.remove(); // Hint: As we have actual objects no explicit delete is needed listenersChanged = true; } } } void ControlManager::warnUnexpectedChangeType(ControlChangeType::Type type, QObject *obj) { kWarning() << "Unexpected type " << type << " received by " << obj->metaObject()->className(); } void ControlManager::shutdownNow() { if (GlobalConfig::instance().data.debugControlManager) kDebug() << "Shutting down ControlManager"; QList::iterator it; for (it = listeners.begin(); it != listeners.end(); ++it) { Listener& listener = *it; if (GlobalConfig::instance().data.debugControlManager) kDebug() << "Listener still connected. Closing it. source=" << listener.getSourceId() << "listener=" << listener.getTarget()->metaObject()->className(); } } #include "moc_ControlManager.cpp"