File indexing completed on 2024-05-05 05:37:09
0001 /* 0002 * SPDX-FileCopyrightText: 2007 Aaron Seigo <aseigo@kde.org> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "engineexplorer.h" 0008 0009 #include <QApplication> 0010 #include <QBitArray> 0011 #include <QBitmap> 0012 #include <QDialogButtonBox> 0013 #include <QMenu> 0014 #include <QStandardItemModel> 0015 #include <QUrl> 0016 0017 #include <KLocalizedString> 0018 #include <KPluginMetaData> 0019 #include <KStandardAction> 0020 #include <KStringHandler> 0021 #include <QAction> 0022 #include <QDateTime> 0023 0024 #include <Plasma5Support/PluginLoader> 0025 0026 Q_DECLARE_METATYPE(Plasma5Support::DataEngine::Data) 0027 0028 #include "modelviewer.h" 0029 #include "serviceviewer.h" 0030 #include "titlecombobox.h" 0031 0032 enum { 0033 ColumnDataSourceAndKey = 0, 0034 ColumnType = 1, 0035 ColumnValue = 2, 0036 0037 ColumnCount, 0038 }; 0039 0040 EngineExplorer::EngineExplorer(QWidget *parent) 0041 : QDialog(parent) 0042 , m_engine(nullptr) 0043 , m_sourceCount(0) 0044 , m_requestingSource(false) 0045 , m_expandButton(new QPushButton(i18n("Expand All"), this)) 0046 , m_collapseButton(new QPushButton(i18n("Collapse All"), this)) 0047 { 0048 setWindowTitle(i18n("Plasma Engine Explorer")); 0049 QWidget *mainWidget = new QWidget(this); 0050 0051 QDialogButtonBox *buttonBox = new QDialogButtonBox(this); 0052 buttonBox->addButton(m_expandButton, QDialogButtonBox::ActionRole); 0053 buttonBox->addButton(m_collapseButton, QDialogButtonBox::ActionRole); 0054 buttonBox->addButton(QDialogButtonBox::Close); 0055 0056 QVBoxLayout *layout = new QVBoxLayout(this); 0057 layout->addWidget(mainWidget); 0058 layout->addWidget(buttonBox); 0059 setLayout(layout); 0060 0061 connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); 0062 0063 setupUi(mainWidget); 0064 0065 m_dataModel = new QStandardItemModel(this); 0066 const int size = m_title->style()->pixelMetric(QStyle::PM_LargeIconSize); 0067 m_title->setIconSize(QSize(size, size)); 0068 m_title->setIcon(QIcon::fromTheme("plasma")); 0069 connect(m_engines, &QComboBox::textActivated, this, &EngineExplorer::showEngine); 0070 connect(m_sourceRequesterButton, SIGNAL(clicked(bool)), this, SLOT(requestSource())); 0071 connect(m_serviceRequesterButton, &QAbstractButton::clicked, this, &EngineExplorer::requestServiceForSource); 0072 m_data->setModel(m_dataModel); 0073 m_data->setWordWrap(true); 0074 0075 m_searchLine->setTreeView(m_data); 0076 m_searchLine->setPlaceholderText(i18n("Search")); 0077 0078 listEngines(); 0079 m_engines->setFocus(); 0080 0081 connect(m_collapseButton, &QAbstractButton::clicked, m_data, &QTreeView::collapseAll); 0082 connect(m_expandButton, &QAbstractButton::clicked, m_data, &QTreeView::expandAll); 0083 enableButtons(false); 0084 0085 addAction(KStandardAction::quit(qApp, SLOT(quit()), this)); 0086 0087 connect(m_data, &QWidget::customContextMenuRequested, this, &EngineExplorer::showDataContextMenu); 0088 m_data->setContextMenuPolicy(Qt::CustomContextMenu); 0089 connect(qApp, &QCoreApplication::aboutToQuit, this, &EngineExplorer::cleanUp); 0090 } 0091 0092 EngineExplorer::~EngineExplorer() 0093 { 0094 } 0095 0096 void EngineExplorer::cleanUp() 0097 { 0098 } 0099 0100 void EngineExplorer::setApp(const QString &app) 0101 { 0102 m_app = app; 0103 0104 if (m_engines->count() > 0) { 0105 listEngines(); 0106 } 0107 } 0108 0109 void EngineExplorer::setEngine(const QString &engine) 0110 { 0111 // find the engine in the combo box 0112 const int index = m_engines->findText(engine); 0113 if (index != -1) { 0114 qDebug() << QString("Engine %1 found!").arg(engine); 0115 m_engines->setCurrentIndex(index); 0116 showEngine(engine); 0117 } 0118 } 0119 0120 void EngineExplorer::setInterval(const int interval) 0121 { 0122 m_updateInterval->setValue(interval); 0123 } 0124 0125 void EngineExplorer::removeExtraRows(QStandardItem *parent, int preserve) 0126 { 0127 if (parent->rowCount() > preserve) { 0128 parent->removeRows(preserve, parent->rowCount() - preserve); 0129 } 0130 } 0131 0132 void EngineExplorer::dataUpdated(const QString &source, const Plasma5Support::DataEngine::Data &data) 0133 { 0134 QList<QStandardItem *> items = m_dataModel->findItems(source, Qt::MatchExactly); 0135 0136 if (items.isEmpty()) { 0137 return; 0138 } 0139 0140 QStandardItem *parent = items.first(); 0141 0142 int rows = showData(parent, data); 0143 removeExtraRows(parent, rows); 0144 } 0145 0146 void EngineExplorer::listEngines() 0147 { 0148 m_engines->clear(); 0149 QVector<KPluginMetaData> engines = Plasma5Support::PluginLoader::listDataEngineMetaData(m_app); 0150 std::sort(engines.begin(), engines.end(), [](auto lhs, auto rhs) { 0151 if (lhs.category() < rhs.category()) { 0152 return true; 0153 } 0154 if (lhs.category() == rhs.category()) { 0155 return lhs.name() < rhs.name(); 0156 } 0157 return false; 0158 }); 0159 0160 for (const KPluginMetaData &engine : std::as_const(engines)) { 0161 m_engines->addItem(QIcon::fromTheme(engine.iconName()), engine.pluginId()); 0162 } 0163 0164 m_engines->setCurrentIndex(-1); 0165 } 0166 0167 void EngineExplorer::showEngine(const QString &name) 0168 { 0169 m_sourceRequester->setEnabled(false); 0170 m_sourceRequesterButton->setEnabled(false); 0171 m_serviceRequester->setEnabled(false); 0172 m_serviceRequesterButton->setEnabled(false); 0173 enableButtons(false); 0174 m_dataModel->clear(); 0175 m_dataModel->setColumnCount(ColumnCount); 0176 QStringList headers{i18n("DataSource/Key"), i18n("Type"), i18n("Value")}; 0177 m_dataModel->setHorizontalHeaderLabels(headers); 0178 m_data->setColumnWidth(ColumnDataSourceAndKey, 200); 0179 m_engine = nullptr; 0180 m_sourceCount = 0; 0181 0182 m_engineName = name; 0183 if (m_engineName.isEmpty()) { 0184 updateTitle(); 0185 return; 0186 } 0187 0188 if (auto res = KPluginFactory::instantiatePlugin<Plasma5Support::DataEngine>( 0189 KPluginMetaData::findPluginById(QStringLiteral("plasma5support/dataengine"), m_engineName))) { 0190 m_engine = res.plugin; 0191 } else { 0192 m_engineName.clear(); 0193 updateTitle(); 0194 return; 0195 } 0196 0197 // qDebug() << "showing engine " << m_engine->objectName(); 0198 // qDebug() << "we have " << sources.count() << " data sources"; 0199 connect(m_engine, &Plasma5Support::DataEngine::sourceAdded, this, &EngineExplorer::addSource); 0200 connect(m_engine, &Plasma5Support::DataEngine::sourceRemoved, this, &EngineExplorer::removeSource); 0201 const QStringList &sources = m_engine->sources(); 0202 for (const QString &source : sources) { 0203 // qDebug() << "adding " << source; 0204 addSource(source); 0205 } 0206 0207 m_sourceRequesterButton->setEnabled(true); 0208 m_updateInterval->setEnabled(true); 0209 m_sourceRequester->setEnabled(true); 0210 m_sourceRequester->setFocus(); 0211 m_serviceRequester->setEnabled(true); 0212 m_serviceRequesterButton->setEnabled(true); 0213 updateTitle(); 0214 } 0215 0216 void EngineExplorer::addSource(const QString &source) 0217 { 0218 // qDebug() << "adding" << source; 0219 QList<QStandardItem *> items = m_dataModel->findItems(source, Qt::MatchExactly); 0220 if (!items.isEmpty()) { 0221 // qDebug() << "er... already there?"; 0222 return; 0223 } 0224 0225 QStandardItem *parent = new QStandardItem(source); 0226 parent->setToolTip(source); 0227 m_dataModel->appendRow(parent); 0228 0229 // qDebug() << "getting data for source " << source; 0230 if (!m_requestingSource || m_sourceRequester->text() != source) { 0231 // qDebug() << "connecting up now"; 0232 m_engine->connectSource(source, this); 0233 } 0234 0235 ++m_sourceCount; 0236 updateTitle(); 0237 0238 enableButtons(true); 0239 } 0240 0241 void EngineExplorer::removeSource(const QString &source) 0242 { 0243 const QList<QStandardItem *> items = m_dataModel->findItems(source, Qt::MatchExactly); 0244 0245 if (items.count() < 1) { 0246 return; 0247 } 0248 0249 for (QStandardItem *item : items) { 0250 m_dataModel->removeRow(item->row()); 0251 } 0252 0253 --m_sourceCount; 0254 m_engine->disconnectSource(source, this); 0255 updateTitle(); 0256 } 0257 0258 void EngineExplorer::requestSource() 0259 { 0260 requestSource(m_sourceRequester->text()); 0261 } 0262 0263 void EngineExplorer::requestServiceForSource() 0264 { 0265 ServiceViewer *viewer = new ServiceViewer(m_engine, m_serviceRequester->text()); 0266 viewer->show(); 0267 } 0268 0269 void EngineExplorer::requestSource(const QString &source) 0270 { 0271 if (!m_engine || source.isEmpty()) { 0272 return; 0273 } 0274 0275 qDebug() << "request source" << source; 0276 m_requestingSource = true; 0277 m_engine->connectSource(source, this, (uint)m_updateInterval->value()); 0278 m_requestingSource = false; 0279 } 0280 0281 void EngineExplorer::showDataContextMenu(const QPoint &point) 0282 { 0283 QModelIndex index = m_data->indexAt(point); 0284 if (index.isValid()) { 0285 while (index.parent().isValid()) { 0286 index = index.parent(); 0287 } 0288 0289 if (index.column() != 0) { 0290 index = m_dataModel->index(index.row(), 0); 0291 } 0292 0293 const QString source = index.data().toString(); 0294 QMenu menu; 0295 menu.addSection(source); 0296 QAction *service = menu.addAction(i18n("Get associated service")); 0297 QAction *model = menu.addAction(i18n("Get associated model")); 0298 QAction *update = menu.addAction(i18n("Update source now")); 0299 QAction *remove = menu.addAction(i18n("Remove source")); 0300 0301 QAction *activated = menu.exec(m_data->viewport()->mapToGlobal(point)); 0302 if (activated == service) { 0303 ServiceViewer *viewer = new ServiceViewer(m_engine, source); 0304 viewer->show(); 0305 } else if (activated == model) { 0306 ModelViewer *viewer = new ModelViewer(m_engine, source); 0307 viewer->show(); 0308 } else if (activated == update) { 0309 m_engine->connectSource(source, this); 0310 // Plasma5Support::DataEngine::Data data = m_engine->query(source); 0311 } else if (activated == remove) { 0312 removeSource(source); 0313 } 0314 } 0315 } 0316 0317 QString EngineExplorer::convertToString(const QVariant &value) 0318 { 0319 switch (value.type()) { 0320 case QVariant::BitArray: { 0321 return i18np("<1 bit>", "<%1 bits>", value.toBitArray().size()); 0322 } 0323 case QVariant::Bitmap: { 0324 QBitmap bitmap = value.value<QBitmap>(); 0325 return QString("<%1x%2px - %3bpp>").arg(bitmap.width()).arg(bitmap.height()).arg(bitmap.depth()); 0326 } 0327 case QVariant::ByteArray: { 0328 // Return the array size if it is not displayable 0329 if (value.toString().isEmpty()) { 0330 return i18np("<1 byte>", "<%1 bytes>", value.toByteArray().size()); 0331 } else { 0332 return value.toString(); 0333 } 0334 } 0335 case QVariant::Image: { 0336 QImage image = value.value<QImage>(); 0337 return QString("<%1x%2px - %3bpp>").arg(image.width()).arg(image.height()).arg(image.depth()); 0338 } 0339 case QVariant::Line: { 0340 QLine line = value.toLine(); 0341 return QString("<x1:%1, y1:%2, x2:%3, y2:%4>").arg(line.x1()).arg(line.y1()).arg(line.x2()).arg(line.y2()); 0342 } 0343 case QVariant::LineF: { 0344 QLineF lineF = value.toLineF(); 0345 return QString("<x1:%1, y1:%2, x2:%3, y2:%4>").arg(lineF.x1()).arg(lineF.y1()).arg(lineF.x2()).arg(lineF.y2()); 0346 } 0347 case QVariant::Locale: { 0348 return QString("%1").arg(value.toLocale().name()); 0349 } 0350 case QVariant::Map: { 0351 QVariantMap map = value.toMap(); 0352 QString str = i18np("<1 item>", "<%1 items>", map.size()); 0353 0354 for (auto it = map.constBegin(); it != map.constEnd(); it++) { 0355 str += "\n" + it.key() + ": " + convertToString(it.value()); 0356 } 0357 0358 return str; 0359 } 0360 case QVariant::Pixmap: { 0361 QPixmap pixmap = value.value<QPixmap>(); 0362 return QString("<%1x%2px - %3bpp>").arg(pixmap.width()).arg(pixmap.height()).arg(pixmap.depth()); 0363 } 0364 case QVariant::Point: { 0365 QPoint point = value.toPoint(); 0366 return QString("<x:%1, y:%2>").arg(point.x()).arg(point.y()); 0367 } 0368 case QVariant::PointF: { 0369 QPointF pointF = value.toPointF(); 0370 return QString("<x:%1, y:%2>").arg(pointF.x()).arg(pointF.y()); 0371 } 0372 case QVariant::Rect: { 0373 QRect rect = value.toRect(); 0374 return QString("<x:%1, y:%2, w:%3, h:%4>").arg(rect.x()).arg(rect.y()).arg(rect.width()).arg(rect.height()); 0375 } 0376 case QVariant::RectF: { 0377 QRectF rectF = value.toRectF(); 0378 return QString("<x:%1, y:%2, w:%3, h:%4>").arg(rectF.x()).arg(rectF.y()).arg(rectF.width()).arg(rectF.height()); 0379 } 0380 case QVariant::RegularExpression: { 0381 return value.toRegularExpression().pattern(); 0382 } 0383 case QVariant::Region: { 0384 QRect region = value.value<QRegion>().boundingRect(); 0385 return QString("<x:%1, y:%2, w:%3, h:%4>").arg(region.x()).arg(region.y()).arg(region.width()).arg(region.height()); 0386 } 0387 case QVariant::Size: { 0388 QSize size = value.toSize(); 0389 return QString("<w:%1, h:%2>").arg(size.width()).arg(size.height()); 0390 } 0391 case QVariant::SizeF: { 0392 QSizeF sizeF = value.toSizeF(); 0393 return QString("<w:%1, h:%2>").arg(sizeF.width()).arg(sizeF.height()); 0394 } 0395 case QVariant::Url: { 0396 return QString("%1").arg(value.toUrl().toString()); 0397 } 0398 case QVariant::StringList: { 0399 return QString("%1").arg(value.toStringList().join(", ")); 0400 } 0401 case QVariant::Date: { 0402 return QString("%1").arg(value.toDate().toString()); 0403 } 0404 case QVariant::DateTime: { 0405 return QString("%1").arg(value.toDateTime().toString()); 0406 } 0407 case QVariant::Time: { 0408 return QString("%1").arg(value.toTime().toString()); 0409 } 0410 default: { 0411 if (QLatin1String(value.typeName()) == "QDateTime") { 0412 return QString("%1").arg(value.value<QDateTime>().toString()); 0413 } 0414 0415 Plasma5Support::DataEngine::Data data = value.value<Plasma5Support::DataEngine::Data>(); 0416 if (!data.isEmpty()) { 0417 QStringList result; 0418 0419 for (auto it = data.constBegin(); it != data.constEnd(); it++) { 0420 result << (it.key() + ": " + convertToString(it.value())); 0421 } 0422 0423 return result.join("\n"); 0424 } else if (value.canConvert<QString>()) { 0425 QString str = value.toString(); 0426 if (str.isEmpty()) { 0427 return i18nc("The user did a query to a dataengine and it returned empty data", "<empty>"); 0428 } else { 0429 return str; 0430 } 0431 } 0432 0433 return i18nc("A the dataengine returned something that the humble view on the engineexplorer can't display, like a picture", "<not displayable>"); 0434 } 0435 } 0436 } 0437 0438 int EngineExplorer::showData(QStandardItem *parent, Plasma5Support::DataEngine::Data data) 0439 { 0440 int rowCount = 0; 0441 for (auto it = data.constBegin(); it != data.constEnd(); it++) { 0442 showData(parent, rowCount++, it.key(), it.value()); 0443 } 0444 return rowCount; 0445 } 0446 0447 void EngineExplorer::showData(QStandardItem *parent, int row, const QString &key, const QVariant &value) 0448 { 0449 static_assert(ColumnDataSourceAndKey == 0); 0450 // QTreeView only expands tree for children of column #zero. 0451 QStandardItem *current = new QStandardItem(key); 0452 current->setToolTip(key); 0453 parent->setChild(row, ColumnDataSourceAndKey, current); 0454 0455 const char *typeName = value.typeName(); 0456 int rowCount = 0; 0457 0458 if (value.userType() == qMetaTypeId<QList<QVariantMap>>()) { 0459 // this case is a bit special, and has to be handled before generic QVariantList 0460 const QList<QVariantMap> list = value.value<QList<QVariantMap>>(); 0461 rowCount = showContainerData(parent, current, row, typeName, list); 0462 } else if (value.canConvert<QVariantList>() && value.type() != QVariant::String && value.type() != QVariant::ByteArray) { 0463 const QVariantList list = value.toList(); 0464 rowCount = showContainerData(parent, current, row, typeName, list); 0465 } else if (value.canConvert<QVariantMap>()) { 0466 const QVariantMap map = value.toMap(); 0467 rowCount = showContainerData(parent, current, row, typeName, map); 0468 } else { 0469 parent->setChild(row, ColumnType, new QStandardItem(typeName)); 0470 // clang-format off 0471 QStandardItem *item = value.canConvert<QIcon>() 0472 ? new QStandardItem(value.value<QIcon>(), "") 0473 : new QStandardItem(convertToString(value)); 0474 // clang-format on 0475 item->setToolTip(item->text()); 0476 parent->setChild(row, ColumnValue, item); 0477 // leave rowCount at value 0 0478 } 0479 removeExtraRows(current, rowCount); 0480 } 0481 0482 int EngineExplorer::showContainerData(QStandardItem *parent, QStandardItem *current, int row, const char *typeName, const QList<QVariantMap> &list) 0483 { 0484 QStandardItem *typeItem = new QStandardItem(typeName); 0485 typeItem->setToolTip(typeItem->text()); 0486 parent->setChild(row, ColumnType, typeItem); 0487 parent->setChild(row, ColumnValue, new QStandardItem(ki18ncp("Length of the list", "<%1 item>", "<%1 items>").subs(list.length()).toString())); 0488 0489 int rowCount = 0; 0490 for (const QVariantMap &map : list) { 0491 showData(current, rowCount, QString::number(rowCount), map); 0492 rowCount++; 0493 } 0494 return rowCount; 0495 } 0496 0497 int EngineExplorer::showContainerData(QStandardItem *parent, QStandardItem *current, int row, const char *typeName, const QVariantList &list) 0498 { 0499 QStandardItem *typeItem = new QStandardItem(typeName); 0500 typeItem->setToolTip(typeItem->text()); 0501 parent->setChild(row, ColumnType, typeItem); 0502 parent->setChild(row, ColumnValue, new QStandardItem(ki18ncp("Length of the list", "<%1 item>", "<%1 items>").subs(list.length()).toString())); 0503 0504 int rowCount = 0; 0505 for (const QVariant &var : list) { 0506 showData(current, rowCount, QString::number(rowCount), var); 0507 rowCount++; 0508 } 0509 return rowCount; 0510 } 0511 0512 int EngineExplorer::showContainerData(QStandardItem *parent, QStandardItem *current, int row, const char *typeName, const QVariantMap &map) 0513 { 0514 QStandardItem *typeItem = new QStandardItem(typeName); 0515 typeItem->setToolTip(typeItem->text()); 0516 parent->setChild(row, ColumnType, typeItem); 0517 parent->setChild(row, ColumnValue, new QStandardItem(ki18ncp("Size of the map", "<%1 pair>", "<%1 pairs>").subs(map.size()).toString())); 0518 0519 int rowCount = 0; 0520 for (auto it = map.constBegin(); it != map.constEnd(); it++) { 0521 showData(current, rowCount++, it.key(), it.value()); 0522 } 0523 return rowCount; 0524 } 0525 0526 void EngineExplorer::updateTitle() 0527 { 0528 if (!m_engine || !m_engine->metadata().isValid()) { 0529 m_title->setIcon(QIcon::fromTheme("plasma")); 0530 m_title->setText(i18n("Plasma DataEngine Explorer")); 0531 return; 0532 } 0533 0534 m_title->setText(ki18ncp("The name of the engine followed by the number of data sources", "%1 Engine - 1 data source", "%1 Engine - %2 data sources") 0535 .subs(KStringHandler::capwords(m_engine->metadata().name())) 0536 .subs(m_sourceCount) 0537 .toString()); 0538 0539 if (m_engine->metadata().iconName().isEmpty()) { 0540 m_title->setIcon(QIcon::fromTheme("plasma")); 0541 } else { 0542 m_title->setIcon(QIcon::fromTheme(m_engine->metadata().iconName())); 0543 } 0544 } 0545 0546 void EngineExplorer::enableButtons(bool enable) 0547 { 0548 if (m_expandButton) { 0549 m_expandButton->setEnabled(enable); 0550 } 0551 0552 if (m_collapseButton) { 0553 m_collapseButton->setEnabled(enable); 0554 } 0555 } 0556 0557 #include "moc_engineexplorer.cpp"