summaryrefslogtreecommitdiff
path: root/src/QGlib/signal.h
blob: b51f770a8c3db3cfbff2ca53109f4b291534ba29 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
/*
    Copyright (C) 2010  George Kiagiadakis <kiagiadakis.george@gmail.com>

    This library is free software; you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published
    by the Free Software Foundation; either version 2.1 of the License, or
    (at your option) any later version.

    This program 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 Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef QGLIB_SIGNAL_H
#define QGLIB_SIGNAL_H

#include "global.h"
#include <QtCore/QString>
#include <QtCore/QFlags>
#include <QtCore/QSharedData>

#if !QGLIB_HAVE_CXX0X
//boost::bind restricts us to 9 arguments. if you need more,
//consider using a modern compiler with variadic template support ;)
# define QGLIB_SIGNAL_MAX_ARGS 9
# include <boost/preprocessor.hpp>
#endif

//Qt's emit will clash
#if defined(emit)
# if defined(Q_CC_GNU)
#  warning "The emit keyword is defined and will be undefined here to compile QGlib::emit."
#  warning "It is recommended to compile your project with QT_NO_KEYWORDS defined."
# elif defined(Q_CC_MSVC)
#  pragma message("Warning: The emit keyword is defined and will be undefined here to compile QGlib::emit.")
#  pragma message("Warning: It is recommended to compile your project with QT_NO_KEYWORDS defined.")
# endif
# undef emit
# define QT_NO_EMIT //undocumented Qt macro that skips "#define emit" in qglobal.h
#endif

namespace QGlib {

class SignalHandler
{
public:
    inline SignalHandler()
        : m_instance(NULL), m_id(0) {}

    inline bool isValid() const { return m_instance != NULL; }

    bool isConnected() const;
    void disconnect();

    void block();
    void unblock();

    inline SignalHandler(void *instance, ulong id)
        : m_instance(instance), m_id(id) {}

private:
    void *m_instance;
    ulong m_id;
};

/*! \headerfile signal.h <QGlib/Signal>
 * \brief Helper class providing integration with the GObject signal system
 *
 * Signals are a generic notification mechanism. Each signal is bound to a
 * certain instantiatable Type and can be emitted on any instance of this type.
 *
 * This class allows you to inspect, emit and connect to these signals. To inspect
 * the signals of a certain Type, you can use the lookup() and listSignals() methods.
 * To emit or connect a signal, use the emit() and connect() methods respectively.
 *
 * For more information, please read the relevant Glib documentation.
 */
class Signal
{
public:
    enum SignalFlag {
        RunFirst = 1<<0,
        RunLast = 1<<1,
        RunCleanup = 1<<2,
        NoRecurse = 1<<3,
        Detailed = 1<<4,
        Action = 1<<5,
        NoHooks = 1<<6
    };
    Q_DECLARE_FLAGS(SignalFlags, SignalFlag);

    Signal(const Signal & other);
    Signal & operator=(const Signal & other);
    virtual ~Signal();

    bool isValid() const;

    uint id() const;
    QString name() const;
    SignalFlags flags() const;

    Type instanceType() const;
    Type returnType() const;
    QList<Type> paramTypes() const;

    static Signal lookup(const char *name, Type type);
    static QList<Signal> listSignals(Type type);

private:
    Signal(uint id);

    struct Private;
    QSharedDataPointer<Private> d;
};


/*! These flags define options that can be passed to connect() to modify its behaviour. */
enum ConnectFlag { //codegen: skip=true
    /*! If ConnectAfter is specified, the slot passed to connect() will be invoked after the
     * default signal handler of this signal has been called. See the Glib signals
     * documentation for more details on this parameter.
     */
    ConnectAfter = 1,
    /*! If PassSender is specified, the slot passed to connect() will receive as the first
     * argument a pointer to the sender of the signal. Thus, your slot should be defined
     * like this:
     * \code
     * void mySlot(const QGlib::ObjectPtr & sender, const Foo & firstArgument, ...);
     * \endcode
     */
    PassSender = 2
};
Q_DECLARE_FLAGS(ConnectFlags, ConnectFlag);

#if QGLIB_HAVE_CXX0X

