changes associated with save/restore of AutomationControl id's
[ardour.git] / libs / pbd / pbd / signals.h
1 /*
2     Copyright (C) 2009 Paul Davis 
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #ifndef __pbd_signals_h__
21 #define __pbd_signals_h__
22
23 #include <list>
24 #include <glibmm/thread.h>
25
26 #include <boost/signals2.hpp>
27 #include <boost/noncopyable.hpp>
28 #include <boost/bind.hpp>
29 #include <boost/bind/protect.hpp>
30
31 #include "pbd/event_loop.h"
32
33 namespace PBD {
34
35 typedef boost::signals2::connection UnscopedConnection;
36 typedef boost::signals2::scoped_connection ScopedConnection;
37
38 class ScopedConnectionList  : public boost::noncopyable
39 {
40   public:
41         ScopedConnectionList();
42         ~ScopedConnectionList ();
43         
44         void add_connection (const UnscopedConnection& c);
45         void drop_connections ();
46
47   private:
48         /* this class is not copyable */
49         ScopedConnectionList(const ScopedConnectionList&);
50
51         /* this lock is shared by all instances of a ScopedConnectionList.
52            We do not want one mutex per list, and since we only need the lock
53            when adding or dropping connections, which are generally occuring
54            in object creation and UI operations, the contention on this 
55            lock is low and not of significant consequence. Even though
56            boost::signals2 is thread-safe, this additional list of
57            scoped connections needs to be protected in 2 cases:
58
59            (1) (unlikely) we make a connection involving a callback on the
60                same object from 2 threads. (wouldn't that just be appalling 
61                programming style?)
62              
63            (2) where we are dropping connections in one thread and adding
64                one from another.
65          */
66
67         static Glib::StaticMutex _lock;
68
69         typedef std::list<ScopedConnection*> ConnectionList;
70         ConnectionList _list;
71 };
72
73 template<typename R>
74 class Signal0 {
75 public:
76     Signal0 () {}
77     typedef boost::signals2::signal<R()> SignalType;
78
79     void connect_same_thread (ScopedConnection& c, 
80                   const typename SignalType::slot_function_type& slot) {
81             c = _signal.connect (slot);
82     }
83
84     void connect_same_thread (ScopedConnectionList& clist, 
85                   const typename SignalType::slot_function_type& slot) {
86             clist.add_connection (_signal.connect (slot));
87     }
88
89     void connect (ScopedConnectionList& clist, 
90                   PBD::EventLoop::InvalidationRecord* ir, 
91                   const typename SignalType::slot_function_type& slot,
92                   PBD::EventLoop* event_loop) {
93             clist.add_connection (_signal.connect (boost::bind (&EventLoop::call_slot, event_loop, ir, slot)));
94     }
95     
96     void connect (ScopedConnection& c, 
97                   PBD::EventLoop::InvalidationRecord* ir, 
98                   const typename SignalType::slot_function_type& slot,
99                   PBD::EventLoop* event_loop) {
100             c = _signal.connect (boost::bind (&EventLoop::call_slot, event_loop, ir, slot));
101     }
102     
103     typename SignalType::result_type operator()() {
104             return _signal ();
105     }
106
107     bool empty() const { return _signal.empty(); }
108     
109 private:
110     SignalType _signal;
111 };
112
113 template<typename R, typename A, typename C = boost::signals2::optional_last_value<R> >
114 class Signal1 {
115 public:
116     Signal1 () {}
117     typedef boost::signals2::signal<R(A), C> SignalType;
118
119     void connect_same_thread (ScopedConnectionList& clist, 
120                   const typename SignalType::slot_function_type& slot) {
121             clist.add_connection (_signal.connect (slot));
122     }
123
124     void connect_same_thread (ScopedConnection& c, 
125                      const typename SignalType::slot_function_type& slot) {
126             c = _signal.connect (slot);
127     }
128
129     static void compositor (typename boost::function<void(A)> f, EventLoop* event_loop, EventLoop::InvalidationRecord* ir, A arg) {
130             event_loop->call_slot (ir, boost::bind (f, arg));
131     }
132
133     void connect (ScopedConnectionList& clist, 
134                   PBD::EventLoop::InvalidationRecord* ir, 
135                   const typename SignalType::slot_function_type& slot,
136                   PBD::EventLoop* event_loop) {
137             clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1)));
138     }
139
140     void connect (ScopedConnection& c, 
141                   PBD::EventLoop::InvalidationRecord* ir, 
142                   const typename SignalType::slot_function_type& slot,
143                   PBD::EventLoop* event_loop) {
144             c = _signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1));
145
146     }
147     
148     typename SignalType::result_type operator()(A arg1) {
149             return _signal (arg1);
150     }
151     
152     bool empty() const { return _signal.empty(); }
153
154 private:
155     SignalType _signal;
156 };
157
158 template<typename R, typename A1, typename A2>
159 class Signal2 {
160 public:
161     Signal2 () {}
162     typedef boost::signals2::signal<R(A1, A2)> SignalType;
163
164     void connect_same_thread (ScopedConnectionList& clist, 
165                   const typename SignalType::slot_function_type& slot) {
166             clist.add_connection (_signal.connect (slot));
167     }
168
169     void connect_same_thread (ScopedConnection& c, 
170                      const typename SignalType::slot_function_type& slot) {
171             c = _signal.connect (slot);
172     }
173
174     static void compositor (typename boost::function<void(A1,A2)> f, PBD::EventLoop* event_loop, 
175                             EventLoop::InvalidationRecord* ir,
176                             A1 arg1, A2 arg2) {
177             event_loop->call_slot (ir, boost::bind (f, arg1, arg2));
178     }
179
180     void connect (ScopedConnectionList& clist, 
181                   PBD::EventLoop::InvalidationRecord* ir, 
182                   const typename SignalType::slot_function_type& slot,
183                   PBD::EventLoop* event_loop) {
184             clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2)));
185     }
186
187     void connect (ScopedConnection& c, 
188                   PBD::EventLoop::InvalidationRecord* ir, 
189                   const typename SignalType::slot_function_type& slot,
190                   PBD::EventLoop* event_loop) {
191             c = _signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2));
192     }
193
194     typename SignalType::result_type operator()(A1 arg1, A2 arg2) {
195             return _signal (arg1, arg2);
196     }
197     
198     bool empty() const { return _signal.empty(); }
199
200 private:
201     SignalType _signal;
202 };
203
204 template<typename R, typename A1, typename A2, typename A3>
205 class Signal3 {
206 public:
207     Signal3 () {}
208     typedef boost::signals2::signal<R(A1,A2,A3)> SignalType;
209
210     void connect_same_thread (ScopedConnectionList& clist, 
211                   const typename SignalType::slot_function_type& slot) {
212             clist.add_connection (_signal.connect (slot));
213     }
214
215     void connect_same_thread (ScopedConnection& c, 
216                               const typename SignalType::slot_function_type& slot) {
217             c = _signal.connect (slot);
218     }
219
220     static void compositor (typename boost::function<void(A1,A2,A3)> f, PBD::EventLoop* event_loop, 
221                             EventLoop::InvalidationRecord* ir, 
222                             A1 arg1, A2 arg2, A3 arg3) {
223             event_loop->call_slot (ir, boost::bind (f, arg1, arg2, arg3));
224     }
225
226     void connect (ScopedConnectionList& clist, 
227                   PBD::EventLoop::InvalidationRecord* ir, 
228                   const typename SignalType::slot_function_type& slot,
229                   PBD::EventLoop* event_loop) {
230             clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3)));
231     }
232     
233     void connect (ScopedConnection& c, 
234                   PBD::EventLoop::InvalidationRecord* ir, 
235                   const typename SignalType::slot_function_type& slot,
236                   PBD::EventLoop* event_loop) {
237             c = _signal.connect (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3)));
238     }
239     
240     typename SignalType::result_type operator()(A1 arg1, A2 arg2, A3 arg3) {
241             return _signal (arg1, arg2, arg3);
242     }
243     
244     bool empty() const { return _signal.empty(); }
245
246 private:
247     SignalType _signal;
248 };
249
250 template<typename R, typename A1, typename A2, typename A3, typename A4>
251 class Signal4 {
252 public:
253     Signal4 () {}
254     typedef boost::signals2::signal<R(A1,A2,A3,A4)> SignalType;
255
256     void connect_same_thread (ScopedConnectionList& clist, 
257                   const typename SignalType::slot_function_type& slot) {
258             clist.add_connection (_signal.connect (slot));
259     }
260
261     void connect_same_thread (ScopedConnection& c, 
262                               const typename SignalType::slot_function_type& slot) {
263             c = _signal.connect (slot);
264     }
265
266     static void compositor (typename boost::function<void(A1,A2,A3)> f, PBD::EventLoop* event_loop, 
267                             EventLoop::InvalidationRecord* ir, 
268                             A1 arg1, A2 arg2, A3 arg3, A4 arg4) {
269             event_loop->call_slot (ir, boost::bind (f, arg1, arg2, arg3, arg4));
270     }
271
272     void connect (ScopedConnectionList& clist, 
273                   PBD::EventLoop::InvalidationRecord* ir, 
274                   const typename SignalType::slot_function_type& slot,
275                   PBD::EventLoop* event_loop) {
276             clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4)));
277     }
278     
279     void connect (ScopedConnection& c, 
280                   PBD::EventLoop::InvalidationRecord* ir, 
281                   const typename SignalType::slot_function_type& slot,
282                   PBD::EventLoop* event_loop) {
283             c = _signal.connect (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4)));
284     }
285     
286     typename SignalType::result_type operator()(A1 arg1, A2 arg2, A3 arg3, A4 arg4) {
287             return _signal (arg1, arg2, arg3, arg4);
288     }
289     
290     bool empty() const { return _signal.empty(); }
291
292 private:
293     SignalType _signal;
294 };
295
296 } /* namespace */
297
298 #endif /* __pbd_signals_h__ */