// Copyright (C) 2017 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "selectsignaldialog_p.h" #include "ui_selectsignaldialog.h" #include #include #include "abstractintrospection_p.h" #include "metadatabase_p.h" #include "widgetdatabase_p.h" #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE namespace qdesigner_internal { enum { MethodRole = Qt::UserRole + 1 }; using Methods = QList; SelectSignalDialog::SelectSignalDialog(QWidget *parent) : QDialog(parent) , m_ui(new QT_PREPEND_NAMESPACE(Ui)::SelectSignalDialog) , m_model(new QStandardItemModel(0, 1, this)) { m_ui->setupUi(this); m_okButton = m_ui->buttonBox->button(QDialogButtonBox::Ok); m_ui->signalList->setModel(m_model); connect(m_ui->signalList->selectionModel(), &QItemSelectionModel::currentChanged, this, &SelectSignalDialog::currentChanged); connect(m_ui->signalList, &QTreeView::activated, this, &SelectSignalDialog::activated); const QRect availableGeometry = screen()->geometry(); resize(availableGeometry.width() / 5, availableGeometry.height() / 2); } SelectSignalDialog::~SelectSignalDialog() { delete m_ui; } SelectSignalDialog::Method SelectSignalDialog::selectedMethod() const { return methodFromIndex(m_ui->signalList->currentIndex()); } SelectSignalDialog::Method SelectSignalDialog::methodFromIndex(const QModelIndex &index) const { if (index.isValid()) { const QStandardItem *item = m_model->itemFromIndex(index); const QVariant data = item->data(MethodRole); if (data.canConvert()) return data.value(); } return Method(); } static QStandardItem *createTopLevelItem(const QString &text) { QStandardItem *result = new QStandardItem(text); result->setFlags(Qt::ItemIsEnabled); return result; } static bool signatureLessThan(const SelectSignalDialog::Method &m1, const SelectSignalDialog::Method &m2) { return m1.signature.compare(m2.signature) < 0; } // Append a class with alphabetically sorted methods to the model static void appendClass(const QString &className, Methods methods, QStandardItemModel *model) { if (methods.isEmpty()) return; std::sort(methods.begin(), methods.end(), signatureLessThan); QStandardItem *topLevelItem = createTopLevelItem(className); model->appendRow(topLevelItem); for (const SelectSignalDialog::Method &m : std::as_const(methods)) { QStandardItem *item = new QStandardItem(m.signature); item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); item->setData(QVariant::fromValue(m), MethodRole); topLevelItem->appendRow(item); } } static QString declaredInClass(const QDesignerMetaObjectInterface *metaObject, const QString &member) { // Find class whose superclass does not contain the method. const QDesignerMetaObjectInterface *meta = metaObject; for (;;) { const QDesignerMetaObjectInterface *tmpMeta = meta->superClass(); if (tmpMeta == nullptr) break; if (tmpMeta->indexOfMethod(member) == -1) break; meta = tmpMeta; } return meta->className(); } static inline QString msgNoSignals() { return QCoreApplication::translate("QDesignerTaskMenu", "no signals available"); } void SelectSignalDialog::populate(QDesignerFormEditorInterface *core, QObject *object, const QString &defaultSignal) { m_okButton->setEnabled(false); populateModel(core, object); if (m_model->rowCount() == 0) { m_model->appendRow(createTopLevelItem(msgNoSignals())); return; } m_ui->signalList->expandAll(); m_ui->signalList->resizeColumnToContents(0); QModelIndex selectedIndex; if (defaultSignal.isEmpty()) { selectedIndex = m_model->index(0, 0, m_model->index(0, 0, QModelIndex())); // first method } else { const auto items = m_model->findItems(defaultSignal, Qt::MatchExactly | Qt::MatchRecursive, 0); if (!items.isEmpty()) selectedIndex = m_model->indexFromItem(items.constFirst()); } if (selectedIndex.isValid()) m_ui->signalList->setCurrentIndex(selectedIndex); } void SelectSignalDialog::populateModel(QDesignerFormEditorInterface *core, QObject *object) { m_model->removeRows(0, m_model->rowCount()); // Populate methods list in reverse order, starting from derived class. if (object->isWidgetType() && qobject_cast(core->widgetDataBase())) { const QDesignerWidgetDataBaseInterface *db = core->widgetDataBase(); const QString promotedClassName = promotedCustomClassName(core, static_cast(object)); const int index = db->indexOfClassName(promotedClassName); if (index >= 0) { Methods methods; WidgetDataBaseItem* item = static_cast(db->item(index)); const QStringList fakeSignals = item->fakeSignals(); for (const QString &fakeSignal : fakeSignals) methods.append(SelectSignalDialog::Method(promotedClassName, fakeSignal)); appendClass(promotedClassName, methods, m_model); } } // fake signals if (qdesigner_internal::MetaDataBase *metaDataBase = qobject_cast(core->metaDataBase())) { Methods methods; qdesigner_internal::MetaDataBaseItem *item = metaDataBase->metaDataBaseItem(object); Q_ASSERT(item); const QStringList fakeSignals = item->fakeSignals(); for (const QString &fakeSignal : fakeSignals) methods.append(SelectSignalDialog::Method(item->customClassName(), fakeSignal)); appendClass(item->customClassName(), methods, m_model); } // "real" signals if (const QDesignerMetaObjectInterface *metaObject = core->introspection()->metaObject(object)) { QString lastClassName; Methods methods; for (int i = metaObject->methodCount() - 1; i >= 0; --i) { const QDesignerMetaMethodInterface *metaMethod = metaObject->method(i); if (metaMethod->methodType() == QDesignerMetaMethodInterface::Signal) { const QString signature = metaMethod->signature(); const QString className = declaredInClass(metaObject, signature); if (lastClassName.isEmpty()) { lastClassName = className; } else if (className != lastClassName) { appendClass(lastClassName, methods, m_model); lastClassName = className; methods.clear(); } methods.append(SelectSignalDialog::Method(className, signature, metaMethod->parameterNames())); } } appendClass(lastClassName, methods, m_model); } } void SelectSignalDialog::activated(const QModelIndex &index) { if (methodFromIndex(index).isValid()) m_okButton->animateClick(); } void SelectSignalDialog::currentChanged(const QModelIndex ¤t, const QModelIndex &) { m_okButton->setEnabled(methodFromIndex(current).isValid()); } } // namespace qdesigner_internal QT_END_NAMESPACE #include "moc_selectsignaldialog_p.cpp"