/*! Emits a signal on a specified \a instance with the specified arguments.
 *
 * This method will convert all the specified arguments to GValues using Value::set()
 * and will then call the non-templated emit() method, which is a wrapper for g_signal_emitv().
 * The returned value from the signal (if the signal returns a value) will be converted
 * from GValue to the type R using Value::get() and will be returned. If some argument
 * is not of the type that the signal expects, a warning will be printed to stderr at runtime
 * and the signal will not be emitted. If the return value is not of the type that the signal
 * returns, the signal will be emitted, but a default-constructed value for the type R will
 * be returned and a warning will be printed to stderr.
 *
 * Note that since the implementation uses Value::set() to convert the GValues into the
 * specified types, the same rules that apply to Value::set() apply here (i.e. you should
 * only use the types of the bindings and not the C types, which means QGst::ObjectPtr instead
 * of GstObject*, etc...).
 *
 * Emitting a signal is useful for the so-called Action signals. These are meant to be emitted
 * from the application and not connected to. They are more like dynamic methods that can be
 * identified with a string.
 *
 * \note This method makes use of C++0x features (namely, variadic templates and rvalue
 * references). If your compiler does not support them, a hacky implementation using boost's
 * preprocessor, function and bind libraries will be compiled instead. That version has a
 * limit of 9 arguments.
 *
 * \param instance The instance of the object on which the signal will be emitted. You can pass
 * a RefPointer as an instance without any problems; it will automatically cast to void*.
 * \param detailedSignal The name of the signal that you want to emit, with an optional
 * detail if the signal is detailed. The detail may be specified with the following syntax:
 * "signal::detail".
 * \param args The arguments that will be passed to the signal.
 * \returns The return value of the signal.
 */
template <typename R, typename... Args>
R emit(void *instance, const char *detailedSignal, const Args & ... args);

/*! Connects a signal to a specified \a slot.
 *
 * This method will generate a set of template functions and classes that bind on a GClosure.
 * When the signal is emitted, this GClosure will be invoked and its templated marshaller
 * function will take care of converting the parameters of the signal (which are given as
 * GValues) to the types that the \a slot expects. In case the types do not match, a warning
 * will be printed to stderr at runtime and the \a slot will not be invoked. You are
 * responsible for defining the \a slot with the correct arguments!
 *
 * Note that since the implementation uses Value::get() to convert the GValues into the
 * specified types, the same rules that apply to Value::get() apply here (i.e. you should
 * only use the types of the bindings and not the C types, which means QGst::ObjectPtr instead
 * of GstObject*, etc...).
 *
 * \note
 * \li You can use const references for the arguments of the slot to avoid unnecessary
 * copying of objects. The marshaller will always hold one copy of them during the execution
 * of your \a slot.
 * \li This method makes use of C++0x features (namely, variadic templates and rvalue
 * references). If your compiler does not support them, a hacky implementation using boost's
 * preprocessor, function and bind libraries will be compiled instead. That version has a
 * limit of 9 slot arguments.
 *
 * \param instance The instance of the object that emits this signal. You can pass
 * a RefPointer as an instance without any problems; it will automatically cast to void*.
 * \param detailedSignal The name of the signal that you want to connect to, with an optional
 * detail if the signal is detailed. The detail may be specified with the following syntax:
 * "signal::detail".
 * \param receiver The instance of the class on which \a slot will be invoked.
 * \param slot A pointer to a member function that will be invoked when the signal is emitted.
 * \param flags See ConnectFlag.
 * \returns A SignalHandler instance, which can be used to disconnect or block this handler.
 * Note that the return type of this function is subject to change before a stable release is made.
 */
template <typename T, typename R, typename... Args>
SignalHandler connect(void *instance, const char *detailedSignal,
                      T *receiver, R (T::*slot)(Args...), ConnectFlags flags = 0);

#else //QGLIB_HAVE_CXX0X

# define QGLIB_SIGNAL_EMIT_DECLARATION(z, n, data) \
    template <typename R BOOST_PP_ENUM_TRAILING_PARAMS(n, typename A) > \
    R emit(void *instance, const char *detailedSignal \
           BOOST_PP_ENUM_TRAILING_BINARY_PARAMS(n, const A, & a) );

# define QGLIB_SIGNAL_CONNECT_DECLARATION(z, n, data) \
    template <typename T, typename R BOOST_PP_ENUM_TRAILING_PARAMS(n, typename A) > \
    SignalHandler connect(void *instance, const char *detailedSignal, \
                          T *receiver, R (T::*slot)(BOOST_PP_ENUM_PARAMS(n, A)), \
                          ConnectFlags flags = 0);

BOOST_PP_REPEAT_FROM_TO(0, BOOST_PP_INC(QGLIB_SIGNAL_MAX_ARGS), QGLIB_SIGNAL_EMIT_DECLARATION, dummy)
BOOST_PP_REPEAT_FROM_TO(0, BOOST_PP_INC(QGLIB_SIGNAL_MAX_ARGS), QGLIB_SIGNAL_CONNECT_DECLARATION, dummy)

# undef QGLIB_SIGNAL_CONNECT_DECLARATION
# undef QGLIB_SIGNAL_EMIT_DECLARATION

#endif //QGLIB_HAVE_CXX0X

} //namespace QGlib

Q_DECLARE_OPERATORS_FOR_FLAGS(QGlib::Signal::SignalFlags)
Q_DECLARE_OPERATORS_FOR_FLAGS(QGlib::ConnectFlags)

#define IN_QGLIB_SIGNAL_H
# include "emitimpl.h"
# include "connectimpl.h"
#undef IN_QGLIB_SIGNAL_H

#if defined(QGLIB_SIGNAL_MAX_ARGS)
# undef QGLIB_SIGNAL_MAX_ARGS
#endif

#endif