1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Designer of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include "signalsloteditorwindow.h"
30#include "signalsloteditor_p.h"
31#include "signalsloteditor.h"
32#include "signalslot_utils_p.h"
33
34#include <iconloader_p.h>
35#include <spacer_widget_p.h>
36#include <qlayout_widget_p.h>
37
38#include <QtDesigner/abstractformwindow.h>
39#include <QtDesigner/abstractformeditor.h>
40#include <QtDesigner/abstractformwindowmanager.h>
41#include <QtDesigner/qextensionmanager.h>
42#include <QtDesigner/abstractintegration.h>
43#include <QtDesigner/container.h>
44#include <QtDesigner/abstractmetadatabase.h>
45#include <QtDesigner/abstractformwindowcursor.h>
46#include <abstractdialoggui_p.h>
47
48#include <QtCore/qabstractitemmodel.h>
49#include <QtCore/qdebug.h>
50#include <QtWidgets/qaction.h>
51#include <QtWidgets/qbuttongroup.h>
52#include <QtWidgets/qmenu.h>
53#include <QtCore/qsortfilterproxymodel.h>
54#include <QtGui/qstandarditemmodel.h>
55#include <QtWidgets/qcombobox.h>
56#include <QtWidgets/qapplication.h>
57#include <QtWidgets/qitemdelegate.h>
58#include <QtWidgets/qitemeditorfactory.h>
59#include <QtWidgets/qtreeview.h>
60#include <QtWidgets/qheaderview.h>
61#include <QtWidgets/qboxlayout.h>
62#include <QtWidgets/qtoolbutton.h>
63#include <QtWidgets/qbuttongroup.h>
64#include <QtWidgets/qtoolbar.h>
65
66QT_BEGIN_NAMESPACE
67
68// Add suitable form widgets to a list of objects for the signal slot
69// editor. Prevent special widgets from showing up there.
70static void addWidgetToObjectList(const QWidget *w, QStringList &r)
71{
72 const QMetaObject *mo = w->metaObject();
73 if (mo != &QLayoutWidget::staticMetaObject && mo != &Spacer::staticMetaObject) {
74 const QString name = w->objectName().trimmed();
75 if (!name.isEmpty())
76 r.push_back(t: name);
77 }
78}
79
80static QStringList objectNameList(QDesignerFormWindowInterface *form)
81{
82 QStringList result;
83
84 QWidget *mainContainer = form->mainContainer();
85 if (!mainContainer)
86 return result;
87
88 // Add main container container pages (QStatusBar, QWizardPages) etc.
89 // to the list. Pages of containers on the form are not added, however.
90 if (const QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension *>(manager: form->core()->extensionManager(), object: mainContainer)) {
91 const int count = c->count();
92 for (int i = 0 ; i < count; i++)
93 addWidgetToObjectList(w: c->widget(index: i), r&: result);
94 }
95
96 const QDesignerFormWindowCursorInterface *cursor = form->cursor();
97 const int widgetCount = cursor->widgetCount();
98 for (int i = 0; i < widgetCount; ++i)
99 addWidgetToObjectList(w: cursor->widget(index: i), r&: result);
100
101 const QDesignerMetaDataBaseInterface *mdb = form->core()->metaDataBase();
102
103 // Add managed actions and actions with managed menus
104 const auto actions = mainContainer->findChildren<QAction*>();
105 for (QAction *a : actions) {
106 if (!a->isSeparator()) {
107 if (QMenu *menu = a->menu()) {
108 if (mdb->item(object: menu))
109 result.push_back(t: menu->objectName());
110 } else {
111 if (mdb->item(object: a))
112 result.push_back(t: a->objectName());
113 }
114 }
115 }
116
117 // Add managed buttons groups
118 const auto buttonGroups = mainContainer->findChildren<QButtonGroup *>();
119 for (QButtonGroup * b : buttonGroups) {
120 if (mdb->item(object: b))
121 result.append(t: b->objectName());
122 }
123
124 result.sort();
125 return result;
126}
127
128namespace qdesigner_internal {
129
130// ------------ ConnectionModel
131
132ConnectionModel::ConnectionModel(QObject *parent) :
133 QAbstractItemModel(parent)
134{
135}
136
137void ConnectionModel::setEditor(SignalSlotEditor *editor)
138{
139 if (m_editor == editor)
140 return;
141 beginResetModel();
142
143 if (m_editor) {
144 disconnect(sender: m_editor.data(), signal: &SignalSlotEditor::connectionAdded,
145 receiver: this, slot: &ConnectionModel::connectionAdded);
146 disconnect(sender: m_editor.data(), signal: &SignalSlotEditor::connectionRemoved,
147 receiver: this, slot: &ConnectionModel::connectionRemoved);
148 disconnect(sender: m_editor.data(), signal: &SignalSlotEditor::aboutToRemoveConnection,
149 receiver: this, slot: &ConnectionModel::aboutToRemoveConnection);
150 disconnect(sender: m_editor.data(), signal: &SignalSlotEditor::aboutToAddConnection,
151 receiver: this, slot: &ConnectionModel::aboutToAddConnection);
152 disconnect(sender: m_editor.data(), signal: &SignalSlotEditor::connectionChanged,
153 receiver: this, slot: &ConnectionModel::connectionChanged);
154 }
155 m_editor = editor;
156 if (m_editor) {
157 connect(sender: m_editor.data(), signal: &SignalSlotEditor::connectionAdded,
158 receiver: this, slot: &ConnectionModel::connectionAdded);
159 connect(sender: m_editor.data(), signal: &SignalSlotEditor::connectionRemoved,
160 receiver: this, slot: &ConnectionModel::connectionRemoved);
161 connect(sender: m_editor.data(), signal: &SignalSlotEditor::aboutToRemoveConnection,
162 receiver: this, slot: &ConnectionModel::aboutToRemoveConnection);
163 connect(sender: m_editor.data(), signal: &SignalSlotEditor::aboutToAddConnection,
164 receiver: this, slot: &ConnectionModel::aboutToAddConnection);
165 connect(sender: m_editor.data(), signal: &SignalSlotEditor::connectionChanged,
166 receiver: this, slot: &ConnectionModel::connectionChanged);
167 }
168 endResetModel();
169}
170
171QVariant ConnectionModel::headerData(int section, Qt::Orientation orientation,
172 int role) const
173{
174 if (orientation == Qt::Vertical || role != Qt::DisplayRole)
175 return QVariant();
176
177 static const QVariant senderTitle = tr(s: "Sender");
178 static const QVariant signalTitle = tr(s: "Signal");
179 static const QVariant receiverTitle = tr(s: "Receiver");
180 static const QVariant slotTitle = tr(s: "Slot");
181
182 switch (section) {
183 case 0:
184 return senderTitle;
185 case 1:
186 return signalTitle;
187 case 2:
188 return receiverTitle;
189 case 3:
190 return slotTitle;
191 }
192 return QVariant();
193}
194
195QModelIndex ConnectionModel::index(int row, int column,
196 const QModelIndex &parent) const
197{
198 if (parent.isValid() || !m_editor)
199 return QModelIndex();
200 if (row < 0 || row >= m_editor->connectionCount())
201 return QModelIndex();
202 return createIndex(arow: row, acolumn: column);
203}
204
205Connection *ConnectionModel::indexToConnection(const QModelIndex &index) const
206{
207 if (!index.isValid() || !m_editor)
208 return nullptr;
209 if (index.row() < 0 || index.row() >= m_editor->connectionCount())
210 return nullptr;
211 return m_editor->connection(i: index.row());
212}
213
214QModelIndex ConnectionModel::connectionToIndex(Connection *con) const
215{
216 Q_ASSERT(m_editor);
217 return createIndex(arow: m_editor->indexOfConnection(con), acolumn: 0);
218}
219
220QModelIndex ConnectionModel::parent(const QModelIndex&) const
221{
222 return QModelIndex();
223}
224
225int ConnectionModel::rowCount(const QModelIndex &parent) const
226{
227 if (parent.isValid() || !m_editor)
228 return 0;
229 return m_editor->connectionCount();
230}
231
232int ConnectionModel::columnCount(const QModelIndex &parent) const
233{
234 if (parent.isValid())
235 return 0;
236 return 4;
237}
238
239const SignalSlotConnection *ConnectionModel::connectionAt(const QModelIndex &index) const
240{
241 const int row = index.row();
242 return m_editor != nullptr && row >= 0 && row < m_editor->connectionCount()
243 ? static_cast<const SignalSlotConnection*>(m_editor->connection(i: row))
244 : nullptr;
245}
246
247QVariant ConnectionModel::data(const QModelIndex &index, int role) const
248{
249 enum { deprecatedMember = 0 };
250
251 const SignalSlotConnection *con = connectionAt(index);
252 if (con == nullptr)
253 return QVariant();
254
255 // Mark deprecated slots red/italic. Not currently in use (historically for Qt 3 slots in Qt 4),
256 // but may be used again in the future.
257 switch (role) {
258 case Qt::ForegroundRole:
259 return deprecatedMember ? QColor(Qt::red) : QVariant();
260 case Qt::FontRole:
261 if (deprecatedMember) {
262 QFont font = QApplication::font();
263 font.setItalic(true);
264 return font;
265 }
266 return QVariant();
267 case Qt::DisplayRole:
268 case Qt::EditRole:
269 return ConnectionModel::columnText(con, column: index.column());
270 default:
271 break;
272 }
273
274 return QVariant();
275}
276
277QString ConnectionModel::columnText(const SignalSlotConnection *con, int column)
278{
279 static const QString senderDefault = tr(s: "<sender>");
280 static const QString signalDefault = tr(s: "<signal>");
281 static const QString receiverDefault = tr(s: "<receiver>");
282 static const QString slotDefault = tr(s: "<slot>");
283
284 switch (column) {
285 case 0: {
286 const QString sender = con->sender();
287 return sender.isEmpty() ? senderDefault : sender;
288 }
289 case 1: {
290 const QString signalName = con->signal();
291 return signalName.isEmpty() ? signalDefault : signalName;
292 }
293 case 2: {
294 const QString receiver = con->receiver();
295 return receiver.isEmpty() ? receiverDefault : receiver;
296 }
297 case 3: {
298 const QString slotName = con->slot();
299 return slotName.isEmpty() ? slotDefault : slotName;
300 }
301 }
302 return QString();
303}
304
305bool ConnectionModel::setData(const QModelIndex &index, const QVariant &data, int)
306{
307 if (!index.isValid() || !m_editor)
308 return false;
309 if (data.type() != QVariant::String)
310 return false;
311
312 SignalSlotConnection *con = static_cast<SignalSlotConnection*>(m_editor->connection(i: index.row()));
313 QDesignerFormWindowInterface *form = m_editor->formWindow();
314
315 QString s = data.toString();
316 switch (index.column()) {
317 case 0:
318 if (!s.isEmpty() && !objectNameList(form).contains(str: s))
319 s.clear();
320 m_editor->setSource(con, obj_name: s);
321 break;
322 case 1:
323 if (!memberFunctionListContains(core: form->core(), object: con->object(type: CETypes::EndPoint::Source), type: SignalMember, signature: s))
324 s.clear();
325 m_editor->setSignal(con, member: s);
326 break;
327 case 2:
328 if (!s.isEmpty() && !objectNameList(form).contains(str: s))
329 s.clear();
330 m_editor->setTarget(con, obj_name: s);
331 break;
332 case 3:
333 if (!memberFunctionListContains(core: form->core(), object: con->object(type: CETypes::EndPoint::Target), type: SlotMember, signature: s))
334 s.clear();
335 m_editor->setSlot(con, member: s);
336 break;
337 }
338
339 return true;
340}
341
342void ConnectionModel::connectionAdded(Connection*)
343{
344 endInsertRows();
345}
346
347void ConnectionModel::connectionRemoved(int)
348{
349 endRemoveRows();
350}
351
352void ConnectionModel::aboutToRemoveConnection(Connection *con)
353{
354 Q_ASSERT(m_editor);
355 int idx = m_editor->indexOfConnection(con);
356 beginRemoveRows(parent: QModelIndex(), first: idx, last: idx);
357}
358
359void ConnectionModel::aboutToAddConnection(int idx)
360{
361 Q_ASSERT(m_editor);
362 beginInsertRows(parent: QModelIndex(), first: idx, last: idx);
363}
364
365Qt::ItemFlags ConnectionModel::flags(const QModelIndex&) const
366{
367 return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled;
368}
369
370void ConnectionModel::connectionChanged(Connection *con)
371{
372 Q_ASSERT(m_editor);
373 const int idx = m_editor->indexOfConnection(con);
374 SignalSlotConnection *changedCon = static_cast<SignalSlotConnection*>(m_editor->connection(i: idx));
375 SignalSlotConnection *c = nullptr;
376 for (int i=0; i<m_editor->connectionCount(); ++i) {
377 if (i == idx)
378 continue;
379 c = static_cast<SignalSlotConnection*>(m_editor->connection(i));
380 if (c->sender() == changedCon->sender() && c->signal() == changedCon->signal()
381 && c->receiver() == changedCon->receiver() && c->slot() == changedCon->slot()) {
382 const QString message = tr(s: "The connection already exists!<br>%1").arg(a: changedCon->toString());
383 m_editor->formWindow()->core()->dialogGui()->message(parent: m_editor->parentWidget(), context: QDesignerDialogGuiInterface::SignalSlotEditorMessage,
384 icon: QMessageBox::Warning, title: tr(s: "Signal and Slot Editor"), text: message, buttons: QMessageBox::Ok);
385 break;
386 }
387 }
388 emit dataChanged(topLeft: createIndex(arow: idx, acolumn: 0), bottomRight: createIndex(arow: idx, acolumn: 3));
389}
390
391void ConnectionModel::updateAll()
392{
393 emit dataChanged(topLeft: index(row: 0, column: 0), bottomRight: index(row: rowCount() - 1, column: columnCount() - 1));
394}
395}
396
397namespace {
398// ---------------------- InlineEditorModel
399
400class InlineEditorModel : public QStandardItemModel
401{
402 Q_OBJECT
403public:
404 enum { TitleItem = 1 };
405
406 InlineEditorModel(int rows, int cols, QObject *parent = nullptr);
407
408 void addTitle(const QString &title);
409 void addTextList(const QMap<QString, bool> &text_list);
410 void addText(const QString &text);
411 bool isTitle(int idx) const;
412
413 int findText(const QString &text) const;
414
415 Qt::ItemFlags flags(const QModelIndex &index) const override;
416};
417
418InlineEditorModel::InlineEditorModel(int rows, int cols, QObject *parent)
419 : QStandardItemModel(rows, cols, parent)
420{
421}
422
423void InlineEditorModel::addTitle(const QString &title)
424{
425 const int cnt = rowCount();
426 insertRows(row: cnt, count: 1);
427 QModelIndex cat_idx = index(row: cnt, column: 0);
428 setData(index: cat_idx, value: QString(title + QLatin1Char(':')), role: Qt::DisplayRole);
429 setData(index: cat_idx, value: TitleItem, role: Qt::UserRole);
430 QFont font = QApplication::font();
431 font.setBold(true);
432 setData(index: cat_idx, value: font, role: Qt::FontRole);
433}
434
435bool InlineEditorModel::isTitle(int idx) const
436{
437 if (idx == -1)
438 return false;
439
440 return data(index: index(row: idx, column: 0), role: Qt::UserRole).toInt() == TitleItem;
441}
442
443void InlineEditorModel::addText(const QString &text)
444{
445 const int cnt = rowCount();
446 insertRows(row: cnt, count: 1);
447 setData(index: index(row: cnt, column: 0), value: text, role: Qt::DisplayRole);
448}
449
450void InlineEditorModel::addTextList(const QMap<QString, bool> &text_list)
451{
452 int cnt = rowCount();
453 insertRows(row: cnt, count: text_list.size());
454 QFont font = QApplication::font();
455 font.setItalic(true);
456 QVariant fontVariant = QVariant::fromValue(value: font);
457 QMap<QString, bool>::ConstIterator it = text_list.constBegin();
458 const QMap<QString, bool>::ConstIterator itEnd = text_list.constEnd();
459 while (it != itEnd) {
460 const QModelIndex text_idx = index(row: cnt++, column: 0);
461 setData(index: text_idx, value: it.key(), role: Qt::DisplayRole);
462 if (it.value()) {
463 setData(index: text_idx, value: fontVariant, role: Qt::FontRole);
464 setData(index: text_idx, value: QColor(Qt::red), role: Qt::ForegroundRole);
465 }
466 ++it;
467 }
468}
469
470Qt::ItemFlags InlineEditorModel::flags(const QModelIndex &index) const
471{
472 return isTitle(idx: index.row())
473 ? Qt::ItemFlags(Qt::ItemIsEnabled)
474 : Qt::ItemFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
475}
476
477int InlineEditorModel::findText(const QString &text) const
478{
479 const int cnt = rowCount();
480 for (int i = 0; i < cnt; ++i) {
481 const QModelIndex idx = index(row: i, column: 0);
482 if (data(index: idx, role: Qt::UserRole).toInt() == TitleItem)
483 continue;
484 if (data(index: idx, role: Qt::DisplayRole).toString() == text)
485 return i;
486 }
487 return -1;
488}
489
490// ------------ InlineEditor
491class InlineEditor : public QComboBox
492{
493 Q_OBJECT
494 Q_PROPERTY(QString text READ text WRITE setText USER true)
495public:
496 InlineEditor(QWidget *parent = nullptr);
497
498 QString text() const;
499 void setText(const QString &text);
500
501 void addTitle(const QString &title);
502 void addText(const QString &text);
503 void addTextList(const QMap<QString, bool> &text_list);
504
505private slots:
506 void checkSelection(int idx);
507
508private:
509 InlineEditorModel *m_model;
510 int m_idx = -1;
511};
512
513InlineEditor::InlineEditor(QWidget *parent) :
514 QComboBox(parent)
515{
516 setModel(m_model = new InlineEditorModel(0, 4, this));
517 setFrame(false);
518 m_idx = -1;
519 connect(sender: this, signal: QOverload<int>::of(ptr: &QComboBox::activated),
520 receiver: this, slot: &InlineEditor::checkSelection);
521}
522
523void InlineEditor::checkSelection(int idx)
524{
525 if (idx == m_idx)
526 return;
527
528 if (m_model->isTitle(idx))
529 setCurrentIndex(m_idx);
530 else
531 m_idx = idx;
532}
533
534void InlineEditor::addTitle(const QString &title)
535{
536 m_model->addTitle(title);
537}
538
539void InlineEditor::addTextList(const QMap<QString, bool> &text_list)
540{
541 m_model->addTextList(text_list);
542}
543
544void InlineEditor::addText(const QString &text)
545{
546 m_model->addText(text);
547}
548
549QString InlineEditor::text() const
550{
551 return currentText();
552}
553
554void InlineEditor::setText(const QString &text)
555{
556 m_idx = m_model->findText(text);
557 if (m_idx == -1)
558 m_idx = 0;
559 setCurrentIndex(m_idx);
560}
561
562// ------------------ ConnectionDelegate
563
564class ConnectionDelegate : public QItemDelegate
565{
566 Q_OBJECT
567public:
568 ConnectionDelegate(QWidget *parent = nullptr);
569
570 void setForm(QDesignerFormWindowInterface *form);
571
572 QWidget *createEditor(QWidget *parent,
573 const QStyleOptionViewItem &option,
574 const QModelIndex &index) const override;
575
576private slots:
577 void emitCommitData();
578
579private:
580 QDesignerFormWindowInterface *m_form;
581};
582
583ConnectionDelegate::ConnectionDelegate(QWidget *parent)
584 : QItemDelegate(parent)
585{
586 m_form = nullptr;
587
588 static QItemEditorFactory *factory = nullptr;
589 if (factory == nullptr) {
590 factory = new QItemEditorFactory;
591 QItemEditorCreatorBase *creator
592 = new QItemEditorCreator<InlineEditor>("text");
593 factory->registerEditor(userType: QVariant::String, creator);
594 }
595
596 setItemEditorFactory(factory);
597}
598
599void ConnectionDelegate::setForm(QDesignerFormWindowInterface *form)
600{
601 m_form = form;
602}
603
604QWidget *ConnectionDelegate::createEditor(QWidget *parent,
605 const QStyleOptionViewItem &option,
606 const QModelIndex &index) const
607{
608 if (m_form == nullptr)
609 return nullptr;
610
611 QWidget *w = QItemDelegate::createEditor(parent, option, index);
612 InlineEditor *inline_editor = qobject_cast<InlineEditor*>(object: w);
613 Q_ASSERT(inline_editor != nullptr);
614 const QAbstractItemModel *model = index.model();
615
616 const QModelIndex obj_name_idx = model->index(row: index.row(), column: index.column() <= 1 ? 0 : 2);
617 const QString obj_name = model->data(index: obj_name_idx, role: Qt::DisplayRole).toString();
618
619 switch (index.column()) {
620 case 0:
621 case 2: { // object names
622 const QStringList &obj_name_list = objectNameList(form: m_form);
623 QMap<QString, bool> markedNameList;
624 markedNameList.insert(akey: tr(s: "<object>"), avalue: false);
625 inline_editor->addTextList(text_list: markedNameList);
626 markedNameList.clear();
627 for (const QString &name : obj_name_list)
628 markedNameList.insert(akey: name, avalue: false);
629 inline_editor->addTextList(text_list: markedNameList);
630 }
631 break;
632 case 1:
633 case 3: { // signals, slots
634 const qdesigner_internal::MemberType type = index.column() == 1 ? qdesigner_internal::SignalMember : qdesigner_internal::SlotMember;
635 const QModelIndex peer_index = model->index(row: index.row(), column: type == qdesigner_internal::SignalMember ? 3 : 1);
636 const QString peer = model->data(index: peer_index, role: Qt::DisplayRole).toString();
637
638 const qdesigner_internal::ClassesMemberFunctions class_list = qdesigner_internal::reverseClassesMemberFunctions(obj_name, member_type: type, peer, form: m_form);
639
640 inline_editor->addText(text: type == qdesigner_internal::SignalMember ? tr(s: "<signal>") : tr(s: "<slot>"));
641 for (const qdesigner_internal::ClassMemberFunctions &classInfo : class_list) {
642 if (classInfo.m_className.isEmpty() || classInfo.m_memberList.isEmpty())
643 continue;
644 // Mark deprecated members by passing bool=true.
645 QMap<QString, bool> markedMemberList;
646 for (const QString &member : qAsConst(t: classInfo.m_memberList))
647 markedMemberList.insert(akey: member, avalue: false);
648 inline_editor->addTitle(title: classInfo.m_className);
649 inline_editor->addTextList(text_list: markedMemberList);
650 }
651 }
652 break;
653 default:
654 break;
655 }
656
657 connect(sender: inline_editor, signal: QOverload<int>::of(ptr: &QComboBox::activated),
658 receiver: this, slot: &ConnectionDelegate::emitCommitData);
659
660 return inline_editor;
661}
662
663void ConnectionDelegate::emitCommitData()
664{
665 InlineEditor *editor = qobject_cast<InlineEditor*>(object: sender());
666 emit commitData(editor);
667}
668
669}
670
671namespace qdesigner_internal {
672
673/*******************************************************************************
674** SignalSlotEditorWindow
675*/
676
677SignalSlotEditorWindow::SignalSlotEditorWindow(QDesignerFormEditorInterface *core,
678 QWidget *parent) :
679 QWidget(parent),
680 m_view(new QTreeView),
681 m_editor(nullptr),
682 m_add_button(new QToolButton),
683 m_remove_button(new QToolButton),
684 m_core(core),
685 m_model(new ConnectionModel(this)),
686 m_proxy_model(new QSortFilterProxyModel(this)),
687 m_handling_selection_change(false)
688{
689 m_proxy_model->setSourceModel(m_model);
690 m_view->setModel(m_proxy_model);
691 m_view->setSortingEnabled(true);
692 m_view->setItemDelegate(new ConnectionDelegate(this));
693 m_view->setEditTriggers(QAbstractItemView::DoubleClicked
694 | QAbstractItemView::EditKeyPressed);
695 m_view->setRootIsDecorated(false);
696 m_view->setTextElideMode (Qt::ElideMiddle);
697 connect(sender: m_view->selectionModel(), signal: &QItemSelectionModel::currentChanged,
698 receiver: this, slot: &SignalSlotEditorWindow::updateUi);
699 connect(sender: m_view->header(), signal: &QHeaderView::sectionDoubleClicked,
700 receiver: m_view, slot: &QTreeView::resizeColumnToContents);
701
702 QVBoxLayout *layout = new QVBoxLayout(this);
703 layout->setContentsMargins(QMargins());
704 layout->setSpacing(0);
705
706 QToolBar *toolBar = new QToolBar;
707 toolBar->setIconSize(QSize(22, 22));
708 m_add_button->setIcon(createIconSet(QStringLiteral("plus.png")));
709 connect(sender: m_add_button, signal: &QAbstractButton::clicked, receiver: this, slot: &SignalSlotEditorWindow::addConnection);
710 toolBar->addWidget(widget: m_add_button);
711
712 m_remove_button->setIcon(createIconSet(QStringLiteral("minus.png")));
713 connect(sender: m_remove_button, signal: &QAbstractButton::clicked, receiver: this, slot: &SignalSlotEditorWindow::removeConnection);
714 toolBar->addWidget(widget: m_remove_button);
715
716 layout->addWidget(toolBar);
717 layout->addWidget(m_view);
718
719 connect(sender: core->formWindowManager(),
720 signal: &QDesignerFormWindowManagerInterface::activeFormWindowChanged,
721 receiver: this, slot: &SignalSlotEditorWindow::setActiveFormWindow);
722
723 updateUi();
724}
725
726void SignalSlotEditorWindow::setActiveFormWindow(QDesignerFormWindowInterface *form)
727{
728 QDesignerIntegrationInterface *integration = m_core->integration();
729
730 if (!m_editor.isNull()) {
731 disconnect(sender: m_view->selectionModel(),
732 signal: &QItemSelectionModel::currentChanged,
733 receiver: this, slot: &SignalSlotEditorWindow::updateEditorSelection);
734 disconnect(sender: m_editor.data(), signal: &SignalSlotEditor::connectionSelected,
735 receiver: this, slot: &SignalSlotEditorWindow::updateDialogSelection);
736 disconnect(sender: m_editor.data(), signal: &SignalSlotEditor::connectionAdded,
737 receiver: this, slot: &SignalSlotEditorWindow::resizeColumns);
738 if (integration) {
739 disconnect(sender: integration, signal: &QDesignerIntegrationInterface::objectNameChanged,
740 receiver: this, slot: &SignalSlotEditorWindow::objectNameChanged);
741 }
742 }
743
744 m_editor = form->findChild<SignalSlotEditor*>();
745 m_model->setEditor(m_editor);
746 if (!m_editor.isNull()) {
747 ConnectionDelegate *delegate
748 = qobject_cast<ConnectionDelegate*>(object: m_view->itemDelegate());
749 if (delegate != nullptr)
750 delegate->setForm(form);
751
752 connect(sender: m_view->selectionModel(),
753 signal: &QItemSelectionModel::currentChanged,
754 receiver: this, slot: &SignalSlotEditorWindow::updateEditorSelection);
755 connect(sender: m_editor.data(), signal: &SignalSlotEditor::connectionSelected,
756 receiver: this, slot: &SignalSlotEditorWindow::updateDialogSelection);
757 connect(sender: m_editor.data(), signal: &SignalSlotEditor::connectionAdded,
758 receiver: this, slot: &SignalSlotEditorWindow::resizeColumns);
759 if (integration) {
760 connect(sender: integration, signal: &QDesignerIntegrationInterface::objectNameChanged,
761 receiver: this, slot: &SignalSlotEditorWindow::objectNameChanged);
762 }
763 }
764
765 resizeColumns();
766 updateUi();
767}
768
769void SignalSlotEditorWindow::updateDialogSelection(Connection *con)
770{
771 if (m_handling_selection_change || m_editor == nullptr)
772 return;
773
774 QModelIndex index = m_proxy_model->mapFromSource(sourceIndex: m_model->connectionToIndex(con));
775 if (!index.isValid() || index == m_view->currentIndex())
776 return;
777 m_handling_selection_change = true;
778 m_view->scrollTo(index, hint: QTreeView::EnsureVisible);
779 m_view->setCurrentIndex(index);
780 m_handling_selection_change = false;
781
782 updateUi();
783}
784
785void SignalSlotEditorWindow::updateEditorSelection(const QModelIndex &index)
786{
787 if (m_handling_selection_change || m_editor == nullptr)
788 return;
789
790 if (m_editor == nullptr)
791 return;
792
793 Connection *con = m_model->indexToConnection(index: m_proxy_model->mapToSource(proxyIndex: index));
794 if (m_editor->selected(con))
795 return;
796 m_handling_selection_change = true;
797 m_editor->selectNone();
798 m_editor->setSelected(con, sel: true);
799 m_handling_selection_change = false;
800
801 updateUi();
802}
803
804void SignalSlotEditorWindow::objectNameChanged(QDesignerFormWindowInterface *, QObject *, const QString &, const QString &)
805{
806 if (m_editor)
807 m_model->updateAll();
808}
809
810void SignalSlotEditorWindow::addConnection()
811{
812 if (m_editor.isNull())
813 return;
814
815 m_editor->addEmptyConnection();
816 updateUi();
817}
818
819void SignalSlotEditorWindow::removeConnection()
820{
821 if (m_editor.isNull())
822 return;
823
824 m_editor->deleteSelected();
825 updateUi();
826}
827
828void SignalSlotEditorWindow::updateUi()
829{
830 m_add_button->setEnabled(!m_editor.isNull());
831 m_remove_button->setEnabled(!m_editor.isNull() && m_view->currentIndex().isValid());
832}
833
834void SignalSlotEditorWindow::resizeColumns()
835{
836 for (int c = 0, count = m_model->columnCount(); c < count; ++c)
837 m_view->resizeColumnToContents(column: c);
838}
839
840} // namespace qdesigner_internal
841
842QT_END_NAMESPACE
843
844#include "signalsloteditorwindow.moc"
845

source code of qttools/src/designer/src/components/signalsloteditor/signalsloteditorwindow.cpp