1/****************************************************************************
2**
3** Copyright (C) 2017 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 "selectsignaldialog_p.h"
30
31#include "ui_selectsignaldialog.h"
32
33#include <QtDesigner/abstractformeditor.h>
34#include <QtDesigner/abstractpromotioninterface.h>
35
36#include "abstractintrospection_p.h"
37#include "metadatabase_p.h"
38#include "widgetdatabase_p.h"
39
40#include <QtWidgets/qapplication.h>
41#include <QtWidgets/qdesktopwidget.h>
42#include <QtWidgets/qpushbutton.h>
43#include <QtGui/qstandarditemmodel.h>
44#include <QtCore/qitemselectionmodel.h>
45#include <QtCore/qvariant.h>
46#include <QtCore/qvector.h>
47
48#include <algorithm>
49
50QT_BEGIN_NAMESPACE
51
52namespace qdesigner_internal {
53
54enum { MethodRole = Qt::UserRole + 1 };
55
56using Methods = QVector<SelectSignalDialog::Method>;
57
58SelectSignalDialog::SelectSignalDialog(QWidget *parent)
59 : QDialog(parent)
60 , m_ui(new Ui::SelectSignalDialog)
61 , m_model(new QStandardItemModel(0, 1, this))
62{
63 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
64 m_ui->setupUi(this);
65 m_okButton = m_ui->buttonBox->button(which: QDialogButtonBox::Ok);
66
67 m_ui->signalList->setModel(m_model);
68 connect(sender: m_ui->signalList->selectionModel(), signal: &QItemSelectionModel::currentChanged,
69 receiver: this, slot: &SelectSignalDialog::currentChanged);
70 connect(sender: m_ui->signalList, signal: &QTreeView::activated,
71 receiver: this, slot: &SelectSignalDialog::activated);
72 const QRect availableGeometry = QApplication::desktop()->availableGeometry(widget: this);
73 resize(w: availableGeometry.width() / 5, h: availableGeometry.height() / 2);
74}
75
76SelectSignalDialog::~SelectSignalDialog()
77{
78 delete m_ui;
79}
80
81SelectSignalDialog::Method SelectSignalDialog::selectedMethod() const
82{
83 return methodFromIndex(m_ui->signalList->currentIndex());
84}
85
86SelectSignalDialog::Method SelectSignalDialog::methodFromIndex(const QModelIndex &index) const
87{
88 if (index.isValid()) {
89 const QStandardItem *item = m_model->itemFromIndex(index);
90 const QVariant data = item->data(role: MethodRole);
91 if (data.canConvert<Method>())
92 return data.value<Method>();
93 }
94 return Method();
95}
96
97static QStandardItem *createTopLevelItem(const QString &text)
98{
99 QStandardItem *result = new QStandardItem(text);
100 result->setFlags(Qt::ItemIsEnabled);
101 return result;
102}
103
104static bool signatureLessThan(const SelectSignalDialog::Method &m1, const SelectSignalDialog::Method &m2)
105{
106 return m1.signature.compare(s: m2.signature) < 0;
107}
108
109// Append a class with alphabetically sorted methods to the model
110static void appendClass(const QString &className, Methods methods, QStandardItemModel *model)
111{
112 if (methods.isEmpty())
113 return;
114 std::sort(first: methods.begin(), last: methods.end(), comp: signatureLessThan);
115 QStandardItem *topLevelItem = createTopLevelItem(text: className);
116 model->appendRow(aitem: topLevelItem);
117 for (const SelectSignalDialog::Method &m : qAsConst(t&: methods)) {
118 QStandardItem *item = new QStandardItem(m.signature);
119 item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
120 item->setData(value: QVariant::fromValue(value: m), role: MethodRole);
121 topLevelItem->appendRow(aitem: item);
122 }
123}
124
125static QString declaredInClass(const QDesignerMetaObjectInterface *metaObject, const QString &member)
126{
127 // Find class whose superclass does not contain the method.
128 const QDesignerMetaObjectInterface *meta = metaObject;
129
130 for (;;) {
131 const QDesignerMetaObjectInterface *tmpMeta = meta->superClass();
132 if (tmpMeta == nullptr)
133 break;
134 if (tmpMeta->indexOfMethod(method: member) == -1)
135 break;
136 meta = tmpMeta;
137 }
138 return meta->className();
139}
140
141static inline QString msgNoSignals()
142{
143 return QCoreApplication::translate(context: "QDesignerTaskMenu", key: "no signals available");
144}
145
146void SelectSignalDialog::populate(QDesignerFormEditorInterface *core, QObject *object,
147 const QString &defaultSignal)
148{
149 m_okButton->setEnabled(false);
150
151 populateModel(core, object);
152
153 if (m_model->rowCount() == 0) {
154 m_model->appendRow(aitem: createTopLevelItem(text: msgNoSignals()));
155 return;
156 }
157
158 m_ui->signalList->expandAll();
159 m_ui->signalList->resizeColumnToContents(column: 0);
160
161 QModelIndex selectedIndex;
162 if (defaultSignal.isEmpty()) {
163 selectedIndex = m_model->index(row: 0, column: 0, parent: m_model->index(row: 0, column: 0, parent: QModelIndex())); // first method
164 } else {
165 const auto items = m_model->findItems(text: defaultSignal, flags: Qt::MatchExactly | Qt::MatchRecursive, column: 0);
166 if (!items.isEmpty())
167 selectedIndex = m_model->indexFromItem(item: items.constFirst());
168 }
169
170 if (selectedIndex.isValid())
171 m_ui->signalList->setCurrentIndex(selectedIndex);
172}
173
174void SelectSignalDialog::populateModel(QDesignerFormEditorInterface *core, QObject *object)
175{
176 m_model->removeRows(row: 0, count: m_model->rowCount());
177
178 // Populate methods list in reverse order, starting from derived class.
179 if (object->isWidgetType() && qobject_cast<WidgetDataBase *>(object: core->widgetDataBase())) {
180 const QDesignerWidgetDataBaseInterface *db = core->widgetDataBase();
181 const QString promotedClassName = promotedCustomClassName(core, w: static_cast<QWidget *>(object));
182 const int index = db->indexOfClassName(className: promotedClassName);
183 if (index >= 0) {
184 Methods methods;
185 WidgetDataBaseItem* item = static_cast<WidgetDataBaseItem*>(db->item(index));
186 const QStringList fakeSignals = item->fakeSignals();
187 for (const QString &fakeSignal : fakeSignals)
188 methods.append(t: SelectSignalDialog::Method(promotedClassName, fakeSignal));
189 appendClass(className: promotedClassName, methods, model: m_model);
190 }
191 }
192
193 // fake signals
194 if (qdesigner_internal::MetaDataBase *metaDataBase
195 = qobject_cast<qdesigner_internal::MetaDataBase *>(object: core->metaDataBase())) {
196 Methods methods;
197 qdesigner_internal::MetaDataBaseItem *item = metaDataBase->metaDataBaseItem(object);
198 Q_ASSERT(item);
199 const QStringList fakeSignals = item->fakeSignals();
200 for (const QString &fakeSignal : fakeSignals)
201 methods.append(t: SelectSignalDialog::Method(item->customClassName(), fakeSignal));
202 appendClass(className: item->customClassName(), methods, model: m_model);
203 }
204
205 // "real" signals
206 if (const QDesignerMetaObjectInterface *metaObject = core->introspection()->metaObject(object)) {
207 QString lastClassName;
208 Methods methods;
209 for (int i = metaObject->methodCount() - 1; i >= 0; --i) {
210 const QDesignerMetaMethodInterface *metaMethod = metaObject->method(index: i);
211 if (metaMethod->methodType() == QDesignerMetaMethodInterface::Signal) {
212 const QString signature = metaMethod->signature();
213 const QString className = declaredInClass(metaObject, member: signature);
214 if (lastClassName.isEmpty()) {
215 lastClassName = className;
216 } else if (className != lastClassName) {
217 appendClass(className: lastClassName, methods, model: m_model);
218 lastClassName = className;
219 methods.clear();
220 }
221 methods.append(t: SelectSignalDialog::Method(className, signature,
222 metaMethod->parameterNames()));
223 }
224 }
225 appendClass(className: lastClassName, methods, model: m_model);
226 }
227}
228
229void SelectSignalDialog::activated(const QModelIndex &index)
230{
231 if (methodFromIndex(index).isValid())
232 m_okButton->animateClick();
233}
234
235void SelectSignalDialog::currentChanged(const QModelIndex &current, const QModelIndex &)
236{
237 m_okButton->setEnabled(methodFromIndex(index: current).isValid());
238}
239
240} // namespace qdesigner_internal
241
242QT_END_NAMESPACE
243

source code of qttools/src/designer/src/lib/shared/selectsignaldialog.cpp