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