1
// Signal.hh for FbTk, Fluxbox Toolkit
2
// Copyright (c) 2008 Henrik Kinnunen (fluxgen at fluxbox dot org)
4
// Permission is hereby granted, free of charge, to any person obtaining a
5
// copy of this software and associated documentation files (the "Software"),
6
// to deal in the Software without restriction, including without limitation
7
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
8
// and/or sell copies of the Software, and to permit persons to whom the
9
// Software is furnished to do so, subject to the following conditions:
11
// The above copyright notice and this permission notice shall be included in
12
// all copies or substantial portions of the Software.
14
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20
// DEALINGS IN THE SOFTWARE.
22
#ifndef FBTK_SIGNAL_HH
23
#define FBTK_SIGNAL_HH
33
/// \namespace Implementation details for signals, do not use anything in this namespace
37
* Parent class for all \c Signal[0...*] classes.
38
* It handles the disconnect and holds all the slots. The connect must be
39
* handled by the child class so it can do the type checking.
43
/// Special tracker interface used by SignalTracker.
46
virtual ~Tracker() { }
47
/// Disconnect this holder.
48
virtual void disconnect(SignalHolder& signal) = 0;
51
/// Do not use this type outside this class
52
typedef std::list<SlotHolder> SlotList;
54
typedef SlotList::iterator Iterator;
55
typedef Iterator SlotID;
56
typedef SlotList::const_iterator ConstIterator;
59
// Disconnect this holder from all trackers.
60
for (Trackers::iterator it = m_trackers.begin(),
61
it_end = m_trackers.end();
62
it != it_end; ++it ) {
63
(*it)->disconnect(*this);
67
/// Remove a specific slot \c id from this signal
68
void disconnect(SlotID slotIt) {
69
m_slots.erase( slotIt );
73
/// Removes all slots connected to this
78
void connectTracker(SignalHolder::Tracker& tracker) {
79
m_trackers.insert(&tracker);
82
void disconnectTracker(SignalHolder::Tracker& tracker) {
83
m_trackers.erase(&tracker);
87
ConstIterator begin() const { return m_slots.begin(); }
88
ConstIterator end() const { return m_slots.end(); }
90
Iterator begin() { return m_slots.begin(); }
91
Iterator end() { return m_slots.end(); }
93
/// Connect a slot to this signal. Must only be called by child classes.
94
SlotID connect(const SlotHolder& slot) {
95
return m_slots.insert(m_slots.end(), slot);
99
typedef std::set<Tracker*> Trackers;
100
SlotList m_slots; ///< all slots connected to a signal
101
Trackers m_trackers; ///< all instances that tracks this signal.
104
/// Signal with no argument
105
template <typename ReturnType>
106
class Signal0: public SignalHolder {
108
typedef Slot0<ReturnType> SlotType;
113
for ( Iterator it = begin(); it != end(); ++it ) {
114
static_cast<SlotType&>(*it)();
118
SlotID connect(const SlotType& slot) {
119
return SignalHolder::connect(slot);
123
/// Signal with one argument
124
template <typename ReturnType, typename Arg1>
125
class Signal1: public SignalHolder {
127
typedef Slot1<ReturnType, Arg1> SlotType;
131
void emit(Arg1 arg) {
132
for ( Iterator it = begin(); it != end(); ++it ) {
133
static_cast<SlotType&>(*it)(arg);
137
SlotID connect(const SlotType& slot) {
138
return SignalHolder::connect(slot);
143
/// Signal with two arguments
144
template <typename ReturnType, typename Arg1, typename Arg2>
145
class Signal2: public SignalHolder {
147
typedef Slot2<ReturnType, Arg1, Arg2> SlotType;
151
void emit(Arg1 arg1, Arg2 arg2) {
152
for ( Iterator it = begin(); it != end(); ++it ) {
153
static_cast<SlotType&>(*it)(arg1, arg2);
157
SlotID connect(const SlotType& slot) {
158
return SignalHolder::connect(slot);
162
/// Signal with three arguments
163
template <typename ReturnType, typename Arg1, typename Arg2, typename Arg3>
164
class Signal3: public SignalHolder {
166
typedef Slot3<ReturnType, Arg1, Arg2, Arg3> SlotType;
170
void emit(Arg1 arg1, Arg2 arg2, Arg3 arg3) {
171
for ( Iterator it = begin(); it != end(); ++it ) {
172
static_cast<SlotType&>(*it)(arg1, arg2, arg3);
176
SlotID connect(const SlotType& slot) {
177
return SignalHolder::connect(slot);
184
} // namespace SigImpl
187
/// Specialization for three arguments.
188
template <typename ReturnType,
189
typename Arg1 = SigImpl::EmptyArg, typename Arg2 = SigImpl::EmptyArg, typename Arg3 = SigImpl::EmptyArg >
190
class Signal: public SigImpl::Signal3< ReturnType, Arg1, Arg2, Arg3 > {
194
/// Specialization for two arguments.
195
template <typename ReturnType, typename Arg1, typename Arg2>
196
class Signal<ReturnType, Arg1, Arg2, SigImpl::EmptyArg>: public SigImpl::Signal2< ReturnType, Arg1, Arg2 > {
200
/// Specialization for one argument.
201
template <typename ReturnType, typename Arg1>
202
class Signal<ReturnType, Arg1, SigImpl::EmptyArg, SigImpl::EmptyArg>: public SigImpl::Signal1< ReturnType, Arg1 > {
206
/// Specialization for no argument.
207
template <typename ReturnType>
208
class Signal<ReturnType, SigImpl::EmptyArg, SigImpl::EmptyArg, SigImpl::EmptyArg>: public SigImpl::Signal0< ReturnType > {
213
* Tracks a signal during it's life time. All signals connected using \c
214
* SignalTracker::join will be erased when this instance dies.
216
class SignalTracker: public SigImpl::SignalHolder::Tracker {
218
/// Internal type, do not use.
219
typedef std::map<SigImpl::SignalHolder*,
220
SigImpl::SignalHolder::SlotID> Connections;
221
typedef Connections::iterator TrackID; ///< \c ID type for join/leave.
227
/// Starts tracking a signal.
228
/// @return A tracking ID
229
template <typename Signal, typename Functor>
230
TrackID join(Signal& sig, const Functor& functor) {
231
ValueType value = ValueType(&sig, sig.connect(functor));
232
std::pair<TrackID, bool> ret = m_connections.insert(value);
234
// failed to insert this functor
235
sig.disconnect(value.second);
238
sig.connectTracker(*this);
243
/// Leave tracking for a signal
244
/// @param id the \c id from the previous \c join
245
void leave(TrackID id, bool withTracker = false) {
246
// keep temporary, while disconnecting we can
247
// in some strange cases get a call to this again
249
m_connections.erase(id);
250
tmp.first->disconnect(tmp.second);
252
tmp.first->disconnectTracker(*this);
255
/// Leave tracking for a signal
256
/// @param sig the signal to leave
257
template <typename Signal>
258
void leave(Signal &sig) {
259
Iterator it = m_connections.find(&sig);
260
if (it != m_connections.end()) {
267
// disconnect all connections
268
for ( ; !m_connections.empty(); ) {
269
leave(m_connections.begin(), true);
275
virtual void disconnect(SigImpl::SignalHolder& signal) {
276
m_connections.erase(&signal);
280
typedef Connections::value_type ValueType;
281
typedef Connections::iterator Iterator;
282
/// holds all connections to different signals and slots.
283
Connections m_connections;
289
#endif // FBTK_SIGNAL_HH