1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qglobal.h"
5
6#include "qsignalmapper.h"
7#include "qhash.h"
8#include "qobject_p.h"
9
10QT_BEGIN_NAMESPACE
11
12class QSignalMapperPrivate : public QObjectPrivate
13{
14 Q_DECLARE_PUBLIC(QSignalMapper)
15public:
16 void _q_senderDestroyed()
17 {
18 Q_Q(QSignalMapper);
19 q->removeMappings(sender: q->sender());
20 }
21
22 template <class Signal, class Container>
23 void emitMappedValue(QObject *sender, Signal signal, const Container &mappedValues)
24 {
25 Q_Q(QSignalMapper);
26
27 auto it = mappedValues.find(sender);
28 if (it != mappedValues.end())
29 Q_EMIT(q->*signal)(*it);
30 }
31
32 void emitMappedValues(QObject *sender)
33 {
34 emitMappedValue(sender, signal: &QSignalMapper::mappedInt, mappedValues: intHash);
35 emitMappedValue(sender, signal: &QSignalMapper::mappedString, mappedValues: stringHash);
36 emitMappedValue(sender, signal: &QSignalMapper::mappedObject, mappedValues: objectHash);
37 }
38
39 QHash<QObject *, int> intHash;
40 QHash<QObject *, QString> stringHash;
41 QHash<QObject *, QObject *> objectHash;
42};
43
44/*!
45 \class QSignalMapper
46 \inmodule QtCore
47 \brief The QSignalMapper class bundles signals from identifiable senders.
48
49 \ingroup objectmodel
50
51
52 This class collects a set of parameterless signals, and re-emits
53 them with integer, string or widget parameters corresponding to
54 the object that sent the signal. Note that in most cases you can
55 use lambdas for passing custom parameters to slots. This is less
56 costly and will simplify the code.
57
58 The class supports the mapping of particular strings, integers,
59 objects and widgets with particular objects using setMapping().
60 The objects' signals can then be connected to the map() slot which
61 will emit a signal (it could be mappedInt(), mappedString()
62 and mappedObject()) with a value associated with
63 the original signalling object. Mappings can be removed later using
64 removeMappings().
65
66 Example: Suppose we want to create a custom widget that contains
67 a group of buttons (like a tool palette). One approach is to
68 connect each button's \c clicked() signal to its own custom slot;
69 but in this example we want to connect all the buttons to a
70 single slot and parameterize the slot by the button that was
71 clicked.
72
73 Here's the definition of a simple custom widget that has a single
74 signal, \c clicked(), which is emitted with the text of the button
75 that was clicked:
76
77 \snippet qsignalmapper/buttonwidget.h 0
78 \snippet qsignalmapper/buttonwidget.h 1
79
80 The only function that we need to implement is the constructor:
81
82 \snippet qsignalmapper/buttonwidget.cpp 0
83 \snippet qsignalmapper/buttonwidget.cpp 1
84 \snippet qsignalmapper/buttonwidget.cpp 2
85
86 A list of texts is passed to the constructor. A signal mapper is
87 constructed and for each text in the list a QPushButton is
88 created. We connect each button's \c clicked() signal to the
89 signal mapper's map() slot, and create a mapping in the signal
90 mapper from each button to the button's text. Finally we connect
91 the signal mapper's mappedString() signal to the custom widget's
92 \c clicked() signal. When the user clicks a button, the custom
93 widget will emit a single \c clicked() signal whose argument is
94 the text of the button the user clicked.
95
96 This class was mostly useful before lambda functions could be used as
97 slots. The example above can be rewritten simpler without QSignalMapper
98 by connecting to a lambda function.
99
100 \snippet qsignalmapper/buttonwidget.cpp 3
101
102 \sa QObject, QButtonGroup, QActionGroup
103*/
104
105/*!
106 Constructs a QSignalMapper with parent \a parent.
107*/
108QSignalMapper::QSignalMapper(QObject* parent)
109 : QObject(*new QSignalMapperPrivate, parent)
110{
111}
112
113/*!
114 Destroys the QSignalMapper.
115*/
116QSignalMapper::~QSignalMapper()
117{
118}
119
120/*!
121 Adds a mapping so that when map() is signalled from the given \a
122 sender, the signal mappedInt(\a id) is emitted.
123
124 There may be at most one integer ID for each sender.
125
126 \sa mapping()
127*/
128void QSignalMapper::setMapping(QObject *sender, int id)
129{
130 Q_D(QSignalMapper);
131 d->intHash.insert(key: sender, value: id);
132 connect(sender, SIGNAL(destroyed()), receiver: this, SLOT(_q_senderDestroyed()));
133}
134
135/*!
136 Adds a mapping so that when map() is signalled from the \a sender,
137 the signal mappedString(\a text ) is emitted.
138
139 There may be at most one text for each sender.
140*/
141void QSignalMapper::setMapping(QObject *sender, const QString &text)
142{
143 Q_D(QSignalMapper);
144 d->stringHash.insert(key: sender, value: text);
145 connect(sender, SIGNAL(destroyed()), receiver: this, SLOT(_q_senderDestroyed()));
146}
147
148/*!
149 Adds a mapping so that when map() is signalled from the \a sender,
150 the signal mappedObject(\a object ) is emitted.
151
152 There may be at most one object for each sender.
153*/
154void QSignalMapper::setMapping(QObject *sender, QObject *object)
155{
156 Q_D(QSignalMapper);
157 d->objectHash.insert(key: sender, value: object);
158 connect(sender, SIGNAL(destroyed()), receiver: this, SLOT(_q_senderDestroyed()));
159}
160
161/*!
162 Returns the sender QObject that is associated with the \a id.
163
164 \sa setMapping()
165*/
166QObject *QSignalMapper::mapping(int id) const
167{
168 Q_D(const QSignalMapper);
169 return d->intHash.key(value: id);
170}
171
172/*!
173 \overload mapping()
174*/
175QObject *QSignalMapper::mapping(const QString &id) const
176{
177 Q_D(const QSignalMapper);
178 return d->stringHash.key(value: id);
179}
180
181/*!
182 \overload mapping()
183
184 Returns the sender QObject that is associated with the \a object.
185*/
186QObject *QSignalMapper::mapping(QObject *object) const
187{
188 Q_D(const QSignalMapper);
189 return d->objectHash.key(value: object);
190}
191
192/*!
193 Removes all mappings for \a sender.
194
195 This is done automatically when mapped objects are destroyed.
196
197 \note This does not disconnect any signals. If \a sender is not destroyed
198 then this will need to be done explicitly if required.
199*/
200void QSignalMapper::removeMappings(QObject *sender)
201{
202 Q_D(QSignalMapper);
203
204 d->intHash.remove(key: sender);
205 d->stringHash.remove(key: sender);
206 d->objectHash.remove(key: sender);
207}
208
209/*!
210 This slot emits signals based on which object sends signals to it.
211*/
212void QSignalMapper::map() { map(sender: sender()); }
213
214/*!
215 This slot emits signals based on the \a sender object.
216*/
217void QSignalMapper::map(QObject *sender)
218{
219 d_func()->emitMappedValues(sender);
220}
221
222/*!
223 \fn void QSignalMapper::mappedInt(int i)
224 \since 5.15
225
226 This signal is emitted when map() is signalled from an object that
227 has an integer mapping set. The object's mapped integer is passed
228 in \a i.
229
230 \sa setMapping()
231*/
232
233/*!
234 \fn void QSignalMapper::mappedString(const QString &text)
235 \since 5.15
236
237 This signal is emitted when map() is signalled from an object that
238 has a string mapping set. The object's mapped string is passed in
239 \a text.
240
241 \sa setMapping()
242*/
243
244/*!
245 \fn void QSignalMapper::mappedObject(QObject *object)
246 \since 5.15
247
248 This signal is emitted when map() is signalled from an object that
249 has an object mapping set. The object provided by the map is passed in
250 \a object.
251
252 \sa setMapping()
253*/
254
255QT_END_NAMESPACE
256
257#include "moc_qsignalmapper.cpp"
258

source code of qtbase/src/corelib/kernel/qsignalmapper.cpp