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