/* Copyright (C) 2018-2021 Carl Hetherington This file is part of DCP-o-matic. DCP-o-matic 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. DCP-o-matic 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 DCP-o-matic. If not, see . */ #ifndef DCPOMATIC_CHANGE_SIGNALLER_H #define DCPOMATIC_CHANGE_SIGNALLER_H #include #include enum class ChangeType { PENDING, DONE, CANCELLED }; template class ChangeSignal { public: ChangeSignal(T* thing_, P property_, ChangeType type_) : thing(thing_) , property(property_) , type(type_) {} T* thing; P property; ChangeType type; }; class ChangeSignalDespatcherBase { protected: static boost::mutex _instance_mutex; }; template class ChangeSignalDespatcher : public ChangeSignalDespatcherBase { public: ChangeSignalDespatcher() = default; ChangeSignalDespatcher(ChangeSignalDespatcher const&) = delete; ChangeSignalDespatcher& operator=(ChangeSignalDespatcher const&) = delete; void signal_change(ChangeSignal const& signal) { if (_suspended) { boost::mutex::scoped_lock lm(_mutex); auto iter = std::find_if( _pending.begin(), _pending.end(), [signal](ChangeSignal const& s) { return s.property == signal.property && s.type == signal.type; } ); if (iter == _pending.end()) { _pending.push_back(signal); } } else { signal.thing->signal_change(signal.type, signal.property); } } void suspend() { boost::mutex::scoped_lock lm(_mutex); _suspended = true; } void resume() { boost::mutex::scoped_lock lm(_mutex); auto pending = _pending; _pending.clear(); _suspended = false; lm.unlock(); for (auto signal: pending) { signal.thing->signal_change(signal.type, signal.property); } } static ChangeSignalDespatcher* instance() { static boost::mutex _instance_mutex; boost::mutex::scoped_lock lm(_instance_mutex); static ChangeSignalDespatcher* _instance; if (!_instance) { _instance = new ChangeSignalDespatcher(); } return _instance; } private: std::vector> _pending; bool _suspended = false; boost::mutex _mutex; }; template class ChangeSignaller { public: ChangeSignaller (T* t, P p) : _thing(t) , _property(p) , _done(true) { ChangeSignalDespatcher::instance()->signal_change({_thing, _property, ChangeType::PENDING}); } ~ChangeSignaller () { ChangeSignalDespatcher::instance()->signal_change({_thing, _property, _done ? ChangeType::DONE : ChangeType::CANCELLED}); } void abort () { _done = false; } private: T* _thing; P _property; bool _done; }; #endif