Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
signalsloteditor.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3// Qt-Security score:significant reason:default
4
9
10#include <metadatabase_p.h>
11#include <qdesigner_formwindowcommand_p.h>
12#include <signalslotdialog_p.h>
13
14#include <QtDesigner/private/ui4_p.h>
15#include <QtDesigner/abstractformwindow.h>
16#include <QtDesigner/abstractformeditor.h>
17#include <QtDesigner/abstractmetadatabase.h>
18
19#include <QtWidgets/qapplication.h>
20#include <QtWidgets/qmenu.h>
21
22#include <QtGui/qundostack.h>
23
24#include <QtCore/qcoreapplication.h>
25#include <QtCore/qdebug.h>
26
28
29using namespace Qt::StringLiterals;
30
31namespace qdesigner_internal {
32
33/*******************************************************************************
34** SignalSlotConnection
35*/
36
37SignalSlotConnection::SignalSlotConnection(ConnectionEdit *edit, QWidget *source, QWidget *target)
39{
40}
41
42DomConnection *SignalSlotConnection::toUi() const
43{
44 DomConnection *result = new DomConnection;
45
46 result->setElementSender(sender());
47 result->setElementSignal(signal());
48 result->setElementReceiver(receiver());
49 result->setElementSlot(slot());
50
51 DomConnectionHints *hints = new DomConnectionHints;
52 QList<DomConnectionHint *> list;
53
54 QPoint sp = endPointPos(EndPoint::Source);
55 QPoint tp = endPointPos(EndPoint::Target);
56
57 DomConnectionHint *hint = new DomConnectionHint;
58 hint->setAttributeType(u"sourcelabel"_s);
59 hint->setElementX(sp.x());
60 hint->setElementY(sp.y());
61 list.append(hint);
62
63 hint = new DomConnectionHint;
64 hint->setAttributeType(u"destinationlabel"_s);
65 hint->setElementX(tp.x());
66 hint->setElementY(tp.y());
67 list.append(hint);
68
69 hints->setElementHint(list);
70 result->setElementHints(hints);
71
72 return result;
73}
74
75void SignalSlotConnection::setSignal(const QString &signal)
76{
77 m_signal = signal;
78 setLabel(EndPoint::Source, m_signal);
79}
80
81void SignalSlotConnection::setSlot(const QString &slot)
82{
83 m_slot = slot;
84 setLabel(EndPoint::Target, m_slot);
85}
86
88{
89 QObject *source = object(EndPoint::Source);
90 if (!source)
91 return QString();
92
93 SignalSlotEditor *edit = qobject_cast<SignalSlotEditor*>(this->edit());
94 Q_ASSERT(edit != nullptr);
95
96 return realObjectName(edit->formWindow()->core(), source);
97}
98
100{
101 QObject *sink = object(EndPoint::Target);
102 if (!sink)
103 return QString();
104
105 SignalSlotEditor *edit = qobject_cast<SignalSlotEditor*>(this->edit());
106 Q_ASSERT(edit != nullptr);
107 return realObjectName(edit->formWindow()->core(), sink);
108}
109
111{
112 Connection::updateVisibility();
113 if (isVisible() && (signal().isEmpty() || slot().isEmpty()))
114 setVisible(false);
115}
116
118{
119 return QCoreApplication::translate("SignalSlotConnection", "SENDER(%1), SIGNAL(%2), RECEIVER(%3), SLOT(%4)")
120 .arg(sender(), signal(), receiver(), slot());
121}
122
123SignalSlotConnection::State SignalSlotConnection::isValid(const QWidget *background) const
124{
125 const QObject *source = object(EndPoint::Source);
126 if (!source)
127 return ObjectDeleted;
128
129 const QObject *target = object(EndPoint::Target);
130 if (!target)
131 return ObjectDeleted;
132
133 if (m_slot.isEmpty() || m_signal.isEmpty())
134 return InvalidMethod;
135
136 if (const QWidget *sourceWidget = qobject_cast<const QWidget*>(source))
137 if (!background->isAncestorOf(sourceWidget))
138 return NotAncestor;
139
140 if (const QWidget *targetWidget = qobject_cast<const QWidget*>(target))
141 if (!background->isAncestorOf(targetWidget))
142 return NotAncestor;
143
144 return Valid;
145}
146
147/*******************************************************************************
148** Commands
149*/
150
152{
153public:
154 SetMemberCommand(SignalSlotConnection *con, EndPoint::Type type,
155 const QString &member, SignalSlotEditor *editor);
158private:
159 const QString m_old_member;
160 const QString m_new_member;
161 const EndPoint::Type m_type;
163 SignalSlotEditor *m_editor;
164};
165
167 const QString &member, SignalSlotEditor *editor) :
170 m_type(type),
171 m_con(con),
172 m_editor(editor)
173{
174 if (type == EndPoint::Source)
175 setText(QApplication::translate("Command", "Change signal"));
176 else
177 setText(QApplication::translate("Command", "Change slot"));
178}
179
181{
182 m_con->update();
183 if (m_type == EndPoint::Source)
184 m_con->setSignal(m_new_member);
185 else
186 m_con->setSlot(m_new_member);
187 m_con->update();
188 emit m_editor->connectionChanged(m_con);
189}
190
192{
193 m_con->update();
194 if (m_type == EndPoint::Source)
195 m_con->setSignal(m_old_member);
196 else
197 m_con->setSlot(m_old_member);
198 m_con->update();
199 emit m_editor->connectionChanged(m_con);
200}
201
202// Command to modify a connection
204{
205public:
206 explicit ModifyConnectionCommand(QDesignerFormWindowInterface *form,
208 const QString &newSignal,
209 const QString &newSlot);
212
213private:
214 SignalSlotConnection *m_conn;
215 const QString m_oldSignal;
216 const QString m_oldSlot;
217 const QString m_newSignal;
218 const QString m_newSlot;
219};
220
221ModifyConnectionCommand::ModifyConnectionCommand(QDesignerFormWindowInterface *form,
223 const QString &newSignal,
224 const QString &newSlot) :
225 QDesignerFormWindowCommand(QCoreApplication::translate("Command", "Change signal-slot connection"), form),
226 m_conn(conn),
228 m_oldSlot(conn->slot()),
231{
232}
233
235{
236 m_conn->setSignal(m_newSignal);
237 m_conn->setSlot(m_newSlot);
238}
239
241{
242 m_conn->setSignal(m_oldSignal);
243 m_conn->setSlot(m_oldSlot);
244}
245
246/*******************************************************************************
247** SignalSlotEditor
248*/
249
250SignalSlotEditor::SignalSlotEditor(QDesignerFormWindowInterface *form_window, QWidget *parent) :
251 ConnectionEdit(parent, form_window),
252 m_form_window(form_window),
253 m_showAllSignalsSlots(false)
254{
255}
256
257void SignalSlotEditor::modifyConnection(Connection *con)
258{
259 SignalSlotConnection *sigslot_con = static_cast<SignalSlotConnection*>(con);
260 ConnectDialog dialog(m_form_window,
261 sigslot_con->widget(EndPoint::Source),
262 sigslot_con->widget(EndPoint::Target),
263 m_form_window->core()->topLevel());
264
265 dialog.setSignalSlot(sigslot_con->signal(), sigslot_con->slot());
266 dialog.setShowAllSignalsSlots(m_showAllSignalsSlots);
267
268 if (dialog.exec() == QDialog::Accepted) {
269 const QString newSignal = dialog.signal();
270 const QString newSlot = dialog.slot();
271 if (sigslot_con->signal() != newSignal || sigslot_con->slot() != newSlot) {
272 ModifyConnectionCommand *cmd = new ModifyConnectionCommand(m_form_window, sigslot_con, newSignal, newSlot);
273 m_form_window->commandHistory()->push(cmd);
274 }
275 }
276
277 m_showAllSignalsSlots = dialog.showAllSignalsSlots();
278}
279
280Connection *SignalSlotEditor::createConnection(QWidget *source, QWidget *destination)
281{
282 SignalSlotConnection *con = nullptr;
283
284 Q_ASSERT(source != nullptr);
285 Q_ASSERT(destination != nullptr);
286
287 ConnectDialog dialog(m_form_window, source, destination, m_form_window->core()->topLevel());
288 dialog.setShowAllSignalsSlots(m_showAllSignalsSlots);
289
290 if (dialog.exec() == QDialog::Accepted) {
291 con = new SignalSlotConnection(this, source, destination);
292 con->setSignal(dialog.signal());
293 con->setSlot(dialog.slot());
294 }
295
296 m_showAllSignalsSlots = dialog.showAllSignalsSlots();
297
298 return con;
299}
300
301DomConnections *SignalSlotEditor::toUi() const
302{
303 DomConnections *result = new DomConnections;
304 QList<DomConnection *> list;
305
306 const int count = connectionCount();
307 list.reserve(count);
308 for (int i = 0; i < count; ++i) {
309 const SignalSlotConnection *con = static_cast<const SignalSlotConnection*>(connection(i));
310 Q_ASSERT(con != nullptr);
311
312 // If a widget's parent has been removed or moved to a different form,
313 // and the parent was not a managed widget
314 // (a page in a tab widget), we never get a widgetRemoved(). So we filter out
315 // these child widgets here (check QPointer and verify ancestor).
316 // Also, the user might demote a promoted widget or remove a fake
317 // slot in the editor, which causes the connection to become invalid
318 // once he doubleclicks on the method combo.
319 switch (con->isValid(background())) {
321 list.append(con->toUi());
322 break;
326 break;
327 }
328 }
329 result->setElementConnection(list);
330 return result;
331}
332
333QObject *SignalSlotEditor::objectByName(QWidget *topLevel, const QString &name) const
334{
335 if (name.isEmpty())
336 return nullptr;
337
338 Q_ASSERT(topLevel);
339 QObject *object = nullptr;
340 if (topLevel->objectName() == name)
341 object = topLevel;
342 else
343 object = topLevel->findChild<QObject*>(name);
344 const QDesignerMetaDataBaseInterface *mdb = formWindow()->core()->metaDataBase();
345 if (mdb->item(object))
346 return object;
347 return nullptr;
348}
349
350void SignalSlotEditor::fromUi(const DomConnections *connections, QWidget *parent)
351{
352 if (connections == nullptr)
353 return;
354
355 // For old forms, that were saved before Qt 4 times, there was no <slots>
356 // section inside ui file. Currently, when we specify custom signals or slots
357 // for the form, we add them into the <slots> section. For all signals / slots
358 // inside <slots> section uic creates string-based connections.
359 // In order to fix old forms, we detect if a signal or slot used inside connection
360 // is a custom (fake) one, like it's being done inside SignalSlotDialog.
361 // In case of a fake signal / slot we register it inside meta data base, so that
362 // the next save will add a missing <slots> section.
363 QStringList existingSlots, existingSignals;
364 SignalSlotDialog::existingMethodsFromMemberSheet(m_form_window->core(), parent,
365 existingSlots, existingSignals);
366 QStringList fakeSlots, fakeSignals;
367 SignalSlotDialog::fakeMethodsFromMetaDataBase(m_form_window->core(), parent,
368 fakeSlots, fakeSignals);
369
370 setBackground(parent);
371 clear();
372 const auto &list = connections->elementConnection();
373 for (const DomConnection *dom_con : list) {
374 QObject *source = objectByName(parent, dom_con->elementSender());
375 if (source == nullptr) {
376 qDebug("SignalSlotEditor::fromUi(): no source widget called \"%s\"",
377 dom_con->elementSender().toUtf8().constData());
378 continue;
379 }
380 QObject *destination = objectByName(parent, dom_con->elementReceiver());
381 if (destination == nullptr) {
382 qDebug("SignalSlotEditor::fromUi(): no destination widget called \"%s\"",
383 dom_con->elementReceiver().toUtf8().constData());
384 continue;
385 }
386
387 QPoint sp = QPoint(20, 20), tp = QPoint(20, 20);
388 const DomConnectionHints *dom_hints = dom_con->elementHints();
389 if (dom_hints != nullptr) {
390 const auto &hints = dom_hints->elementHint();
391 for (DomConnectionHint *hint : hints) {
392 QString attr_type = hint->attributeType();
393 QPoint p = QPoint(hint->elementX(), hint->elementY());
394 if (attr_type == "sourcelabel"_L1)
395 sp = p;
396 else if (attr_type == "destinationlabel"_L1)
397 tp = p;
398 }
399 }
400
401 const QString sourceSignal = dom_con->elementSignal();
402 if (source == parent && !existingSignals.contains(sourceSignal)
403 && !fakeSignals.contains(sourceSignal)) {
404 fakeSignals.append(sourceSignal);
405 }
406
407 const QString destSlot = dom_con->elementSlot();
408 if (destination == parent && !existingSlots.contains(destSlot)
409 && !fakeSlots.contains(destSlot)) {
410 fakeSlots.append(destSlot);
411 }
412
413
414 SignalSlotConnection *con = new SignalSlotConnection(this);
415
416 con->setEndPoint(EndPoint::Source, source, sp);
417 con->setEndPoint(EndPoint::Target, destination, tp);
418 con->setSignal(sourceSignal);
419 con->setSlot(destSlot);
420 addConnection(con);
421 }
422 SignalSlotDialog::fakeMethodsToMetaDataBase(m_form_window->core(), parent,
423 fakeSlots, fakeSignals);
424}
425
426static bool skipWidget(const QWidget *w)
427{
428 const QString name = QLatin1StringView(w->metaObject()->className());
429 if (name == "QDesignerWidget"_L1)
430 return true;
431 if (name == "QLayoutWidget"_L1)
432 return true;
433 if (name == "qdesigner_internal::FormWindow"_L1)
434 return true;
435 if (name == "Spacer"_L1)
436 return true;
437 return false;
438}
439
440QWidget *SignalSlotEditor::widgetAt(const QPoint &pos) const
441{
442 QWidget *widget = ConnectionEdit::widgetAt(pos);
443
444 if (widget == m_form_window->mainContainer())
445 return widget;
446
447 for (; widget != nullptr; widget = widget->parentWidget()) {
448 QDesignerMetaDataBaseItemInterface *item = m_form_window->core()->metaDataBase()->item(widget);
449 if (item == nullptr)
450 continue;
451 if (skipWidget(widget))
452 continue;
453 break;
454 }
455
456 return widget;
457}
458
459void SignalSlotEditor::setSignal(SignalSlotConnection *con, const QString &member)
460{
461 if (member == con->signal())
462 return;
463
464 m_form_window->beginCommand(QApplication::translate("Command", "Change signal"));
465 undoStack()->push(new SetMemberCommand(con, EndPoint::Source, member, this));
466 if (!signalMatchesSlot(m_form_window->core(), member, con->slot()))
467 undoStack()->push(new SetMemberCommand(con, EndPoint::Target, QString(), this));
468 m_form_window->endCommand();
469}
470
471void SignalSlotEditor::setSlot(SignalSlotConnection *con, const QString &member)
472{
473 if (member == con->slot())
474 return;
475
476 m_form_window->beginCommand(QApplication::translate("Command", "Change slot"));
477 undoStack()->push(new SetMemberCommand(con, EndPoint::Target, member, this));
478 if (!signalMatchesSlot(m_form_window->core(), con->signal(), member))
479 undoStack()->push(new SetMemberCommand(con, EndPoint::Source, QString(), this));
480 m_form_window->endCommand();
481}
482
483void SignalSlotEditor::setSource(Connection *_con, const QString &obj_name)
484{
485 SignalSlotConnection *con = static_cast<SignalSlotConnection*>(_con);
486
487 if (con->sender() == obj_name)
488 return;
489
490 m_form_window->beginCommand(QApplication::translate("Command", "Change sender"));
491 ConnectionEdit::setSource(con, obj_name);
492
493 QObject *sourceObject = con->object(EndPoint::Source);
494
495 if (!memberFunctionListContains(m_form_window->core(), sourceObject, SignalMember, con->signal()))
496 undoStack()->push(new SetMemberCommand(con, EndPoint::Source, QString(), this));
497
498 m_form_window->endCommand();
499}
500
501void SignalSlotEditor::setTarget(Connection *_con, const QString &obj_name)
502{
503 SignalSlotConnection *con = static_cast<SignalSlotConnection*>(_con);
504
505 if (con->receiver() == obj_name)
506 return;
507
508 m_form_window->beginCommand(QApplication::translate("Command", "Change receiver"));
509 ConnectionEdit::setTarget(con, obj_name);
510
511 QObject *targetObject = con->object(EndPoint::Target);
512 if (!memberFunctionListContains(m_form_window->core(), targetObject, SlotMember, con->slot()))
513 undoStack()->push(new SetMemberCommand(con, EndPoint::Target, QString(), this));
514
515 m_form_window->endCommand();
516}
517
519{
520 SignalSlotConnection *con = new SignalSlotConnection(this);
521 undoStack()->push(new AddConnectionCommand(this, con));
522}
523
524} // namespace qdesigner_internal
525
526QT_END_NAMESPACE
void redo() override
Applies a change to the document.
ModifyConnectionCommand(QDesignerFormWindowInterface *form, SignalSlotConnection *conn, const QString &newSignal, const QString &newSlot)
void undo() override
Reverts a change to the document.
void redo() override
Applies a change to the document.
void undo() override
Reverts a change to the document.
SetMemberCommand(SignalSlotConnection *con, EndPoint::Type type, const QString &member, SignalSlotEditor *editor)
SignalSlotConnection(ConnectionEdit *edit, QWidget *source=nullptr, QWidget *target=nullptr)
State isValid(const QWidget *background) const
virtual void setSlot(SignalSlotConnection *con, const QString &member)
virtual void setSignal(SignalSlotConnection *con, const QString &member)
Connection * createConnection(QWidget *source, QWidget *destination) override
void fromUi(const DomConnections *connections, QWidget *parent)
QWidget * widgetAt(const QPoint &pos) const override
QObject * objectByName(QWidget *topLevel, const QString &name) const
Combined button and popup list for selecting options.
Auxiliary methods to store/retrieve settings.
static bool skipWidget(const QWidget *w)