2008-09-18 20:24:35 +00:00
|
|
|
// Signal.hh for FbTk, Fluxbox Toolkit
|
|
|
|
// Copyright (c) 2008 Henrik Kinnunen (fluxgen at fluxbox dot org)
|
|
|
|
//
|
|
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
// copy of this software and associated documentation files (the "Software"),
|
|
|
|
// to deal in the Software without restriction, including without limitation
|
|
|
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
|
|
// and/or sell copies of the Software, and to permit persons to whom the
|
|
|
|
// Software is furnished to do so, subject to the following conditions:
|
|
|
|
//
|
|
|
|
// The above copyright notice and this permission notice shall be included in
|
|
|
|
// all copies or substantial portions of the Software.
|
|
|
|
//
|
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
|
|
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
|
|
// DEALINGS IN THE SOFTWARE.
|
|
|
|
|
|
|
|
#ifndef FBTK_SIGNAL_HH
|
|
|
|
#define FBTK_SIGNAL_HH
|
|
|
|
|
2011-05-19 10:32:42 +00:00
|
|
|
#include "RefCount.hh"
|
2008-09-18 20:24:35 +00:00
|
|
|
#include "Slot.hh"
|
2011-05-02 21:01:24 +00:00
|
|
|
#include <algorithm>
|
2008-09-18 20:24:35 +00:00
|
|
|
#include <list>
|
2008-09-21 06:46:23 +00:00
|
|
|
#include <map>
|
2010-03-26 17:04:58 +00:00
|
|
|
#include <set>
|
2008-09-18 20:24:35 +00:00
|
|
|
|
|
|
|
namespace FbTk {
|
|
|
|
|
|
|
|
/// \namespace Implementation details for signals, do not use anything in this namespace
|
|
|
|
namespace SigImpl {
|
|
|
|
|
|
|
|
/**
|
2011-05-03 10:49:05 +00:00
|
|
|
* Parent class for all \c Signal template classes.
|
2008-09-18 20:24:35 +00:00
|
|
|
* It handles the disconnect and holds all the slots. The connect must be
|
|
|
|
* handled by the child class so it can do the type checking.
|
|
|
|
*/
|
|
|
|
class SignalHolder {
|
2011-05-02 21:01:24 +00:00
|
|
|
protected:
|
|
|
|
typedef RefCount<SlotBase> SlotPtr;
|
|
|
|
typedef std::list<SlotPtr> SlotList;
|
|
|
|
|
2008-09-18 20:24:35 +00:00
|
|
|
public:
|
2010-03-26 17:04:58 +00:00
|
|
|
/// Special tracker interface used by SignalTracker.
|
|
|
|
class Tracker {
|
|
|
|
public:
|
|
|
|
virtual ~Tracker() { }
|
|
|
|
/// Disconnect this holder.
|
|
|
|
virtual void disconnect(SignalHolder& signal) = 0;
|
|
|
|
};
|
|
|
|
|
2008-09-18 20:24:35 +00:00
|
|
|
typedef SlotList::iterator Iterator;
|
|
|
|
typedef Iterator SlotID;
|
|
|
|
typedef SlotList::const_iterator ConstIterator;
|
|
|
|
|
2011-05-02 21:01:24 +00:00
|
|
|
SignalHolder() : m_emitting(0) {}
|
|
|
|
|
2010-03-26 17:04:58 +00:00
|
|
|
~SignalHolder() {
|
|
|
|
// Disconnect this holder from all trackers.
|
|
|
|
for (Trackers::iterator it = m_trackers.begin(),
|
|
|
|
it_end = m_trackers.end();
|
|
|
|
it != it_end; ++it ) {
|
|
|
|
(*it)->disconnect(*this);
|
|
|
|
}
|
|
|
|
}
|
2008-09-21 06:46:23 +00:00
|
|
|
|
2008-09-18 20:24:35 +00:00
|
|
|
/// Remove a specific slot \c id from this signal
|
2011-08-08 11:14:20 +00:00
|
|
|
void disconnect(SlotID slotIt) const {
|
2011-05-02 21:01:24 +00:00
|
|
|
if(m_emitting) {
|
|
|
|
// if we are emitting, we must not erase the actual element, as that would
|
|
|
|
// invalidate iterators in the emit() function
|
|
|
|
*slotIt = SlotPtr();
|
|
|
|
} else
|
|
|
|
m_slots.erase( slotIt );
|
2008-09-18 20:24:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Removes all slots connected to this
|
|
|
|
void clear() {
|
2011-05-02 21:01:24 +00:00
|
|
|
if(m_emitting)
|
|
|
|
std::fill(m_slots.begin(), m_slots.end(), SlotPtr());
|
|
|
|
else
|
|
|
|
m_slots.clear();
|
2008-09-18 20:24:35 +00:00
|
|
|
}
|
|
|
|
|
2011-08-08 11:14:20 +00:00
|
|
|
void connectTracker(SignalHolder::Tracker& tracker) const {
|
2010-03-26 17:04:58 +00:00
|
|
|
m_trackers.insert(&tracker);
|
|
|
|
}
|
|
|
|
|
2011-08-08 11:14:20 +00:00
|
|
|
void disconnectTracker(SignalHolder::Tracker& tracker) const {
|
2010-03-26 17:04:58 +00:00
|
|
|
m_trackers.erase(&tracker);
|
|
|
|
}
|
|
|
|
|
2008-09-18 20:24:35 +00:00
|
|
|
protected:
|
|
|
|
ConstIterator begin() const { return m_slots.begin(); }
|
|
|
|
ConstIterator end() const { return m_slots.end(); }
|
|
|
|
|
|
|
|
Iterator begin() { return m_slots.begin(); }
|
|
|
|
Iterator end() { return m_slots.end(); }
|
|
|
|
|
|
|
|
/// Connect a slot to this signal. Must only be called by child classes.
|
2011-08-08 11:14:20 +00:00
|
|
|
SlotID connect(const SlotPtr& slot) const {
|
2008-09-18 20:24:35 +00:00
|
|
|
return m_slots.insert(m_slots.end(), slot);
|
|
|
|
}
|
|
|
|
|
2011-05-02 21:01:24 +00:00
|
|
|
void begin_emitting() { ++m_emitting; }
|
|
|
|
void end_emitting() {
|
|
|
|
if(--m_emitting == 0) {
|
|
|
|
// remove elements which belonged slots that detached themselves
|
|
|
|
m_slots.erase(std::remove(m_slots.begin(), m_slots.end(), SlotPtr()), m_slots.end());
|
|
|
|
}
|
|
|
|
}
|
2008-09-18 20:24:35 +00:00
|
|
|
private:
|
2010-03-26 17:04:58 +00:00
|
|
|
typedef std::set<Tracker*> Trackers;
|
2011-08-08 11:14:20 +00:00
|
|
|
mutable SlotList m_slots; ///< all slots connected to a signal
|
|
|
|
mutable Trackers m_trackers; ///< all instances that tracks this signal.
|
2011-05-02 21:01:24 +00:00
|
|
|
unsigned m_emitting;
|
2008-09-18 20:24:35 +00:00
|
|
|
};
|
|
|
|
|
2011-05-19 10:32:42 +00:00
|
|
|
} // namespace SigImpl
|
2011-05-03 10:49:05 +00:00
|
|
|
|
2011-05-19 10:32:42 +00:00
|
|
|
|
|
|
|
/// Specialization for three arguments.
|
|
|
|
template <typename Arg1 = SigImpl::EmptyArg, typename Arg2 = SigImpl::EmptyArg, typename Arg3 = SigImpl::EmptyArg >
|
|
|
|
class Signal: public SigImpl::SignalHolder {
|
|
|
|
public:
|
|
|
|
void emit(Arg1 arg1, Arg2 arg2, Arg3 arg3) {
|
2011-05-02 21:01:24 +00:00
|
|
|
begin_emitting();
|
2008-09-18 20:24:35 +00:00
|
|
|
for ( Iterator it = begin(); it != end(); ++it ) {
|
2011-05-02 21:01:24 +00:00
|
|
|
if(*it)
|
2011-05-19 10:32:42 +00:00
|
|
|
static_cast<Slot<void, Arg1, Arg2, Arg3> &>(**it)(arg1, arg2, arg3);
|
2008-09-18 20:24:35 +00:00
|
|
|
}
|
2011-05-02 21:01:24 +00:00
|
|
|
end_emitting();
|
2008-09-18 20:24:35 +00:00
|
|
|
}
|
2011-05-03 10:49:05 +00:00
|
|
|
|
2011-05-19 10:32:42 +00:00
|
|
|
template<typename Functor>
|
2011-08-08 11:14:20 +00:00
|
|
|
SlotID connect(const Functor& functor) const {
|
2011-05-19 10:32:42 +00:00
|
|
|
return SignalHolder::connect(SlotPtr(
|
|
|
|
new SlotImpl<Functor, void, Arg1, Arg2, Arg3>(functor)
|
|
|
|
));
|
|
|
|
}
|
2011-07-09 21:57:32 +00:00
|
|
|
|
2011-08-08 11:14:20 +00:00
|
|
|
SlotID connectSlot(const RefCount<FbTk::Slot<void, Arg1, Arg2, Arg3> > &slot) const {
|
2011-07-09 21:57:32 +00:00
|
|
|
return SignalHolder::connect(slot);
|
|
|
|
}
|
2008-09-18 20:24:35 +00:00
|
|
|
};
|
|
|
|
|
2011-04-29 08:32:21 +00:00
|
|
|
/// Specialization for two arguments.
|
2011-05-03 10:49:05 +00:00
|
|
|
template <typename Arg1, typename Arg2>
|
2011-05-19 10:32:42 +00:00
|
|
|
class Signal<Arg1, Arg2, SigImpl::EmptyArg>: public SigImpl::SignalHolder {
|
2008-09-18 20:24:35 +00:00
|
|
|
public:
|
|
|
|
void emit(Arg1 arg1, Arg2 arg2) {
|
2011-05-19 10:32:42 +00:00
|
|
|
begin_emitting();
|
|
|
|
for ( Iterator it = begin(); it != end(); ++it ) {
|
|
|
|
if(*it)
|
|
|
|
static_cast<Slot<void, Arg1, Arg2> &>(**it)(arg1, arg2);
|
|
|
|
}
|
|
|
|
end_emitting();
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename Functor>
|
2011-08-08 11:14:20 +00:00
|
|
|
SlotID connect(const Functor& functor) const {
|
2011-05-19 10:32:42 +00:00
|
|
|
return SignalHolder::connect(SlotPtr(
|
|
|
|
new SlotImpl<Functor, void, Arg1, Arg2>(functor)
|
|
|
|
));
|
2008-09-18 20:24:35 +00:00
|
|
|
}
|
2011-07-09 21:57:32 +00:00
|
|
|
|
2011-08-08 11:14:20 +00:00
|
|
|
SlotID connectSlot(const RefCount<FbTk::Slot<void, Arg1, Arg2> > &slot) const {
|
2011-07-09 21:57:32 +00:00
|
|
|
return SignalHolder::connect(slot);
|
|
|
|
}
|
2008-09-18 20:24:35 +00:00
|
|
|
};
|
|
|
|
|
2011-04-29 08:32:21 +00:00
|
|
|
/// Specialization for one argument.
|
2011-05-03 10:49:05 +00:00
|
|
|
template <typename Arg1>
|
2011-05-19 10:32:42 +00:00
|
|
|
class Signal<Arg1, SigImpl::EmptyArg, SigImpl::EmptyArg>: public SigImpl::SignalHolder {
|
2008-09-18 20:24:35 +00:00
|
|
|
public:
|
2011-05-19 10:32:42 +00:00
|
|
|
void emit(Arg1 arg) {
|
|
|
|
begin_emitting();
|
|
|
|
for ( Iterator it = begin(); it != end(); ++it ) {
|
|
|
|
if(*it)
|
|
|
|
static_cast<Slot<void, Arg1> &>(**it)(arg);
|
|
|
|
}
|
|
|
|
end_emitting();
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename Functor>
|
2011-08-08 11:14:20 +00:00
|
|
|
SlotID connect(const Functor& functor) const {
|
2011-05-19 10:32:42 +00:00
|
|
|
return SignalHolder::connect(SlotPtr(
|
|
|
|
new SlotImpl<Functor, void, Arg1>(functor)
|
|
|
|
));
|
2008-09-18 20:24:35 +00:00
|
|
|
}
|
2011-07-09 21:57:32 +00:00
|
|
|
|
2011-08-08 11:14:20 +00:00
|
|
|
SlotID connectSlot(const RefCount<FbTk::Slot<void, Arg1> > &slot) const {
|
2011-07-09 21:57:32 +00:00
|
|
|
return SignalHolder::connect(slot);
|
|
|
|
}
|
2008-09-18 20:24:35 +00:00
|
|
|
};
|
|
|
|
|
2011-04-29 08:32:21 +00:00
|
|
|
/// Specialization for no arguments.
|
2011-05-03 10:49:05 +00:00
|
|
|
template <>
|
2011-05-19 10:32:42 +00:00
|
|
|
class Signal<SigImpl::EmptyArg, SigImpl::EmptyArg, SigImpl::EmptyArg>: public SigImpl::SignalHolder {
|
2008-09-18 20:24:35 +00:00
|
|
|
public:
|
2011-04-29 08:32:21 +00:00
|
|
|
void emit() {
|
2011-05-19 10:32:42 +00:00
|
|
|
begin_emitting();
|
|
|
|
for ( Iterator it = begin(); it != end(); ++it ) {
|
|
|
|
if(*it)
|
|
|
|
static_cast<Slot<void> &>(**it)();
|
|
|
|
}
|
|
|
|
end_emitting();
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename Functor>
|
2011-08-08 11:14:20 +00:00
|
|
|
SlotID connect(const Functor& functor) const {
|
2011-05-19 10:32:42 +00:00
|
|
|
return SignalHolder::connect(SlotPtr(
|
|
|
|
new SlotImpl<Functor, void>(functor)
|
|
|
|
));
|
2011-04-29 08:32:21 +00:00
|
|
|
}
|
2011-07-09 21:57:32 +00:00
|
|
|
|
2011-08-08 11:14:20 +00:00
|
|
|
SlotID connectSlot(const RefCount<FbTk::Slot<void> > &slot) const {
|
2011-07-09 21:57:32 +00:00
|
|
|
return SignalHolder::connect(slot);
|
|
|
|
}
|
2008-09-18 20:24:35 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Tracks a signal during it's life time. All signals connected using \c
|
|
|
|
* SignalTracker::join will be erased when this instance dies.
|
|
|
|
*/
|
2010-03-26 17:04:58 +00:00
|
|
|
class SignalTracker: public SigImpl::SignalHolder::Tracker {
|
2008-09-18 20:24:35 +00:00
|
|
|
public:
|
|
|
|
/// Internal type, do not use.
|
2011-08-08 11:14:20 +00:00
|
|
|
typedef std::map<const SigImpl::SignalHolder*,
|
2008-09-28 08:46:49 +00:00
|
|
|
SigImpl::SignalHolder::SlotID> Connections;
|
2008-09-18 20:24:35 +00:00
|
|
|
typedef Connections::iterator TrackID; ///< \c ID type for join/leave.
|
|
|
|
|
2008-09-21 10:02:49 +00:00
|
|
|
~SignalTracker() {
|
|
|
|
leaveAll();
|
2008-09-18 20:24:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Starts tracking a signal.
|
2010-03-26 17:04:58 +00:00
|
|
|
/// @return A tracking ID
|
2011-07-09 21:57:32 +00:00
|
|
|
template<typename Arg1, typename Arg2, typename Arg3, typename Functor>
|
2011-08-08 11:14:20 +00:00
|
|
|
TrackID join(const Signal<Arg1, Arg2, Arg3> &sig, const Functor &functor) {
|
2011-08-02 09:58:41 +00:00
|
|
|
return joinSlot(sig, RefCount<Slot<void, Arg1, Arg2, Arg3> >(
|
2011-07-09 21:57:32 +00:00
|
|
|
new SlotImpl<Functor, void, Arg1, Arg2, Arg3>(functor)
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename Arg1, typename Arg2, typename Arg3>
|
|
|
|
TrackID
|
2011-08-08 11:14:20 +00:00
|
|
|
joinSlot(const Signal<Arg1, Arg2, Arg3> &sig,
|
|
|
|
const RefCount<Slot<void, Arg1, Arg2, Arg3> > &slot) {
|
|
|
|
|
2011-08-02 09:58:41 +00:00
|
|
|
ValueType value = ValueType(&sig, sig.connectSlot(slot));
|
2008-09-28 08:46:49 +00:00
|
|
|
std::pair<TrackID, bool> ret = m_connections.insert(value);
|
|
|
|
if ( !ret.second ) {
|
|
|
|
// failed to insert this functor
|
|
|
|
sig.disconnect(value.second);
|
|
|
|
}
|
2010-03-26 17:04:58 +00:00
|
|
|
|
|
|
|
sig.connectTracker(*this);
|
|
|
|
|
2008-09-28 08:46:49 +00:00
|
|
|
return ret.first;
|
2008-09-18 20:24:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Leave tracking for a signal
|
|
|
|
/// @param id the \c id from the previous \c join
|
2011-05-07 22:04:12 +00:00
|
|
|
void leave(TrackID id) {
|
2010-05-07 07:36:30 +00:00
|
|
|
// keep temporary, while disconnecting we can
|
|
|
|
// in some strange cases get a call to this again
|
|
|
|
ValueType tmp = *id;
|
2008-09-18 20:24:35 +00:00
|
|
|
m_connections.erase(id);
|
2010-07-14 15:36:00 +00:00
|
|
|
tmp.first->disconnect(tmp.second);
|
2011-05-07 22:04:12 +00:00
|
|
|
tmp.first->disconnectTracker(*this);
|
2008-09-18 20:24:35 +00:00
|
|
|
}
|
|
|
|
|
2008-09-21 06:46:23 +00:00
|
|
|
/// Leave tracking for a signal
|
|
|
|
/// @param sig the signal to leave
|
|
|
|
template <typename Signal>
|
2008-09-28 08:46:49 +00:00
|
|
|
void leave(Signal &sig) {
|
|
|
|
Iterator it = m_connections.find(&sig);
|
|
|
|
if (it != m_connections.end()) {
|
2010-05-07 07:36:30 +00:00
|
|
|
leave(it);
|
2008-09-28 08:46:49 +00:00
|
|
|
}
|
2008-09-21 06:46:23 +00:00
|
|
|
}
|
|
|
|
|
2008-09-21 10:02:49 +00:00
|
|
|
|
|
|
|
void leaveAll() {
|
|
|
|
// disconnect all connections
|
2010-05-23 13:58:35 +00:00
|
|
|
for ( ; !m_connections.empty(); ) {
|
2011-05-07 22:04:12 +00:00
|
|
|
leave(m_connections.begin());
|
2008-09-21 10:02:49 +00:00
|
|
|
}
|
|
|
|
}
|
2010-03-26 17:04:58 +00:00
|
|
|
|
|
|
|
protected:
|
|
|
|
|
2010-05-06 18:25:47 +00:00
|
|
|
virtual void disconnect(SigImpl::SignalHolder& signal) {
|
2010-03-26 17:04:58 +00:00
|
|
|
m_connections.erase(&signal);
|
|
|
|
}
|
|
|
|
|
2008-09-18 20:24:35 +00:00
|
|
|
private:
|
2008-09-28 08:46:49 +00:00
|
|
|
typedef Connections::value_type ValueType;
|
|
|
|
typedef Connections::iterator Iterator;
|
2008-09-18 20:24:35 +00:00
|
|
|
/// holds all connections to different signals and slots.
|
|
|
|
Connections m_connections;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace FbTk
|
|
|
|
|
|
|
|
#endif // FBTK_SIGNAL_HH
|