File indexing completed on 2024-05-12 15:28:15

0001 /***************************************************************************
0002     File                 : WelcomeScreenHelper.cpp
0003     Project              : LabPlot
0004     --------------------------------------------------------------------
0005     Copyright            : (C) 2019 Ferencz Kovacs (kferike98@gmail.com)
0006     Description          : Helper class for the welcome screen
0007  ***************************************************************************/
0008 
0009 /***************************************************************************
0010  *                                                                         *
0011  *  This program is free software; you can redistribute it and/or modify   *
0012  *  it under the terms of the GNU General Public License as published by   *
0013  *  the Free Software Foundation; either version 2 of the License, or      *
0014  *  (at your option) any later version.                                    *
0015  *                                                                         *
0016  *  This program is distributed in the hope that it will be useful,        *
0017  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
0018  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
0019  *  GNU General Public License for more details.                           *
0020  *                                                                         *
0021  *   You should have received a copy of the GNU General Public License     *
0022  *   along with this program; if not, write to the Free Software           *
0023  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
0024  *   Boston, MA  02110-1301  USA                                           *
0025  *                                                                         *
0026  ***************************************************************************/
0027 #include "WelcomeScreenHelper.h"
0028 #include "kdefrontend/DatasetModel.h"
0029 #include "kdefrontend/datasources/ImportDatasetWidget.h"
0030 #include "backend/datasources/DatasetHandler.h"
0031 
0032 #include <QBuffer>
0033 #include <QDebug>
0034 #include <QFile>
0035 #include <QJsonDocument>
0036 #include <QJsonArray>
0037 #include <QJsonObject>
0038 #include <QStandardPaths>
0039 #include <QTimer>
0040 #include <QUrl>
0041 
0042 #include <KConfigGroup>
0043 #include <KCompressionDevice>
0044 #include <KFilterDev>
0045 #include <KSharedConfig>
0046 
0047 /*!
0048 \class WelcomeScreenHelper
0049 \brief Helper class for the welcome screen
0050 
0051 \ingroup kdefrontend
0052 */
0053 WelcomeScreenHelper::WelcomeScreenHelper() : m_datasetWidget(new ImportDatasetWidget(0)) {
0054     m_datasetWidget->hide();
0055 
0056     QIcon icon = QIcon::fromTheme("labplot-maximize");
0057     m_maxIcon = icon.pixmap(icon.availableSizes().constFirst());
0058 
0059     icon = QIcon::fromTheme("labplot-minimize");
0060     m_minIcon = icon.pixmap(icon.availableSizes().constFirst());
0061 
0062     m_datasetModel = new DatasetModel(m_datasetWidget->getDatasetsMap());
0063 
0064     loadConfig();
0065     processExampleProjects();
0066 }
0067 
0068 WelcomeScreenHelper::~WelcomeScreenHelper() {
0069     //save width&height ratio values
0070     KConfigGroup conf(KSharedConfig::openConfig(), "WelcomeScreenHelper");
0071 
0072     int widthCount = m_widthScale.size();
0073     conf.writeEntry("width_count", widthCount);
0074     int currentWidthIndex = 0;
0075 
0076     for(auto item = m_widthScale.begin(); item != m_widthScale.end() && currentWidthIndex < widthCount; ++item) {
0077         conf.writeEntry("widthName_" + QString::number(currentWidthIndex), item.key());
0078         conf.writeEntry("widthValue_" + QString::number(currentWidthIndex), QString::number(item.value()));
0079         currentWidthIndex++;
0080     }
0081 
0082     int heightCount = m_heightScale.size();
0083     conf.writeEntry("height_count", widthCount);
0084     int currentHeightIndex = 0;
0085 
0086     for(auto item = m_heightScale.begin(); item != m_heightScale.end() && currentHeightIndex < heightCount; ++item) {
0087         conf.writeEntry("heightName_" + QString::number(currentHeightIndex), item.key());
0088         conf.writeEntry("heightValue_" + QString::number(currentHeightIndex), QString::number(item.value()));
0089         currentHeightIndex++;
0090     }
0091 }
0092 
0093 /**
0094  * @brief Loads the saved configuration
0095  */
0096 void WelcomeScreenHelper::loadConfig() {
0097     KConfigGroup conf(KSharedConfig::openConfig(), "WelcomeScreenHelper");
0098 
0099     int widthCount = conf.readEntry("width_count", -1);
0100     for(int i = 0; i < widthCount; ++i) {
0101         QString id = conf.readEntry("widthName_" + QString::number(i), "");
0102         double value = QString(conf.readEntry("widthValue_" + QString::number(i), "-1")).toDouble();
0103 
0104         if(!id.isEmpty() && value != -1)
0105             m_widthScale[id] = value;
0106     }
0107 
0108     int heightCount = conf.readEntry("height_count", -1);
0109     for(int i = 0; i < heightCount; ++i) {
0110         QString id = conf.readEntry("heightName_" + QString::number(i), "");
0111         double value = QString(conf.readEntry("heightValue_" + QString::number(i), "-1")).toDouble();
0112 
0113         if(!id.isEmpty() && value != -1)
0114             m_heightScale[id] = value;
0115     }
0116 }
0117 
0118 /**
0119  * @brief Handles a dataset being clicked in the dataset section of the WelcomeScreen.
0120  * Initiates listing information about the dataset in the previously mentioned section.
0121  *
0122  * @param category the category the dataset belongs to
0123  * @param subcategory the subcategory the dataset belongs to
0124  * @param datasetName the name of the dataset
0125  */
0126 void WelcomeScreenHelper::datasetClicked(const QString& category, const QString& subcategory, const QString& datasetName) {
0127     m_datasetWidget->setCollection("All");
0128     m_datasetWidget->setCategory(category);
0129     m_datasetWidget->setSubcategory(subcategory);
0130     m_datasetWidget->setDataset(datasetName);
0131     m_spreadsheet.reset(new Spreadsheet(i18n("Dataset%1", 1)));
0132 
0133     if(m_datasetHandler)
0134         delete m_datasetHandler;
0135     m_datasetHandler = new DatasetHandler(m_spreadsheet.get());
0136 
0137     m_datasetWidget->import(m_datasetHandler);
0138 
0139     QTimer timer;
0140     timer.setSingleShot(true);
0141     QEventLoop loop;
0142     connect(m_datasetHandler,  &DatasetHandler::downloadCompleted, &loop, &QEventLoop::quit);
0143     connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
0144     timer.start(1500);
0145     loop.exec();
0146 
0147     if(timer.isActive()){
0148         timer.stop();
0149         emit datasetFound();
0150     } else
0151         emit datasetNotFound();
0152 }
0153 
0154 /**
0155  * @brief Returns the dataset's full name.
0156  */
0157 QVariant WelcomeScreenHelper::datasetName() {
0158     return QVariant(m_spreadsheet->name());
0159 }
0160 
0161 /**
0162  * @brief Returns the dataset's descripton.
0163  */
0164 QVariant WelcomeScreenHelper::datasetDescription() {
0165     return QVariant(m_spreadsheet->comment());
0166 }
0167 
0168 /**
0169  * @brief Returns the number of the dataset's columns.
0170  */
0171 QVariant WelcomeScreenHelper::datasetColumns() {
0172     return QVariant(m_spreadsheet->columnCount());
0173 }
0174 
0175 /**
0176  * @brief Returns the number of the dataset's rows.
0177  */
0178 QVariant WelcomeScreenHelper::datasetRows() {
0179     return QVariant(m_spreadsheet->rowCount());
0180 }
0181 
0182 /**
0183  * @brief Returns a pointer to the spreadsheet in which the data of the dataset was loaded.
0184  */
0185 Spreadsheet* WelcomeScreenHelper::releaseConfiguredSpreadsheet() {
0186     return m_spreadsheet.release();
0187 }
0188 
0189 /**
0190  * @brief Returns the thumbnail image saved with the project.
0191  * @param url the path to the saved project file.
0192  */
0193 QVariant WelcomeScreenHelper::getProjectThumbnail(const QUrl& url) {
0194     QString filename;
0195     if (url.isLocalFile())  // fix for Windows
0196         filename = url.toLocalFile();
0197     else
0198         filename = url.path();
0199 
0200     QIODevice* file;
0201     // first try gzip compression, because projects can be gzipped and end with .lml
0202     if (filename.endsWith(QLatin1String(".lml"), Qt::CaseInsensitive))
0203         file = new KCompressionDevice(filename,KFilterDev::compressionTypeForMimeType("application/x-gzip"));
0204     else    // opens filename using file ending
0205         file = new KFilterDev(filename);
0206 
0207     if (!file)
0208         file = new QFile(filename);
0209 
0210     if (!file->open(QIODevice::ReadOnly)) {
0211         qDebug() << "Could not open file for reading.";
0212         return QVariant();
0213     }
0214 
0215     char c;
0216     bool rc = file->getChar(&c);
0217     if (!rc) {
0218         qDebug() << "The project file is empty.";
0219         file->close();
0220         delete file;
0221         return false;
0222     }
0223     file->seek(0);
0224 
0225     //parse XML
0226     XmlStreamReader reader(file);
0227     while (!(reader.isStartDocument() || reader.atEnd()))
0228         reader.readNext();
0229 
0230     if (!(reader.atEnd())) {
0231         if (!reader.skipToNextTag())
0232             return false;
0233 
0234         if (reader.name() == "project") {
0235             QString thumbnail = reader.attributes().value("thumbnail").toString();
0236 
0237             thumbnail.prepend("data:image/jpg;base64,");
0238             return QVariant(thumbnail);
0239         }
0240     }
0241     return QVariant();
0242 }
0243 
0244 /**
0245  * @brief Returns a pointer to the datasetModel of the class.
0246  */
0247 DatasetModel* WelcomeScreenHelper::getDatasetModel() {
0248     return m_datasetModel;
0249 }
0250 
0251 /**
0252  * @brief Processes metadata file containing example projects.
0253  */
0254 void WelcomeScreenHelper::processExampleProjects() {
0255 
0256     const QString filePath = QStandardPaths::locate(QStandardPaths::AppDataLocation, "example_projects/example_projects.json");
0257     QFile file(filePath);
0258 
0259     if (file.open(QIODevice::ReadOnly)) {
0260         QJsonDocument document = QJsonDocument::fromJson(file.readAll());
0261         QJsonArray exampleArray = document.array();
0262 
0263         //processing examples
0264         for(int i = 0 ; i < exampleArray.size(); ++i) {
0265             const QJsonObject currentExample = exampleArray[i].toObject();
0266 
0267             const QString exampleName = currentExample.value("name").toString();
0268             if(m_projectNameList.contains(exampleName)) {
0269                 qDebug() << "There is already an example file with this name";
0270             } else {
0271                 m_projectNameList.append(exampleName);
0272                 const QString exampleFile = currentExample.value("fileName").toString();
0273                 m_pathMap[exampleName] = exampleFile;
0274 
0275                 //processing tags
0276                 const QJsonArray tags = currentExample.value("tags").toArray();
0277                 for(int j = 0; j < tags.size(); ++j) {
0278                     QString tagName = tags[j].toString();
0279                     m_tagMap[tagName].append(exampleName);
0280                     m_datasetTag[exampleName].append(tagName);
0281                 }
0282             }
0283         }
0284 
0285         file.close();
0286     } else {
0287         qDebug("Couldn't open dataset category file");
0288     }
0289 }
0290 
0291 /**
0292  * @brief Returns in string format the thumbnail for the given example file
0293  */
0294 QVariant WelcomeScreenHelper::getExampleProjectThumbnail(const QString& exampleName) {
0295     const QString filePath = QStandardPaths::locate(QStandardPaths::AppDataLocation, "example_projects/" + m_pathMap[exampleName]);
0296     return getProjectThumbnail(filePath);
0297 }
0298 
0299 /**
0300  * @brief Returns the list of example projects.
0301  */
0302 QVariant WelcomeScreenHelper::getExampleProjects() {
0303     return QVariant(m_projectNameList);
0304 }
0305 
0306 /**
0307  * @brief Returns the tags of the given example project.
0308  */
0309 QVariant WelcomeScreenHelper::getExampleProjectTags(const QString& exampleName) {
0310     QString tags;
0311     const QStringList& tagList = m_datasetTag[exampleName];
0312 
0313     for(int i = 0; i < tagList.size(); i++) {
0314         tags.append(tagList[i]);
0315         if(i < tagList.size() - 1)
0316             tags.append(", ");
0317     }
0318 
0319     return QVariant(tags);
0320 }
0321 
0322 /**
0323  * @brief Handles an example project being clicked in the welcome screen.
0324  * @param exampleName the name of the clicked example project
0325  */
0326 void WelcomeScreenHelper::exampleProjectClicked(const QString& exampleName) {
0327     QString path = QStandardPaths::locate(QStandardPaths::AppDataLocation, "example_projects/" + m_pathMap[exampleName]);
0328     emit openExampleProject(path);
0329 }
0330 
0331 /**
0332  * @brief Searches among the example projects based on the text introduce in the search bar of the welcome screen.
0333  * @param searchtext - the text based on which we'll have to search
0334  * @return Returns the results
0335  */
0336 QVariant WelcomeScreenHelper::searchExampleProjects(const QString& searchtext) {
0337     QStringList results;
0338 
0339     //search based on tags
0340     for(auto tag = m_tagMap.begin(); tag != m_tagMap.end(); ++tag) {
0341         if (tag.key().contains(searchtext)) {
0342             for(QString example : tag.value()) {
0343                 if(!results.contains(example))
0344                     results.append(example);
0345             }
0346         }
0347     }
0348 
0349     //search based on name
0350     for(QString example : m_projectNameList) {
0351         if(example.contains(searchtext) && !results.contains(example))
0352             results.append(example);
0353     }
0354 
0355     return QVariant(results);
0356 }
0357 
0358 /**
0359  * @brief Sets the width scale for the given section, which will be saved.
0360  */
0361 void WelcomeScreenHelper::setWidthScale(const QString& sectionID, double scale) {
0362     m_widthScale[sectionID] = scale;
0363 }
0364 
0365 /**
0366  * @brief Sets the height scale for the given section, which will be saved.
0367  */
0368 void WelcomeScreenHelper::setHeightScale(const QString& sectionID, double scale) {
0369     m_heightScale[sectionID] = scale;
0370 }
0371 
0372 /**
0373  * @brief Returns the width scale for the given section.
0374  */
0375 QVariant WelcomeScreenHelper::getWidthScale(const QString& sectionID) {
0376     if(m_widthScale.contains(sectionID))
0377         return QVariant(m_widthScale[sectionID]);
0378 
0379     return QVariant(-1);
0380 }
0381 
0382 /**
0383  * @brief Returns the height scale for the given section.
0384  */
0385 QVariant WelcomeScreenHelper::getHeightScale(const QString& sectionID) {
0386     if(m_heightScale.contains(sectionID))
0387         return QVariant(m_heightScale[sectionID]);
0388 
0389     return QVariant(-1);;
0390 }
0391 
0392 /**
0393  * @brief Returns the maximize icon.
0394  */
0395 QVariant WelcomeScreenHelper::getMaxIcon() {
0396     QByteArray bArray;
0397     QBuffer buffer(&bArray);
0398     buffer.open(QIODevice::WriteOnly);
0399     m_maxIcon.save(&buffer, "PNG");
0400     QString image = QString::fromLatin1(bArray.toBase64().data());
0401     image.prepend("data:image/png;base64,");
0402     return QVariant(image);
0403 }
0404 
0405 /**
0406  * @brief Returns the minimize icon.
0407  */
0408 QVariant WelcomeScreenHelper::getMinIcon() {
0409     QByteArray bArray;
0410     QBuffer buffer(&bArray);
0411     buffer.open(QIODevice::WriteOnly);
0412     m_minIcon.save(&buffer, "PNG");
0413     QString image = QString::fromLatin1(bArray.toBase64().data());
0414     image.prepend("data:image/png;base64,");
0415     return QVariant(image);
0416 }
0417 
0418 /**
0419  * @brief Returns the go-back icon.
0420  */
0421 QVariant WelcomeScreenHelper::getBackIcon() {
0422     QIcon icon = QIcon::fromTheme("labplot-back");
0423     QPixmap pixmap = icon.pixmap(icon.availableSizes().constFirst());
0424     QByteArray bArray;
0425     QBuffer buffer(&bArray);
0426     buffer.open(QIODevice::WriteOnly);
0427     pixmap.save(&buffer, "PNG");
0428     QString image = QString::fromLatin1(bArray.toBase64().data());
0429     image.prepend("data:image/png;base64,");
0430     return QVariant(image);
0431 }
0432 
0433 /**
0434  * @brief Returns the go-forward icon.
0435  */
0436 QVariant WelcomeScreenHelper::getForwardIcon() {
0437     QIcon icon = QIcon::fromTheme("labplot-forward");
0438     QPixmap pixmap = icon.pixmap(icon.availableSizes().constFirst());
0439     QByteArray bArray;
0440     QBuffer buffer(&bArray);
0441     buffer.open(QIODevice::WriteOnly);
0442     pixmap.save(&buffer, "PNG");
0443     QString image = QString::fromLatin1(bArray.toBase64().data());
0444     image.prepend("data:image/png;base64,");
0445     return QVariant(image);
0446 }