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