1 #ifndef _GLIBMM_SIGNALPROXY_H
2 #define _GLIBMM_SIGNALPROXY_H
3 
4 /* signalproxy.h
5  *
6  * Copyright (C) 2015 The gtkmm Development Team
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 extern "C" {
23 typedef void (*GCallback)(void);
24 typedef struct _GObject GObject;
25 }
26 
27 #include <sigc++/sigc++.h>
28 #include <glibmm/signalproxy_connectionnode.h>
29 #include <glibmm/ustring.h>
30 #include <utility> // std::move()
31 
32 namespace Glib
33 {
34 
35 // Forward declarations
36 class GLIBMM_API ObjectBase;
37 
38 #ifndef DOXYGEN_SHOULD_SKIP_THIS
39 
40 struct SignalProxyInfo
41 {
42   const char* signal_name;
43   GCallback callback;
44   GCallback notify_callback;
45 };
46 
47 #endif // DOXYGEN_SHOULD_SKIP_THIS
48 
49 // This base class is used by SignalProxyNormal, SignalProxyDetailed and SignalProxyProperty.
50 class GLIBMM_API SignalProxyBase
51 {
52 public:
53   SignalProxyBase(Glib::ObjectBase* obj);
54 
55 #ifndef DOXYGEN_SHOULD_SKIP_THIS
data_to_slot(void * data)56   static inline sigc::slot_base* data_to_slot(void* data)
57   {
58     const auto pConnectionNode = static_cast<SignalProxyConnectionNode*>(data);
59 
60     // Return null pointer if the connection is blocked.
61     return (!pConnectionNode->slot_.blocked()) ? &pConnectionNode->slot_ : nullptr;
62   }
63 #endif /* DOXYGEN_SHOULD_SKIP_THIS */
64 
65 protected:
66   ObjectBase* obj_;
67 
68 private:
69   SignalProxyBase& operator=(const SignalProxyBase&); // not implemented
70 };
71 
72 // Shared portion of a Signal without detail
73 /** The SignalProxy provides an API similar to sigc::signal that can be used to
74  * connect sigc::slots to glib signals.
75  *
76  * This holds the name of the glib signal and the object
77  * which might emit it. Actually, proxies are controlled by
78  * the template derivatives, which serve as gatekeepers for the
79  * types allowed on a particular signal.
80  *
81  * For signals with a detailed name (signal_name::detail_name) see SignalProxyDetailed.
82  */
83 class GLIBMM_API SignalProxyNormal : public SignalProxyBase
84 {
85 public:
86   ~SignalProxyNormal() noexcept;
87 
88   /// Stops the current signal emission (not in libsigc++)
89   void emission_stop();
90 
91 #ifndef DOXYGEN_SHOULD_SKIP_THIS
92   // This callback for SignalProxy<void>
93   // is defined here to avoid code duplication.
94   static void slot0_void_callback(GObject*, void* data);
95 #endif
96 
97 protected:
98   /** Creates a proxy for a signal that can be emitted by @a obj.
99    * @param obj The object that can emit the signal.
100    * @param info Information about the signal, including its name, and the C callbacks that should
101    * be called by glib.
102    */
103   SignalProxyNormal(Glib::ObjectBase* obj, const SignalProxyInfo* info);
104 
105   /** Connects a generic signal handler to a signal.
106    * This is called by connect() in derived SignalProxy classes.
107    *
108    * @param slot The signal handler, usually created with sigc::mem_fun() or sigc::ptr_fun().
109    * @param after Whether this signal handler should be called before or after the default signal
110    * handler.
111    */
112   sigc::slot_base& connect_(const sigc::slot_base& slot, bool after);
113 
114   /** Connects a signal handler without a return value to a signal.
115    * This is called by connect_notify() in derived SignalProxy classes.
116    *
117    * @param slot The signal handler, which should have a @c void return type,
118    *        usually created with sigc::mem_fun() or sigc::ptr_fun().
119    * @param after Whether this signal handler should be called before or after the default signal
120    * handler.
121    */
122   sigc::slot_base& connect_notify_(const sigc::slot_base& slot, bool after);
123 
124   /** Connects a signal handler to a signal.
125    * @see connect_(const sigc::slot_base& slot, bool after) and
126    * connect_notify_(const sigc::slot_base& slot, bool after).
127    *
128    * @newin{2,48}
129    */
130   sigc::slot_base& connect_impl_(bool notify, sigc::slot_base&& slot, bool after);
131 
132 private:
133   const SignalProxyInfo* info_;
134 
135   // TODO: We could maybe replace both connect_() and connect_notify_() with this in future, because
136   // they don't do anything extra.
137   /** This is called by connect_() and connect_notify_().
138    */
139   sigc::slot_base& connect_impl_(GCallback callback, const sigc::slot_base& slot, bool after);
140 
141   // no copy assignment
142   SignalProxyNormal& operator=(const SignalProxyNormal&);
143 };
144 
145 /**** Glib::SignalProxy ***************************************************/
146 
147 /** Proxy for signals with any number of arguments.
148  * Use the connect() or connect_notify() method, with sigc::mem_fun() or sigc::ptr_fun()
149  * to connect signal handlers to signals.
150  */
151 template <class R, class... T>
152 class SignalProxy : public SignalProxyNormal
153 {
154 public:
155   using SlotType = sigc::slot<R, T...>;
156   using VoidSlotType = sigc::slot<void, T...>;
157 
SignalProxy(ObjectBase * obj,const SignalProxyInfo * info)158   SignalProxy(ObjectBase* obj, const SignalProxyInfo* info) : SignalProxyNormal(obj, info) {}
159 
160   /** Connects a signal handler to a signal.
161    *
162    * For instance, connect( sigc::mem_fun(*this, &TheClass::on_something) );
163    *
164    * @param slot The signal handler, usually created with sigc::mem_fun() or sigc::ptr_fun().
165    * @param after Whether this signal handler should be called before or after the default signal
166    * handler.
167    */
168   sigc::connection connect(const SlotType& slot, bool after = true)
169   {
170     return sigc::connection(connect_(slot, after));
171   }
172 
173   /** Connects a signal handler to a signal.
174    * @see connect(const SlotType& slot, bool after).
175    *
176    * @newin{2,48}
177    */
178   sigc::connection connect(SlotType&& slot, bool after = true)
179   {
180     return sigc::connection(connect_impl_(false, std::move(slot), after));
181   }
182 
183   /** Connects a signal handler without a return value to a signal.
184    * By default, the signal handler will be called before the default signal handler.
185    *
186    * For instance, connect_notify( sigc::mem_fun(*this, &TheClass::on_something) );
187    *
188    * If the signal requires signal handlers with a @c void return type,
189    * the only difference between connect() and connect_notify() is the default
190    * value of @a after.
191    *
192    * If the signal requires signal handlers with a return value of type T,
193    * connect_notify() binds <tt>return T()</tt> to the connected signal handler.
194    * For instance, if the return type is @c bool, the following two calls are equivalent.
195    * @code
196    * connect_notify( sigc::mem_fun(*this, &TheClass::on_something) );
197    * connect( sigc::bind_return<bool>(sigc::mem_fun(*this, &TheClass::on_something), false), false
198    * );
199    * @endcode
200    *
201    * @param slot The signal handler, which should have a @c void return type,
202    *        usually created with sigc::mem_fun() or sigc::ptr_fun().
203    * @param after Whether this signal handler should be called before or after the default signal
204    * handler.
205    */
206   sigc::connection connect_notify(const VoidSlotType& slot, bool after = false)
207   {
208     return sigc::connection(connect_notify_(slot, after));
209   }
210 
211   /** Connects a signal handler without a return value to a signal.
212    * @see connect_notify(const VoidSlotType& slot, bool after).
213    *
214    * @newin{2,48}
215    */
216   sigc::connection connect_notify(VoidSlotType&& slot, bool after = false)
217   {
218     return sigc::connection(connect_impl_(true, std::move(slot), after));
219   }
220 };
221 
222 /* Templates below has been added to avoid API break, and should not be
223  * used in a newly created code. SignalProxy class should be used instead
224  * of SignalProxy# class.
225  */
226 template <typename R>
227 using SignalProxy0 = SignalProxy<R>;
228 template <typename R, typename T1>
229 using SignalProxy1 = SignalProxy<R, T1>;
230 template <typename R, typename T1, typename T2>
231 using SignalProxy2 = SignalProxy<R, T1, T2>;
232 template <typename R, typename T1, typename T2, typename T3>
233 using SignalProxy3 = SignalProxy<R, T1, T2, T3>;
234 template <typename R, typename T1, typename T2, typename T3, typename T4>
235 using SignalProxy4 = SignalProxy<R, T1, T2, T3, T4>;
236 template <typename R, typename T1, typename T2, typename T3, typename T4, typename T5>
237 using SignalProxy5 = SignalProxy<R, T1, T2, T3, T4, T5>;
238 template <typename R, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
239 using SignalProxy6 = SignalProxy<R, T1, T2, T3, T4, T5, T6>;
240 
241 // TODO: When we can break ABI, consider renaming
242 // SignalProxyDetailed => SignalProxyDetailedBase
243 // SignalProxyDetailedAnyType => SignalProxyDetailed
244 
245 // Shared portion of a Signal with detail
246 /** The SignalProxy provides an API similar to sigc::signal that can be used to
247  * connect sigc::slots to glib signals.
248  *
249  * This holds the name of the glib signal, including the detail name if any,
250  * and the object which might emit it. Actually, proxies are controlled by
251  * the template derivatives, which serve as gatekeepers for the
252  * types allowed on a particular signal.
253  */
254 class GLIBMM_API SignalProxyDetailed : public SignalProxyBase
255 {
256 public:
257   ~SignalProxyDetailed() noexcept;
258 
259   /// Stops the current signal emission (not in libsigc++)
260   void emission_stop();
261 
262 protected:
263   /** Creates a proxy for a signal that can be emitted by @a obj.
264    * @param obj The object that can emit the signal.
265    * @param info Information about the signal, including its name
266    *             and the C callbacks that should be called by glib.
267    * @param detail_name The detail name, if any.
268    */
269   SignalProxyDetailed(
270     Glib::ObjectBase* obj, const SignalProxyInfo* info, const Glib::ustring& detail_name);
271 
272   /** Connects a signal handler to a signal.
273    * This is called by connect() and connect_notify() in derived SignalProxyDetailedAnyType classes.
274    *
275    * @param notify Whether this method is called by connect_notify() or by connect().
276    * @param slot The signal handler, usually created with sigc::mem_fun() or sigc::ptr_fun().
277    * @param after Whether this signal handler should be called before or after the default signal
278    * handler.
279    */
280   sigc::slot_base& connect_impl_(bool notify, const sigc::slot_base& slot, bool after);
281 
282   /** Connects a signal handler to a signal.
283    * @see connect_impl_(bool notify, const sigc::slot_base& slot, bool after).
284    *
285    * @newin{2,48}
286    */
287   sigc::slot_base& connect_impl_(bool notify, sigc::slot_base&& slot, bool after);
288 
289 private:
290   const SignalProxyInfo* info_; // Pointer to statically allocated structure.
291   const Glib::ustring detailed_name_; // signal_name[::detail_name]
292 
293   // no copy assignment
294   SignalProxyDetailed& operator=(const SignalProxyDetailed&);
295 };
296 
297 /** Proxy for signals with any number of arguments and possibly a detailed name.
298  * Use the connect() or connect_notify() method, with sigc::mem_fun() or sigc::ptr_fun()
299  * to connect signal handlers to signals.
300  */
301 template <class R, class... T>
302 class SignalProxyDetailedAnyType : public SignalProxyDetailed
303 {
304 public:
305   using SlotType = sigc::slot<R, T...>;
306   using VoidSlotType = sigc::slot<void, T...>;
307 
SignalProxyDetailedAnyType(ObjectBase * obj,const SignalProxyInfo * info,const Glib::ustring & detail_name)308   SignalProxyDetailedAnyType(
309     ObjectBase* obj, const SignalProxyInfo* info, const Glib::ustring& detail_name)
310   : SignalProxyDetailed(obj, info, detail_name)
311   {
312   }
313 
314   /** Connects a signal handler to a signal.
315    *
316    * For instance, connect( sigc::mem_fun(*this, &TheClass::on_something) );
317    *
318    * @param slot The signal handler, usually created with sigc::mem_fun() or sigc::ptr_fun().
319    * @param after Whether this signal handler should be called before or after the default signal
320    * handler.
321    */
322   sigc::connection connect(const SlotType& slot, bool after = true)
323   {
324     return sigc::connection(connect_impl_(false, slot, after));
325   }
326 
327   /** Connects a signal handler to a signal.
328    * @see connect(const SlotType& slot, bool after).
329    *
330    * @newin{2,48}
331    */
332   sigc::connection connect(SlotType&& slot, bool after = true)
333   {
334     return sigc::connection(connect_impl_(false, std::move(slot), after));
335   }
336 
337   /** Connects a signal handler without a return value to a signal.
338    * By default, the signal handler will be called before the default signal handler.
339    *
340    * For instance, connect_notify( sigc::mem_fun(*this, &TheClass::on_something) );
341    *
342    * If the signal requires signal handlers with a @c void return type,
343    * the only difference between connect() and connect_notify() is the default
344    * value of @a after.
345    *
346    * If the signal requires signal handlers with a return value of type T,
347    * connect_notify() binds <tt>return T()</tt> to the connected signal handler.
348    * For instance, if the return type is @c bool, the following two calls are equivalent.
349    * @code
350    * connect_notify( sigc::mem_fun(*this, &TheClass::on_something) );
351    * connect( sigc::bind_return<bool>(sigc::mem_fun(*this, &TheClass::on_something), false), false
352    * );
353    * @endcode
354    *
355    * @param slot The signal handler, which should have a @c void return type,
356    *        usually created with sigc::mem_fun() or sigc::ptr_fun().
357    * @param after Whether this signal handler should be called before or after the default signal
358    * handler.
359    */
360   sigc::connection connect_notify(const VoidSlotType& slot, bool after = false)
361   {
362     return sigc::connection(connect_impl_(true, slot, after));
363   }
364 
365   /** Connects a signal handler without a return value to a signal.
366    * @see connect_notify(const VoidSlotType& slot, bool after).
367    *
368    * @newin{2,48}
369    */
370   sigc::connection connect_notify(VoidSlotType&& slot, bool after = false)
371   {
372     return sigc::connection(connect_impl_(true, std::move(slot), after));
373   }
374 };
375 
376 /* Templates below has been added to avoid API break, and should not be
377  * used in a newly created code. SignalProxyDetailedAnyType class should be
378  * used instead of SignalProxyDetailed# class.
379  */
380 template <typename R>
381 using SignalProxyDetailed0 = SignalProxyDetailedAnyType<R>;
382 template <typename R, typename T1>
383 using SignalProxyDetailed1 = SignalProxyDetailedAnyType<R, T1>;
384 template <typename R, typename T1, typename T2>
385 using SignalProxyDetailed2 = SignalProxyDetailedAnyType<R, T1, T2>;
386 template <typename R, typename T1, typename T2, typename T3>
387 using SignalProxyDetailed3 = SignalProxyDetailedAnyType<R, T1, T2, T3>;
388 template <typename R, typename T1, typename T2, typename T3, typename T4>
389 using SignalProxyDetailed4 = SignalProxyDetailedAnyType<R, T1, T2, T3, T4>;
390 template <typename R, typename T1, typename T2, typename T3, typename T4, typename T5>
391 using SignalProxyDetailed5 = SignalProxyDetailedAnyType<R, T1, T2, T3, T4, T5>;
392 template <typename R, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
393 using SignalProxyDetailed6 = SignalProxyDetailedAnyType<R, T1, T2, T3, T4, T5, T6>;
394 
395 } // namespace Glib
396 
397 #endif /* _GLIBMM_SIGNALPROXY_H */
398