diff --git a/src/backend/datasources/DatasetHandler.cpp b/src/backend/datasources/DatasetHandler.cpp index 4cff8ad0d..d0a248b98 100644 --- a/src/backend/datasources/DatasetHandler.cpp +++ b/src/backend/datasources/DatasetHandler.cpp @@ -1,308 +1,318 @@ /*************************************************************************** -File : DatasetHandler.cpp -Project : LabPlot -Description : Processes a dataset's metadata file --------------------------------------------------------------------- -Copyright : (C) 2019 Kovacs Ferencz (kferike98@gmail.com) + File : DatasetHandler.cpp + Project : LabPlot + Description : Processes a dataset's metadata file + -------------------------------------------------------------------- + Copyright : (C) 2019 Kovacs Ferencz (kferike98@gmail.com) + Copyright : (C) 2019 by Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "backend/datasources/filters/AsciiFilter.h" #include "backend/datasources/DatasetHandler.h" #include #include #include #include -#include +// #include #include -#include +// #include #include #include #include #include /*! \class DatasetHandler \brief Provides functionality to process a metadata file of a dataset, configure a spreadsheet and filter based on it, download the dataset and load it into the spreadsheet. \ingroup datasources */ DatasetHandler::DatasetHandler(Spreadsheet* spreadsheet) : m_spreadsheet(spreadsheet), m_filter(new AsciiFilter), - m_object(nullptr), m_downloadManager(new QNetworkAccessManager) { connect(m_downloadManager, &QNetworkAccessManager::finished, this, &DatasetHandler::downloadFinished); connect(this, &DatasetHandler::downloadCompleted, this, &DatasetHandler::processDataset); } DatasetHandler::~DatasetHandler() { delete m_downloadManager; delete m_filter; } /** * @brief Initiates processing the metadata file,, located at the given path, belonging to a dataset. * @param path the path to the metadata file */ void DatasetHandler::processMetadata(const QJsonObject& object) { m_object = new QJsonObject(object); - qDebug("Start processing dataset..."); + DEBUG("Start processing dataset..."); if(!m_object->isEmpty()) { configureFilter(); configureSpreadsheet(); prepareForDataset(); } } /** * @brief Marks the metadata file being invalid by setting the value of a flag, also pops up a messagebox. */ void DatasetHandler::markMetadataAsInvalid() { m_invalidMetadataFile = true; - QMessageBox::critical(0, "Invalid metadata file", "The metadata file for the choosen dataset is invalid!"); + QMessageBox::critical(0, i18n("Invalid metadata file"), i18n("The metadata file for the selected dataset is invalid.")); } /** * @brief Configures the filter, that will be used later, based on the metadata file. */ void DatasetHandler::configureFilter() { //set some default values common to many datasets m_filter->setNumberFormat(QLocale::C); m_filter->setSkipEmptyParts(true); m_filter->setHeaderEnabled(false); //read properties specified in the dataset description if(!m_object->isEmpty()) { if(m_object->contains("separator")) m_filter->setSeparatingCharacter(m_object->value("separator").toString()); if(m_object->contains("comment_character")) m_filter->setCommentCharacter(m_object->value("comment_character").toString()); if(m_object->contains("create_index_column")) m_filter->setCreateIndexEnabled(m_object->value("create_index_column").toBool()); if(m_object->contains("skip_empty_parts")) m_filter->setSkipEmptyParts(m_object->value("skip_empty_parts").toBool()); if(m_object->contains("simplify_whitespaces")) m_filter->setSimplifyWhitespacesEnabled(m_object->value("simplify_whitespaces").toBool()); if(m_object->contains("remove_quotes")) m_filter->setRemoveQuotesEnabled(m_object->value("remove_quotes").toBool()); if(m_object->contains("use_first_row_for_vectorname")) m_filter->setHeaderEnabled(m_object->value("use_first_row_for_vectorname").toBool()); if(m_object->contains("number_format")) m_filter->setNumberFormat(QLocale::Language(m_object->value("number_format").toInt())); if(m_object->contains("DateTime_format")) m_filter->setDateTimeFormat(m_object->value("DateTime_format").toString()); if(m_object->contains("columns")) { const QJsonArray& columnsArray = m_object->value("columns").toArray(); QStringList columnNames; for (int i = 0; i < columnsArray.size(); ++i) columnNames << columnsArray[i].toString(); m_filter->setVectorNames(columnNames); } } else { - qDebug() << "Empty object"; + DEBUG("Empty object"); markMetadataAsInvalid(); } } /** * @brief Configures the spreadsheet based on the metadata file. */ void DatasetHandler::configureSpreadsheet() { - qDebug("Conf spreadsheet"); + DEBUG("Start preparing spreadsheet"); if(!m_object->isEmpty()) { if(m_object->contains("name")) m_spreadsheet->setName( m_object->value("name").toString()); else markMetadataAsInvalid(); - if(m_object->contains("description")) + if (m_object->contains("description_url")) { + QNetworkAccessManager* manager = new QNetworkAccessManager(this); + connect(manager, &QNetworkAccessManager::finished, [this] (QNetworkReply* reply) { + if (reply->error() == QNetworkReply::NoError) { + QByteArray ba = reply->readAll(); + QString info(ba); + m_spreadsheet->setComment(info); + } else { + DEBUG("Failed to fetch the description."); + if(m_object->contains("description")) + m_spreadsheet->setComment(m_object->value("description").toString()); + } + reply->deleteLater(); + } + ); + manager->get(QNetworkRequest(QUrl(m_object->value("description_url").toString()))); + } else if(m_object->contains("description")) m_spreadsheet->setComment(m_object->value("description").toString()); } else { markMetadataAsInvalid(); } } /** * @brief Extracts the download URL of the dataset and initiates the process of download. */ void DatasetHandler::prepareForDataset() { - qDebug("Start downloading dataset"); + DEBUG("Start downloading dataset"); if(!m_object->isEmpty()) { if(m_object->contains("url")) { const QString& url = m_object->value("url").toString(); const QUrl downloadUrl = QUrl::fromEncoded(url.toLocal8Bit()); doDownload(url); } else { QMessageBox::critical(0, i18n("Invalid metadata file"), i18n("There is no download URL present in the metadata file!")); } } else { markMetadataAsInvalid(); } } /** * @brief Starts the download of the dataset. * @param url the download URL of the dataset */ void DatasetHandler::doDownload(const QUrl& url) { - qDebug("Download request"); + DEBUG("Download request"); QNetworkRequest request(url); m_currentDownload = m_downloadManager->get(request); connect(m_currentDownload, &QNetworkReply::downloadProgress, [this] (qint64 bytesReceived, qint64 bytesTotal) { double progress; if (bytesTotal == -1) progress = 0; else progress = 100 * (static_cast(bytesReceived) / static_cast(bytesTotal)); qDebug() << "Progress: " << progress; emit downloadProgress(progress); }); } /** * @brief Called when the download of the dataset is finished. */ void DatasetHandler::downloadFinished(QNetworkReply* reply) { - qDebug("Download finished"); + DEBUG("Download finished"); const QUrl& url = reply->url(); if (reply->error()) { qDebug("Download of %s failed: %s\n", url.toEncoded().constData(), qPrintable(reply->errorString())); } else { if (isHttpRedirect(reply)) { qDebug("Request was redirected.\n"); } else { QString filename = saveFileName(url); if (saveToDisk(filename, reply)) { qDebug("Download of %s succeeded (saved to %s)\n", url.toEncoded().constData(), qPrintable(filename)); m_fileName = filename; emit downloadCompleted(); } } } m_currentDownload = nullptr; reply->deleteLater(); } /** * @brief Checks whether the GET request was redirected or not. */ bool DatasetHandler::isHttpRedirect(QNetworkReply* reply) { const int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); // TODO enum/defines for status codes ? return statusCode == 301 || statusCode == 302 || statusCode == 303 || statusCode == 305 || statusCode == 307 || statusCode == 308; } /** * @brief Returns the name and path of the file that will contain the content of the reply (based on the URL). * @param url */ QString DatasetHandler::saveFileName(const QUrl& url) { const QString path = url.path(); //get the extension of the downloaded file const QString downloadFileName = QFileInfo(path).fileName(); int lastIndex = downloadFileName.lastIndexOf("."); const QString fileExtension = lastIndex >= 0 ? downloadFileName.right(downloadFileName.length() - lastIndex) : ""; QString basename = m_object->value("filename").toString() + fileExtension; if (basename.isEmpty()) basename = "url"; QDir downloadDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QLatin1String("/datasets_local/")); if (!downloadDir.exists()) downloadDir.mkdir(downloadDir.path()); QString fileName = downloadDir.path() + QLatin1Char('/') + basename; QFileInfo fileInfo (fileName); if (QFile::exists(fileName)) { if(fileInfo.lastModified().addDays(1) < QDateTime::currentDateTime()){ QFile removeFile (fileName); removeFile.remove(); } else { qDebug() << "Dataset file already exists, no need to download it again"; } } return fileName; } /** * @brief Saves the content of the network reply to the given path under the given name. */ bool DatasetHandler::saveToDisk(const QString& filename, QIODevice* data) { QFile file(filename); if (!file.open(QIODevice::WriteOnly)) { qDebug("Could not open %s for writing: %s\n", qPrintable(filename), qPrintable(file.errorString())); return false; } file.write(data->readAll()); file.close(); return true; } /** * @brief Processes the downloaded dataset with the help of the already configured filter. */ void DatasetHandler::processDataset() { m_filter->readDataFromFile(m_fileName, m_spreadsheet); - configureColumns(); -} -/** - * @brief Configures the columns of the spreadsheet, based on the metadata file. - */ -void DatasetHandler::configureColumns() { - if(!m_object->isEmpty()) { - int index = 0; - const int columnsCount = m_spreadsheet->columnCount(); - while(m_object->contains(i18n("column_description_%1", index)) && (index < columnsCount)) { - m_spreadsheet->column(index)->setComment(m_object->value(i18n("column_description_%1", index)).toString()); - ++index; - } - } else { - qDebug("Invalid Json document"); - } + //set column comments/descriptions, if available + //TODO: +// if(!m_object->isEmpty()) { +// int index = 0; +// const int columnsCount = m_spreadsheet->columnCount(); +// while(m_object->contains(i18n("column_description_%1", index)) && (index < columnsCount)) { +// m_spreadsheet->column(index)->setComment(m_object->value(i18n("column_description_%1", index)).toString()); +// ++index; +// } +// } } diff --git a/src/backend/datasources/DatasetHandler.h b/src/backend/datasources/DatasetHandler.h index 5b8bcc910..af7bd8b49 100644 --- a/src/backend/datasources/DatasetHandler.h +++ b/src/backend/datasources/DatasetHandler.h @@ -1,77 +1,76 @@ /*************************************************************************** File : DatasetHandler.h Project : LabPlot Description : Processes a dataset's metadata file -------------------------------------------------------------------- Copyright : (C) 2019 Kovacs Ferencz (kferike98@gmail.com) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #ifndef DATASETHANDLER_H #define DATASETHANDLER_H #include #include "backend/spreadsheet/Spreadsheet.h" class QJsonObject; class AsciiFilter; class QNetworkAccessManager; class QNetworkReply; class DatasetHandler: public QObject{ Q_OBJECT public: DatasetHandler(Spreadsheet*); ~DatasetHandler(); void processMetadata(const QJsonObject&); private: Spreadsheet* m_spreadsheet; AsciiFilter* m_filter; - QJsonObject* m_object; + QJsonObject* m_object{nullptr}; QNetworkAccessManager* m_downloadManager; QNetworkReply* m_currentDownload; QString m_fileName; bool m_invalidMetadataFile{false}; QString m_containingDir; void loadJsonDocument(const QString& path); void configureFilter(); void configureSpreadsheet(); void prepareForDataset(); void processDataset(); void doDownload(const QUrl&); bool isHttpRedirect(QNetworkReply*); QString saveFileName(const QUrl&); bool saveToDisk(const QString& filename, QIODevice*); void markMetadataAsInvalid(); - void configureColumns(); private slots: void downloadFinished(QNetworkReply*); signals: void downloadCompleted(); void downloadProgress(int progress); }; #endif // DATASETHANDLER_H diff --git a/src/kdefrontend/MainWin.cpp b/src/kdefrontend/MainWin.cpp index ad6e1eb89..81669e231 100644 --- a/src/kdefrontend/MainWin.cpp +++ b/src/kdefrontend/MainWin.cpp @@ -1,2241 +1,2241 @@ /*************************************************************************** File : MainWin.cc Project : LabPlot Description : Main window of the application -------------------------------------------------------------------- Copyright : (C) 2008-2018 Stefan Gerlach (stefan.gerlach@uni.kn) Copyright : (C) 2009-2018 Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "MainWin.h" #include "backend/core/Project.h" #include "backend/core/Folder.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/Workbook.h" #include "backend/spreadsheet/Spreadsheet.h" #include "backend/matrix/Matrix.h" #include "backend/worksheet/Worksheet.h" #include "backend/datasources/LiveDataSource.h" #include "backend/datasources/DatasetHandler.h" #ifdef HAVE_LIBORIGIN #include "backend/datasources/projects/OriginProjectParser.h" #endif #ifdef HAVE_CANTOR_LIBS #include "backend/cantorWorksheet/CantorWorksheet.h" #endif #include "backend/datapicker/Datapicker.h" #include "backend/note/Note.h" #include "backend/lib/macros.h" #include "backend/worksheet/plots/cartesian/CartesianPlot.h" #ifdef HAVE_MQTT #include "backend/datasources/MQTTClient.h" #endif #include "commonfrontend/core/PartMdiView.h" #include "commonfrontend/ProjectExplorer.h" #include "commonfrontend/matrix/MatrixView.h" #include "commonfrontend/spreadsheet/SpreadsheetView.h" #include "commonfrontend/worksheet/WorksheetView.h" #ifdef HAVE_CANTOR_LIBS #include "commonfrontend/cantorWorksheet/CantorWorksheetView.h" #endif #include "commonfrontend/datapicker/DatapickerView.h" #include "commonfrontend/datapicker/DatapickerImageView.h" #include "commonfrontend/note/NoteView.h" #include "commonfrontend/widgets/MemoryWidget.h" #include "kdefrontend/datasources/ImportFileDialog.h" #include "kdefrontend/datasources/ImportDatasetDialog.h" #include "kdefrontend/datasources/ImportDatasetWidget.h" #include "kdefrontend/datasources/ImportProjectDialog.h" #include "kdefrontend/datasources/ImportSQLDatabaseDialog.h" #include #include "kdefrontend/dockwidgets/ProjectDock.h" #include "kdefrontend/HistoryDialog.h" #include "kdefrontend/SettingsDialog.h" #include "kdefrontend/GuiObserver.h" #include "kdefrontend/widgets/FITSHeaderEditDialog.h" #include "DatasetModel.h" #include "WelcomeScreenHelper.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_CANTOR_LIBS #include #include #include #include #endif /*! \class MainWin \brief Main application window. \ingroup kdefrontend */ MainWin::MainWin(QWidget *parent, const QString& filename) : KXmlGuiWindow(parent) { initGUI(filename); setAcceptDrops(true); //restore the geometry KConfigGroup group = KSharedConfig::openConfig()->group("MainWin"); restoreGeometry(group.readEntry("geometry", QByteArray())); } MainWin::~MainWin() { //save the recent opened files m_recentProjectsAction->saveEntries( KSharedConfig::openConfig()->group("Recent Files") ); KConfigGroup group = KSharedConfig::openConfig()->group("MainWin"); group.writeEntry("geometry", saveGeometry()); KSharedConfig::openConfig()->sync(); qDebug() << "Mainwin Destructor "; if(dynamic_cast(centralWidget()) != nullptr) { qDebug() << "Destructor save welcome screen"; QMetaObject::invokeMethod(m_welcomeWidget->rootObject(), "saveWidgetDimensions"); } if (m_project != nullptr) { if(dynamic_cast(centralWidget()) == nullptr) m_mdiArea->closeAllSubWindows(); disconnect(m_project, nullptr, this, nullptr); delete m_project; } if (m_aspectTreeModel) delete m_aspectTreeModel; if (m_guiObserver) delete m_guiObserver; if(m_welcomeScreenHelper) delete m_welcomeScreenHelper; } void MainWin::showPresenter() { const Worksheet* w = dynamic_cast(m_currentAspect); if (w) { auto* view = dynamic_cast(w->view()); view->presenterMode(); } else { //currently active object is not a worksheet but we're asked to start in the presenter mode //determine the first available worksheet and show it in the presenter mode QVector worksheets = m_project->children(); if (worksheets.size() > 0) { auto* view = qobject_cast(worksheets.first()->view()); view->presenterMode(); } else { QMessageBox::information(this, i18n("Presenter Mode"), i18n("No worksheets are available in the project. The presenter mode will not be started.")); } } } AspectTreeModel* MainWin::model() const { return m_aspectTreeModel; } Project* MainWin::project() const { return m_project; } void MainWin::initGUI(const QString& fileName) { m_mdiArea = new QMdiArea; setCentralWidget(m_mdiArea); connect(m_mdiArea, &QMdiArea::subWindowActivated, this, &MainWin::handleCurrentSubWindowChanged); statusBar()->showMessage(i18nc("%1 is the LabPlot version", "Welcome to LabPlot %1", QLatin1String(LVERSION))); initActions(); #ifdef Q_OS_DARWIN setupGUI(Default, QLatin1String("/Applications/labplot2.app/Contents/Resources/labplot2ui.rc")); #else setupGUI(Default, KXMLGUIClient::xmlFile()); // should be "labplot2ui.rc" #endif DEBUG("component name: " << KXMLGUIClient::componentName().toStdString()); DEBUG("XML file: " << KXMLGUIClient::xmlFile().toStdString() << " (should be \"labplot2ui.rc\")"); //all toolbars created via the KXMLGUI framework are locked on default: // * on the very first program start, unlock all toolbars // * on later program starts, set stored lock status //Furthermore, we want to show icons only after the first program start. KConfigGroup groupMain = KSharedConfig::openConfig()->group("MainWindow"); if (groupMain.exists()) { //KXMLGUI framework automatically stores "Disabled" for the key "ToolBarsMovable" //in case the toolbars are locked -> load this value const QString& str = groupMain.readEntry(QLatin1String("ToolBarsMovable"), ""); bool locked = (str == QLatin1String("Disabled")); KToolBar::setToolBarsLocked(locked); } else { //first start KToolBar::setToolBarsLocked(false); //show icons only for (auto* container : factory()->containers(QLatin1String("ToolBar"))) { auto* toolbar = dynamic_cast(container); if (toolbar) toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly); } } initMenus(); auto* mainToolBar = qobject_cast(factory()->container("main_toolbar", this)); if (!mainToolBar) { QMessageBox::critical(this, i18n("GUI configuration file not found"), i18n("%1 file was not found. Please check your installation.", KXMLGUIClient::xmlFile())); //TODO: the application is not really usable if the rc file was not found. We should quit the application. The following line crashes //the application because of the splash screen. We need to find another solution. // QMetaObject::invokeMethod(this, "close", Qt::QueuedConnection); //call close as soon as we enter the eventloop return; } auto* tbImport = new QToolButton(mainToolBar); tbImport->setPopupMode(QToolButton::MenuButtonPopup); tbImport->setMenu(m_importMenu); tbImport->setDefaultAction(m_importFileAction); mainToolBar->addWidget(tbImport); qobject_cast(factory()->container("import", this))->setIcon(QIcon::fromTheme("document-import")); setWindowIcon(QIcon::fromTheme("LabPlot2", QGuiApplication::windowIcon())); setAttribute( Qt::WA_DeleteOnClose ); //make the status bar of a fixed size in order to avoid height changes when placing a ProgressBar there. QFont font; font.setFamily(font.defaultFamily()); QFontMetrics fm(font); statusBar()->setFixedHeight(fm.height() + 5); //load recently used projects m_recentProjectsAction->loadEntries( KSharedConfig::openConfig()->group("Recent Files") ); //set the view mode of the mdi area KConfigGroup group = KSharedConfig::openConfig()->group( "Settings_General" ); int viewMode = group.readEntry("ViewMode", 0); if (viewMode == 1) { m_mdiArea->setViewMode(QMdiArea::TabbedView); int tabPosition = group.readEntry("TabPosition", 0); m_mdiArea->setTabPosition(QTabWidget::TabPosition(tabPosition)); m_mdiArea->setTabsClosable(true); m_mdiArea->setTabsMovable(true); m_tileWindows->setVisible(false); m_cascadeWindows->setVisible(false); } //auto-save m_autoSaveActive = group.readEntry("AutoSave", false); int interval = group.readEntry("AutoSaveInterval", 1); interval = interval*60*1000; m_autoSaveTimer.setInterval(interval); connect(&m_autoSaveTimer, &QTimer::timeout, this, &MainWin::autoSaveProject); if (!fileName.isEmpty()) { #ifdef HAVE_LIBORIGIN if (Project::isLabPlotProject(fileName) || OriginProjectParser::isOriginProject(fileName)) { #else if (Project::isLabPlotProject(fileName)) { #endif QTimer::singleShot(0, this, [=] () { openProject(fileName); }); } else { newProject(); QTimer::singleShot(0, this, [=] () { importFileDialog(fileName); }); } } else { //There is no file to open. Depending on the settings do nothing, //create a new project or open the last used project. int load = group.readEntry("LoadOnStart", 0); if (load == 1) //create new project newProject(); else if (load == 2) { //create new project with a worksheet newProject(); newWorksheet(); } else if (load == 3) { //open last used project if (!m_recentProjectsAction->urls().isEmpty()) { QDEBUG("TO OPEN m_recentProjectsAction->urls() =" << m_recentProjectsAction->urls().constFirst()); openRecentProject( m_recentProjectsAction->urls().constFirst() ); } } else if (load == 4) { //welcome screen m_showWelcomeScreen = true; m_welcomeWidget = createWelcomeScreen(); setCentralWidget(m_welcomeWidget); } } //show memory info const bool showMemoryInfo = group.readEntry(QLatin1String("ShowMemoryInfo"), true); if (showMemoryInfo) { m_memoryInfoWidget = new MemoryWidget(statusBar()); statusBar()->addPermanentWidget(m_memoryInfoWidget); } updateGUIOnProjectChanges(); } /** * @brief Creates a new welcome screen to be set as central widget. */ QQuickWidget* MainWin::createWelcomeScreen() { QSize maxSize = qApp->primaryScreen()->availableSize(); resize(maxSize); setMinimumSize(700, 400); showMaximized(); KToolBar* toolbar = toolBar(); if(toolbar != nullptr) { toolbar->setVisible(false); } else { qDebug() << "There is no toolbar to hide"; } QList recentList; for (QUrl url : m_recentProjectsAction->urls()) recentList.append(QVariant(url)); //Set the source qml QQuickWidget* quickWidget = new QQuickWidget(this); QUrl source("qrc:///main.qml"); //Set ocntext property QQmlContext *ctxt = quickWidget->rootContext(); QVariant variant(recentList); ctxt->setContextProperty("recentProjects", variant); //Create helper object if(m_welcomeScreenHelper != nullptr) delete m_welcomeScreenHelper; m_welcomeScreenHelper = new WelcomeScreenHelper(); connect(m_welcomeScreenHelper, SIGNAL(openExampleProject(QString)), this, SLOT(openProject(const QString& ))); ctxt->setContextProperty("datasetModel", m_welcomeScreenHelper->getDatasetModel()); ctxt->setContextProperty("helper", m_welcomeScreenHelper); quickWidget->setSource(source); quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); QObject *item = quickWidget->rootObject(); //connect qml's signals QObject::connect(item, SIGNAL(recentProjectClicked(QUrl)), this, SLOT(openRecentProject(QUrl))); QObject::connect(item, SIGNAL(datasetClicked(QString, QString, QString)), m_welcomeScreenHelper, SLOT(datasetClicked(const QString&, const QString&, const QString&))); QObject::connect(item, SIGNAL(openDataset()), this, SLOT(openDatasetExample())); QObject::connect(item, SIGNAL(openExampleProject(QString)), m_welcomeScreenHelper, SLOT(exampleProjectClicked(const QString&))); m_welcomeScreenHelper->showFirstDataset(); return quickWidget; } /** * @brief Initiates resetting the layout of the welcome screen */ void MainWin::resetWelcomeScreen() { if(dynamic_cast(centralWidget()) != nullptr) { QMetaObject::invokeMethod(m_welcomeWidget->rootObject(), "restoreOriginalLayout"); } } /** * @brief Creates a new MDI area, to replace the Welcome Screen as central widget */ void MainWin::createMdiArea() { setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); setMinimumSize(0,0); KToolBar* toolbar = toolBar(); if(toolbar != nullptr) { toolbar->setVisible(true); } else { qDebug() << "There is no toolbar to display"; } //Save welcome screen's dimensions. if(m_showWelcomeScreen) { qDebug() << "Call saving welcome screen widget dimensions"; QMetaObject::invokeMethod(m_welcomeWidget->rootObject(), "saveWidgetDimensions"); } m_mdiArea = new QMdiArea; setCentralWidget(m_mdiArea); connect(m_mdiArea, &QMdiArea::subWindowActivated, this, &MainWin::handleCurrentSubWindowChanged); KConfigGroup group = KSharedConfig::openConfig()->group( "Settings_General" ); int viewMode = group.readEntry("ViewMode", 0); if (viewMode == 1) { m_mdiArea->setViewMode(QMdiArea::TabbedView); int tabPosition = group.readEntry("TabPosition", 0); m_mdiArea->setTabPosition(QTabWidget::TabPosition(tabPosition)); m_mdiArea->setTabsClosable(true); m_mdiArea->setTabsMovable(true); m_tileWindows->setVisible(false); m_cascadeWindows->setVisible(false); } QAction* action = new QAction(i18n("&Close"), this); actionCollection()->setDefaultShortcut(action, QKeySequence::Close); action->setStatusTip(i18n("Close the active window")); actionCollection()->addAction("close window", action); connect(action, &QAction::triggered, m_mdiArea, &QMdiArea::closeActiveSubWindow); action = new QAction(i18n("Close &All"), this); action->setStatusTip(i18n("Close all the windows")); actionCollection()->addAction("close all windows", action); connect(action, &QAction::triggered, m_mdiArea, &QMdiArea::closeAllSubWindows); m_tileWindows = new QAction(i18n("&Tile"), this); m_tileWindows->setStatusTip(i18n("Tile the windows")); actionCollection()->addAction("tile windows", m_tileWindows); connect(m_tileWindows, &QAction::triggered, m_mdiArea, &QMdiArea::tileSubWindows); m_cascadeWindows = new QAction(i18n("&Cascade"), this); m_cascadeWindows->setStatusTip(i18n("Cascade the windows")); actionCollection()->addAction("cascade windows", m_cascadeWindows); connect(m_cascadeWindows, &QAction::triggered, m_mdiArea, &QMdiArea::cascadeSubWindows); action = new QAction(QIcon::fromTheme("go-next-view"), i18n("Ne&xt"), this); actionCollection()->setDefaultShortcut(action, QKeySequence::NextChild); action->setStatusTip(i18n("Move the focus to the next window")); actionCollection()->addAction("next window", action); connect(action, &QAction::triggered, m_mdiArea, &QMdiArea::activateNextSubWindow); action = new QAction(QIcon::fromTheme("go-previous-view"), i18n("Pre&vious"), this); actionCollection()->setDefaultShortcut(action, QKeySequence::PreviousChild); action->setStatusTip(i18n("Move the focus to the previous window")); actionCollection()->addAction("previous window", action); connect(action, &QAction::triggered, m_mdiArea, &QMdiArea::activatePreviousSubWindow); } void MainWin::initActions() { // ******************** File-menu ******************************* //add some standard actions KStandardAction::openNew(this, SLOT(newProject()),actionCollection()); KStandardAction::open(this, SLOT(openProject()),actionCollection()); m_recentProjectsAction = KStandardAction::openRecent(this, SLOT(openRecentProject(QUrl)),actionCollection()); m_closeAction = KStandardAction::close(this, SLOT(closeProject()),actionCollection()); actionCollection()->setDefaultShortcut(m_closeAction, QKeySequence()); //remove the shortcut, QKeySequence::Close will be used for closing sub-windows m_saveAction = KStandardAction::save(this, SLOT(saveProject()),actionCollection()); m_saveAsAction = KStandardAction::saveAs(this, SLOT(saveProjectAs()),actionCollection()); m_printAction = KStandardAction::print(this, SLOT(print()),actionCollection()); m_printPreviewAction = KStandardAction::printPreview(this, SLOT(printPreview()),actionCollection()); //TODO: on Mac OS when going full-screen we get a crash because of an stack-overflow #ifndef Q_OS_MAC KStandardAction::fullScreen(this, SLOT(toggleFullScreen()), this, actionCollection()); #endif //New Folder/Workbook/Spreadsheet/Matrix/Worksheet/Datasources m_newWorkbookAction = new QAction(QIcon::fromTheme("labplot-workbook-new"),i18n("Workbook"),this); actionCollection()->addAction("new_workbook", m_newWorkbookAction); m_newWorkbookAction->setWhatsThis(i18n("Creates a new workbook for collection spreadsheets, matrices and plots")); connect(m_newWorkbookAction, &QAction::triggered, this, &MainWin::newWorkbook); m_newDatapickerAction = new QAction(QIcon::fromTheme("color-picker-black"), i18n("Datapicker"), this); m_newDatapickerAction->setWhatsThis(i18n("Creates a data picker for getting data from a picture")); actionCollection()->addAction("new_datapicker", m_newDatapickerAction); connect(m_newDatapickerAction, &QAction::triggered, this, &MainWin::newDatapicker); m_newSpreadsheetAction = new QAction(QIcon::fromTheme("labplot-spreadsheet-new"),i18n("Spreadsheet"),this); // m_newSpreadsheetAction->setShortcut(Qt::CTRL+Qt::Key_Equal); m_newSpreadsheetAction->setWhatsThis(i18n("Creates a new spreadsheet for data editing")); actionCollection()->addAction("new_spreadsheet", m_newSpreadsheetAction); connect(m_newSpreadsheetAction, &QAction::triggered, this, &MainWin::newSpreadsheet); m_newMatrixAction = new QAction(QIcon::fromTheme("labplot-matrix-new"),i18n("Matrix"),this); // m_newMatrixAction->setShortcut(Qt::CTRL+Qt::Key_Equal); m_newMatrixAction->setWhatsThis(i18n("Creates a new matrix for data editing")); actionCollection()->addAction("new_matrix", m_newMatrixAction); connect(m_newMatrixAction, &QAction::triggered, this, &MainWin::newMatrix); m_newWorksheetAction = new QAction(QIcon::fromTheme("labplot-worksheet-new"),i18n("Worksheet"),this); // m_newWorksheetAction->setShortcut(Qt::ALT+Qt::Key_X); m_newWorksheetAction->setWhatsThis(i18n("Creates a new worksheet for data plotting")); actionCollection()->addAction("new_worksheet", m_newWorksheetAction); connect(m_newWorksheetAction, &QAction::triggered, this, &MainWin::newWorksheet); m_newNotesAction = new QAction(QIcon::fromTheme("document-new"),i18n("Note"),this); m_newNotesAction->setWhatsThis(i18n("Creates a new note for arbitrary text")); actionCollection()->addAction("new_notes", m_newNotesAction); connect(m_newNotesAction, &QAction::triggered, this, &MainWin::newNotes); // m_newScriptAction = new QAction(QIcon::fromTheme("insert-text"),i18n("Note/Script"),this); // actionCollection()->addAction("new_script", m_newScriptAction); // connect(m_newScriptAction, &QAction::triggered,SLOT(newScript())); m_newFolderAction = new QAction(QIcon::fromTheme("folder-new"),i18n("Folder"),this); m_newFolderAction->setWhatsThis(i18n("Creates a new folder to collect sheets and other elements")); actionCollection()->addAction("new_folder", m_newFolderAction); connect(m_newFolderAction, &QAction::triggered, this, &MainWin::newFolder); //"New file datasources" m_newLiveDataSourceAction = new QAction(QIcon::fromTheme("application-octet-stream"),i18n("Live Data Source"),this); m_newLiveDataSourceAction->setWhatsThis(i18n("Creates a live data source to read data from a real time device")); actionCollection()->addAction("new_live_datasource", m_newLiveDataSourceAction); connect(m_newLiveDataSourceAction, &QAction::triggered, this, &MainWin::newLiveDataSourceActionTriggered); //Import/Export m_importFileAction = new QAction(QIcon::fromTheme("document-import"), i18n("From File"), this); actionCollection()->setDefaultShortcut(m_importFileAction, Qt::CTRL+Qt::SHIFT+Qt::Key_I); m_importFileAction->setWhatsThis(i18n("Import data from a regular file")); actionCollection()->addAction("import_file", m_importFileAction); connect(m_importFileAction, &QAction::triggered, this, [=]() {importFileDialog();}); m_importSqlAction = new QAction(QIcon::fromTheme("network-server-database"), i18n("From SQL Database"), this); m_importSqlAction->setWhatsThis(i18n("Import data from a SQL database")); actionCollection()->addAction("import_sql", m_importSqlAction); connect(m_importSqlAction, &QAction::triggered, this, &MainWin::importSqlDialog); m_importDatasetAction = new QAction(QIcon::fromTheme(QLatin1String("database-index")), i18n("From Dataset Collection"), this); m_importDatasetAction->setWhatsThis(i18n("Imports data from an online dataset")); actionCollection()->addAction("import_dataset_datasource", m_importDatasetAction); - connect(m_importDatasetAction, &QAction::triggered, this, &MainWin::newDatasetActionTriggered); + connect(m_importDatasetAction, &QAction::triggered, this, &MainWin::importDatasetDialog); m_importLabPlotAction = new QAction(QIcon::fromTheme("project-open"), i18n("LabPlot Project"), this); m_importLabPlotAction->setWhatsThis(i18n("Import a project from a LabPlot project file (.lml)")); actionCollection()->addAction("import_labplot", m_importLabPlotAction); connect(m_importLabPlotAction, &QAction::triggered, this, &MainWin::importProjectDialog); #ifdef HAVE_LIBORIGIN m_importOpjAction = new QAction(QIcon::fromTheme("project-open"), i18n("Origin Project (OPJ)"), this); m_importOpjAction->setWhatsThis(i18n("Import a project from an OriginLab Origin project file (.opj)")); actionCollection()->addAction("import_opj", m_importOpjAction); connect(m_importOpjAction, &QAction::triggered, this, &MainWin::importProjectDialog); #endif m_exportAction = new QAction(QIcon::fromTheme("document-export"), i18n("Export"), this); m_exportAction->setWhatsThis(i18n("Export selected element")); actionCollection()->setDefaultShortcut(m_exportAction, Qt::CTRL+Qt::SHIFT+Qt::Key_E); actionCollection()->addAction("export", m_exportAction); connect(m_exportAction, &QAction::triggered, this, &MainWin::exportDialog); m_editFitsFileAction = new QAction(QIcon::fromTheme("editor"), i18n("FITS Metadata Editor"), this); m_editFitsFileAction->setWhatsThis(i18n("Open editor to edit FITS meta data")); actionCollection()->addAction("edit_fits", m_editFitsFileAction); connect(m_editFitsFileAction, &QAction::triggered, this, &MainWin::editFitsFileDialog); // Edit //Undo/Redo-stuff m_undoAction = KStandardAction::undo(this, SLOT(undo()), actionCollection()); m_redoAction = KStandardAction::redo(this, SLOT(redo()), actionCollection()); m_historyAction = new QAction(QIcon::fromTheme("view-history"), i18n("Undo/Redo History"),this); actionCollection()->addAction("history", m_historyAction); connect(m_historyAction, &QAction::triggered, this, &MainWin::historyDialog); // TODO: more menus // Appearance // Analysis: see WorksheetView.cpp // Drawing // Script //Windows QAction* action = new QAction(i18n("&Close"), this); actionCollection()->setDefaultShortcut(action, QKeySequence::Close); action->setStatusTip(i18n("Close the active window")); actionCollection()->addAction("close window", action); connect(action, &QAction::triggered, m_mdiArea, &QMdiArea::closeActiveSubWindow); action = new QAction(i18n("Close &All"), this); action->setStatusTip(i18n("Close all the windows")); actionCollection()->addAction("close all windows", action); connect(action, &QAction::triggered, m_mdiArea, &QMdiArea::closeAllSubWindows); m_tileWindows = new QAction(i18n("&Tile"), this); m_tileWindows->setStatusTip(i18n("Tile the windows")); actionCollection()->addAction("tile windows", m_tileWindows); connect(m_tileWindows, &QAction::triggered, m_mdiArea, &QMdiArea::tileSubWindows); m_cascadeWindows = new QAction(i18n("&Cascade"), this); m_cascadeWindows->setStatusTip(i18n("Cascade the windows")); actionCollection()->addAction("cascade windows", m_cascadeWindows); connect(m_cascadeWindows, &QAction::triggered, m_mdiArea, &QMdiArea::cascadeSubWindows); action = new QAction(QIcon::fromTheme("go-next-view"), i18n("Ne&xt"), this); actionCollection()->setDefaultShortcut(action, QKeySequence::NextChild); action->setStatusTip(i18n("Move the focus to the next window")); actionCollection()->addAction("next window", action); connect(action, &QAction::triggered, m_mdiArea, &QMdiArea::activateNextSubWindow); action = new QAction(QIcon::fromTheme("go-previous-view"), i18n("Pre&vious"), this); actionCollection()->setDefaultShortcut(action, QKeySequence::PreviousChild); action->setStatusTip(i18n("Move the focus to the previous window")); actionCollection()->addAction("previous window", action); connect(action, &QAction::triggered, m_mdiArea, &QMdiArea::activatePreviousSubWindow); //"Standard actions" KStandardAction::preferences(this, SLOT(settingsDialog()), actionCollection()); KStandardAction::quit(this, SLOT(close()), actionCollection()); //Actions for window visibility auto* windowVisibilityActions = new QActionGroup(this); windowVisibilityActions->setExclusive(true); m_visibilityFolderAction = new QAction(QIcon::fromTheme("folder"), i18n("Current &Folder Only"), windowVisibilityActions); m_visibilityFolderAction->setCheckable(true); m_visibilityFolderAction->setData(Project::folderOnly); m_visibilitySubfolderAction = new QAction(QIcon::fromTheme("folder-documents"), i18n("Current Folder and &Subfolders"), windowVisibilityActions); m_visibilitySubfolderAction->setCheckable(true); m_visibilitySubfolderAction->setData(Project::folderAndSubfolders); m_visibilityAllAction = new QAction(i18n("&All"), windowVisibilityActions); m_visibilityAllAction->setCheckable(true); m_visibilityAllAction->setData(Project::allMdiWindows); connect(windowVisibilityActions, &QActionGroup::triggered, this, &MainWin::setMdiWindowVisibility); //Actions for hiding/showing the dock widgets auto* docksActions = new QActionGroup(this); docksActions->setExclusive(false); m_toggleProjectExplorerDockAction = new QAction(QIcon::fromTheme("view-list-tree"), i18n("Project Explorer"), docksActions); m_toggleProjectExplorerDockAction->setCheckable(true); m_toggleProjectExplorerDockAction->setChecked(true); actionCollection()->addAction("toggle_project_explorer_dock", m_toggleProjectExplorerDockAction); m_togglePropertiesDockAction = new QAction(QIcon::fromTheme("view-list-details"), i18n("Properties Explorer"), docksActions); m_togglePropertiesDockAction->setCheckable(true); m_togglePropertiesDockAction->setChecked(true); actionCollection()->addAction("toggle_properties_explorer_dock", m_togglePropertiesDockAction); connect(docksActions, &QActionGroup::triggered, this, &MainWin::toggleDockWidget); } void MainWin::initMenus() { //menu in the main toolbar for adding new aspects auto* menu = dynamic_cast(factory()->container("new", this)); menu->setIcon(QIcon::fromTheme("window-new")); //menu in the project explorer and in the toolbar for adding new aspects m_newMenu = new QMenu(i18n("Add New"), this); m_newMenu->setIcon(QIcon::fromTheme("window-new")); m_newMenu->addAction(m_newFolderAction); m_newMenu->addAction(m_newWorkbookAction); m_newMenu->addAction(m_newSpreadsheetAction); m_newMenu->addAction(m_newMatrixAction); m_newMenu->addAction(m_newWorksheetAction); m_newMenu->addAction(m_newNotesAction); m_newMenu->addAction(m_newDatapickerAction); m_newMenu->addSeparator(); m_newMenu->addAction(m_newLiveDataSourceAction); //import menu m_importMenu = new QMenu(this); m_importMenu->setIcon(QIcon::fromTheme("document-import")); m_importMenu ->addAction(m_importFileAction); m_importMenu ->addAction(m_importSqlAction); m_importMenu->addAction(m_importDatasetAction); m_importMenu->addSeparator(); m_importMenu->addAction(m_importLabPlotAction); #ifdef HAVE_LIBORIGIN m_importMenu ->addAction(m_importOpjAction); #endif #ifdef HAVE_CANTOR_LIBS m_newMenu->addSeparator(); m_newCantorWorksheetMenu = new QMenu(i18n("CAS Worksheet"), this); m_newCantorWorksheetMenu->setIcon(QIcon::fromTheme("archive-insert")); //"Adding Cantor backends to menu and context menu" QStringList m_availableBackend = Cantor::Backend::listAvailableBackends(); if (m_availableBackend.count() > 0) { unplugActionList(QLatin1String("backends_list")); QList newBackendActions; for (Cantor::Backend* backend : Cantor::Backend::availableBackends()) { if (!backend->isEnabled()) continue; QAction* action = new QAction(QIcon::fromTheme(backend->icon()), backend->name(),this); action->setData(backend->name()); newBackendActions << action; m_newCantorWorksheetMenu->addAction(action); } connect(m_newCantorWorksheetMenu, &QMenu::triggered, this, &MainWin::newCantorWorksheet); plugActionList(QLatin1String("backends_list"), newBackendActions); } m_newMenu->addMenu(m_newCantorWorksheetMenu); #else delete this->guiFactory()->container("cas_worksheet", this); delete this->guiFactory()->container("new_cas_worksheet", this); delete this->guiFactory()->container("cas_worksheet_toolbar", this); #endif //menu subwindow visibility policy m_visibilityMenu = new QMenu(i18n("Window Visibility Policy"), this); m_visibilityMenu->setIcon(QIcon::fromTheme("window-duplicate")); m_visibilityMenu ->addAction(m_visibilityFolderAction); m_visibilityMenu ->addAction(m_visibilitySubfolderAction); m_visibilityMenu ->addAction(m_visibilityAllAction); //menu for editing files m_editMenu = new QMenu(i18n("Edit"), this); m_editMenu->addAction(m_editFitsFileAction); KColorSchemeManager schemeManager; KActionMenu* schemesMenu = schemeManager.createSchemeSelectionMenu(i18n("Color Theme"), this); schemesMenu->menu()->setTitle(i18n("Color Theme")); schemesMenu->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-color"))); QMenu* settingsMenu = dynamic_cast(factory()->container("settings", this)); if (settingsMenu) settingsMenu->insertMenu(settingsMenu->actions().constFirst(), schemesMenu->menu()); //set the action for the current color scheme checked KConfigGroup generalGlobalsGroup = KSharedConfig::openConfig(QLatin1String("kdeglobals"))->group("General"); QString defaultSchemeName = generalGlobalsGroup.readEntry("ColorScheme", QStringLiteral("Breeze")); KConfigGroup group = KSharedConfig::openConfig()->group(QLatin1String("Settings_General")); QString schemeName = group.readEntry("ColorScheme", defaultSchemeName); for (auto* action : schemesMenu->menu()->actions()) { if (action->text() == schemeName) { action->setChecked(true); break; } } connect(schemesMenu->menu(), &QMenu::triggered, this, &MainWin::colorSchemeChanged); #ifdef HAVE_CANTOR_LIBS QAction* action = new QAction(QIcon::fromTheme(QLatin1String("cantor")), i18n("Configure CAS"), this); connect(action, &QAction::triggered, this, &MainWin::cantorSettingsDialog); if (settingsMenu) settingsMenu->addAction(action); #endif } void MainWin::colorSchemeChanged(QAction* action) { QString schemeName = KLocalizedString::removeAcceleratorMarker(action->text()); //background of the mdi area is not updated on theme changes, do it here. KColorSchemeManager schemeManager; QModelIndex index = schemeManager.indexForScheme(schemeName); const QPalette& palette = KColorScheme::createApplicationPalette( KSharedConfig::openConfig(index.data(Qt::UserRole).toString()) ); const QBrush& brush = palette.brush(QPalette::Dark); m_mdiArea->setBackground(brush); //save the selected color scheme KConfigGroup group = KSharedConfig::openConfig()->group(QLatin1String("Settings_General")); group.writeEntry("ColorScheme", schemeName); group.sync(); } /*! Asks to save the project if it was modified. \return \c true if the project still needs to be saved ("cancel" clicked), \c false otherwise. */ bool MainWin::warnModified() { if (m_project->hasChanged()) { int want_save = KMessageBox::warningYesNoCancel( this, i18n("The current project %1 has been modified. Do you want to save it?", m_project->name()), i18n("Save Project")); switch (want_save) { case KMessageBox::Yes: return !saveProject(); case KMessageBox::No: break; case KMessageBox::Cancel: return true; } } return false; } /*! * updates the state of actions, menus and toolbars (enabled or disabled) * on project changes (project closes and opens) */ void MainWin::updateGUIOnProjectChanges() { if (m_closing) return; KXMLGUIFactory* factory = this->guiFactory(); if (factory->container("worksheet", this) == nullptr) { //no worksheet menu found, most probably labplot2ui.rc //was not properly installed -> return here in order not to crash return; } //disable all menus if there is no project bool b = (m_project == nullptr); m_saveAction->setEnabled(!b); m_saveAsAction->setEnabled(!b); m_printAction->setEnabled(!b); m_printPreviewAction->setEnabled(!b); m_importFileAction->setEnabled(!b); m_importSqlAction->setEnabled(!b); #ifdef HAVE_LIBORIGIN m_importOpjAction->setEnabled(!b); #endif m_exportAction->setEnabled(!b); m_newWorkbookAction->setEnabled(!b); m_newSpreadsheetAction->setEnabled(!b); m_newMatrixAction->setEnabled(!b); m_newWorksheetAction->setEnabled(!b); m_newDatapickerAction->setEnabled(!b); m_closeAction->setEnabled(!b); m_toggleProjectExplorerDockAction->setEnabled(!b); m_togglePropertiesDockAction->setEnabled(!b); if (!m_mdiArea->currentSubWindow()) { factory->container("spreadsheet", this)->setEnabled(false); factory->container("matrix", this)->setEnabled(false); factory->container("worksheet", this)->setEnabled(false); factory->container("analysis", this)->setEnabled(false); factory->container("datapicker", this)->setEnabled(false); factory->container("spreadsheet_toolbar", this)->hide(); factory->container("worksheet_toolbar", this)->hide(); factory->container("cartesian_plot_toolbar", this)->hide(); // factory->container("histogram_toolbar",this)->hide(); // factory->container("barchart_toolbar",this)->hide(); factory->container("datapicker_toolbar", this)->hide(); #ifdef HAVE_CANTOR_LIBS factory->container("cas_worksheet", this)->setEnabled(false); factory->container("cas_worksheet_toolbar", this)->hide(); #endif } factory->container("new", this)->setEnabled(!b); factory->container("edit", this)->setEnabled(!b); factory->container("import", this)->setEnabled(!b); if (b) setCaption("LabPlot2"); else setCaption(m_project->name()); // undo/redo actions are disabled in both cases - when the project is closed or opened m_undoAction->setEnabled(false); m_redoAction->setEnabled(false); } /* * updates the state of actions, menus and toolbars (enabled or disabled) * depending on the currently active window (worksheet or spreadsheet). */ void MainWin::updateGUI() { if (m_project == nullptr || m_project->isLoading()) return; if (m_closing || m_projectClosing) return; KXMLGUIFactory* factory = this->guiFactory(); if (factory->container("worksheet", this) == nullptr) { //no worksheet menu found, most probably labplot2ui.rc //was not properly installed -> return here in order not to crash return; } if (!m_mdiArea->currentSubWindow()) { factory->container("spreadsheet", this)->setEnabled(false); factory->container("matrix", this)->setEnabled(false); factory->container("worksheet", this)->setEnabled(false); factory->container("analysis", this)->setEnabled(false); factory->container("datapicker", this)->setEnabled(false); factory->container("spreadsheet_toolbar", this)->hide(); factory->container("worksheet_toolbar", this)->hide(); // factory->container("histogram_toolbar",this)->hide(); // factory->container("barchart_toolbar",this)->hide(); factory->container("cartesian_plot_toolbar", this)->hide(); factory->container("datapicker_toolbar", this)->hide(); #ifdef HAVE_CANTOR_LIBS factory->container("cas_worksheet", this)->setEnabled(false); factory->container("cas_worksheet_toolbar", this)->hide(); #endif return; } //Handle the Worksheet-object const Worksheet* w = dynamic_cast(m_currentAspect); if (!w) w = dynamic_cast(m_currentAspect->parent(AspectType::Worksheet)); if (w) { //populate worksheet menu auto* view = qobject_cast(w->view()); auto* menu = qobject_cast(factory->container("worksheet", this)); menu->clear(); view->createContextMenu(menu); menu->setEnabled(true); //populate analysis menu menu = qobject_cast(factory->container("analysis", this)); menu->clear(); view->createAnalysisMenu(menu); menu->setEnabled(true); //populate worksheet-toolbar auto* toolbar = qobject_cast(factory->container("worksheet_toolbar", this)); toolbar->clear(); view->fillToolBar(toolbar); toolbar->setVisible(true); toolbar->setEnabled(true); //populate the toolbar for cartesian plots toolbar = qobject_cast(factory->container("cartesian_plot_toolbar", this)); toolbar->clear(); view->fillCartesianPlotToolBar(toolbar); toolbar->setVisible(true); toolbar->setEnabled(true); //hide the spreadsheet toolbar factory->container("spreadsheet_toolbar", this)->setVisible(false); } else { factory->container("worksheet", this)->setEnabled(false); factory->container("worksheet_toolbar", this)->setVisible(false); factory->container("analysis", this)->setEnabled(false); // factory->container("drawing", this)->setEnabled(false); factory->container("worksheet_toolbar", this)->setEnabled(false); factory->container("cartesian_plot_toolbar", this)->setEnabled(false); } //Handle the Spreadsheet-object const auto* spreadsheet = this->activeSpreadsheet(); if (!spreadsheet) spreadsheet = dynamic_cast(m_currentAspect->parent(AspectType::Spreadsheet)); if (spreadsheet) { //populate spreadsheet-menu auto* view = qobject_cast(spreadsheet->view()); auto* menu = qobject_cast(factory->container("spreadsheet", this)); menu->clear(); view->createContextMenu(menu); menu->setEnabled(true); //populate spreadsheet-toolbar auto* toolbar = qobject_cast(factory->container("spreadsheet_toolbar", this)); toolbar->clear(); view->fillToolBar(toolbar); toolbar->setVisible(true); toolbar->setEnabled(true); } else { factory->container("spreadsheet", this)->setEnabled(false); factory->container("spreadsheet_toolbar", this)->setVisible(false); } //Handle the Matrix-object const Matrix* matrix = dynamic_cast(m_currentAspect); if (!matrix) matrix = dynamic_cast(m_currentAspect->parent(AspectType::Matrix)); if (matrix) { //populate matrix-menu auto* view = qobject_cast(matrix->view()); auto* menu = qobject_cast(factory->container("matrix", this)); menu->clear(); view->createContextMenu(menu); menu->setEnabled(true); } else factory->container("matrix", this)->setEnabled(false); #ifdef HAVE_CANTOR_LIBS const CantorWorksheet* cantorworksheet = dynamic_cast(m_currentAspect); if (!cantorworksheet) cantorworksheet = dynamic_cast(m_currentAspect->parent(AspectType::CantorWorksheet)); if (cantorworksheet) { auto* view = qobject_cast(cantorworksheet->view()); auto* menu = qobject_cast(factory->container("cas_worksheet", this)); menu->clear(); view->createContextMenu(menu); menu->setEnabled(true); auto* toolbar = qobject_cast(factory->container("cas_worksheet_toolbar", this)); toolbar->setVisible(true); toolbar->clear(); view->fillToolBar(toolbar); } else { //no Cantor worksheet selected -> deactivate Cantor worksheet related menu and toolbar factory->container("cas_worksheet", this)->setEnabled(false); factory->container("cas_worksheet_toolbar", this)->setVisible(false); } #endif const Datapicker* datapicker = dynamic_cast(m_currentAspect); if (!datapicker) datapicker = dynamic_cast(m_currentAspect->parent(AspectType::Datapicker)); if (!datapicker) { if (m_currentAspect->type() == AspectType::DatapickerCurve) datapicker = dynamic_cast(m_currentAspect->parentAspect()); } if (datapicker) { //populate datapicker-menu auto* view = qobject_cast(datapicker->view()); auto* menu = qobject_cast(factory->container("datapicker", this)); menu->clear(); view->createContextMenu(menu); menu->setEnabled(true); //populate spreadsheet-toolbar auto* toolbar = qobject_cast(factory->container("datapicker_toolbar", this)); toolbar->clear(); view->fillToolBar(toolbar); toolbar->setVisible(true); } else { factory->container("datapicker", this)->setEnabled(false); factory->container("datapicker_toolbar", this)->setVisible(false); } } /*! creates a new empty project. Returns \c true, if a new project was created. */ bool MainWin::newProject() { //close the current project, if available if (!closeProject()) return false; if(dynamic_cast(centralWidget()) != nullptr) { createMdiArea(); setCentralWidget(m_mdiArea); } QApplication::processEvents(QEventLoop::AllEvents, 100); if (m_project) delete m_project; if (m_aspectTreeModel) delete m_aspectTreeModel; m_project = new Project(); m_currentAspect = m_project; m_currentFolder = m_project; KConfigGroup group = KSharedConfig::openConfig()->group( "Settings_General" ); Project::MdiWindowVisibility vis = Project::MdiWindowVisibility(group.readEntry("MdiWindowVisibility", 0)); m_project->setMdiWindowVisibility( vis ); if (vis == Project::folderOnly) m_visibilityFolderAction->setChecked(true); else if (vis == Project::folderAndSubfolders) m_visibilitySubfolderAction->setChecked(true); else m_visibilityAllAction->setChecked(true); m_aspectTreeModel = new AspectTreeModel(m_project, this); connect(m_aspectTreeModel, &AspectTreeModel::statusInfo, [=](const QString& text){ statusBar()->showMessage(text); }); //newProject is called for the first time, there is no project explorer yet //-> initialize the project explorer, the GUI-observer and the dock widgets. if (m_projectExplorer == nullptr) { m_projectExplorerDock = new QDockWidget(this); m_projectExplorerDock->setObjectName("projectexplorer"); m_projectExplorerDock->setWindowTitle(i18nc("@title:window", "Project Explorer")); addDockWidget(Qt::LeftDockWidgetArea, m_projectExplorerDock); m_projectExplorer = new ProjectExplorer(m_projectExplorerDock); m_projectExplorerDock->setWidget(m_projectExplorer); connect(m_projectExplorer, &ProjectExplorer::currentAspectChanged, this, &MainWin::handleCurrentAspectChanged); connect(m_projectExplorerDock, &QDockWidget::visibilityChanged, this, &MainWin::projectExplorerDockVisibilityChanged); //Properties dock m_propertiesDock = new QDockWidget(this); m_propertiesDock->setObjectName("aspect_properties_dock"); m_propertiesDock->setWindowTitle(i18nc("@title:window", "Properties")); addDockWidget(Qt::RightDockWidgetArea, m_propertiesDock); auto* sa = new QScrollArea(m_propertiesDock); stackedWidget = new QStackedWidget(sa); sa->setWidget(stackedWidget); sa->setWidgetResizable(true); m_propertiesDock->setWidget(sa); connect(m_propertiesDock, &QDockWidget::visibilityChanged, this, &MainWin::propertiesDockVisibilityChanged); //GUI-observer; m_guiObserver = new GuiObserver(this); } m_projectExplorer->setModel(m_aspectTreeModel); m_projectExplorer->setProject(m_project); m_projectExplorer->setCurrentAspect(m_project); m_projectExplorerDock->show(); m_propertiesDock->show(); updateGUIOnProjectChanges(); connect(m_project, &Project::aspectAdded, this, &MainWin::handleAspectAdded); connect(m_project, &Project::aspectRemoved, this, &MainWin::handleAspectRemoved); connect(m_project, &Project::aspectAboutToBeRemoved, this, &MainWin::handleAspectAboutToBeRemoved); connect(m_project, SIGNAL(statusInfo(QString)), statusBar(), SLOT(showMessage(QString))); connect(m_project, &Project::changed, this, &MainWin::projectChanged); connect(m_project, &Project::requestProjectContextMenu, this, &MainWin::createContextMenu); connect(m_project, &Project::requestFolderContextMenu, this, &MainWin::createFolderContextMenu); connect(m_project, &Project::mdiWindowVisibilityChanged, this, &MainWin::updateMdiWindowVisibility); connect(m_project, &Project::closeRequested, this, &MainWin::closeProject); m_undoViewEmptyLabel = i18n("%1: created", m_project->name()); setCaption(m_project->name()); return true; } void MainWin::openProject() { KConfigGroup conf(KSharedConfig::openConfig(), "MainWin"); const QString& dir = conf.readEntry("LastOpenDir", ""); const QString& path = QFileDialog::getOpenFileName(this,i18n("Open Project"), dir, #ifdef HAVE_LIBORIGIN i18n("LabPlot Projects (%1);;Origin Projects (%2)", Project::supportedExtensions(), OriginProjectParser::supportedExtensions()) ); #else i18n("LabPlot Projects (%1)", Project::supportedExtensions()) ); #endif if (path.isEmpty())// "Cancel" was clicked return; this->openProject(path); //save new "last open directory" int pos = path.lastIndexOf(QDir::separator()); if (pos != -1) { const QString& newDir = path.left(pos); if (newDir != dir) conf.writeEntry("LastOpenDir", newDir); } } void MainWin::openProject(const QString& filename) { if (filename == m_currentFileName) { KMessageBox::information(this, i18n("The project file %1 is already opened.", filename), i18n("Open Project")); return; } if(dynamic_cast(centralWidget()) != nullptr) { createMdiArea(); setCentralWidget(m_mdiArea); } if (!newProject()) return; WAIT_CURSOR; QElapsedTimer timer; timer.start(); bool rc = false; if (Project::isLabPlotProject(filename)) { m_project->setFileName(filename); rc = m_project->load(filename); #ifdef HAVE_LIBORIGIN } else if (OriginProjectParser::isOriginProject(filename)) { OriginProjectParser parser; parser.setProjectFileName(filename); parser.importTo(m_project, QStringList()); //TODO: add return code rc = true; } #endif if (!rc) { closeProject(); RESET_CURSOR; return; } m_currentFileName = filename; m_project->undoStack()->clear(); m_undoViewEmptyLabel = i18n("%1: opened", m_project->name()); m_recentProjectsAction->addUrl( QUrl(filename) ); setCaption(m_project->name()); updateGUIOnProjectChanges(); updateGUI(); //there are most probably worksheets or spreadsheets in the open project -> update the GUI m_saveAction->setEnabled(false); statusBar()->showMessage( i18n("Project successfully opened (in %1 seconds).", (float)timer.elapsed()/1000) ); if (m_autoSaveActive) m_autoSaveTimer.start(); RESET_CURSOR; } void MainWin::openRecentProject(const QUrl& url) { if(dynamic_cast(centralWidget()) != nullptr) { createMdiArea(); setCentralWidget(m_mdiArea); } if (url.isLocalFile()) // fix for Windows this->openProject(url.toLocalFile()); else this->openProject(url.path()); } /*! Closes the current project, if available. Return \c true, if the project was closed. */ bool MainWin::closeProject() { if (m_project == nullptr) return true; //nothing to close if (warnModified()) return false; if(!m_closing) { if(dynamic_cast(centralWidget()) == nullptr && m_showWelcomeScreen) { m_welcomeWidget = createWelcomeScreen(); setCentralWidget(m_welcomeWidget); } } m_projectClosing = true; statusBar()->clearMessage(); delete m_aspectTreeModel; m_aspectTreeModel = nullptr; delete m_project; m_project = nullptr; m_currentFileName.clear(); m_projectClosing = false; //update the UI if we're just closing a project //and not closing(quitting) the application if (!m_closing) { m_projectExplorerDock->hide(); m_propertiesDock->hide(); m_currentAspect = nullptr; m_currentFolder = nullptr; updateGUIOnProjectChanges(); if (m_autoSaveActive) m_autoSaveTimer.stop(); } removeDockWidget(cursorDock); delete cursorDock; cursorDock = nullptr; cursorWidget = nullptr; // is deleted, because it's the cild of cursorDock return true; } bool MainWin::saveProject() { const QString& fileName = m_project->fileName(); if (fileName.isEmpty()) return saveProjectAs(); else return save(fileName); } bool MainWin::saveProjectAs() { KConfigGroup conf(KSharedConfig::openConfig(), "MainWin"); const QString& dir = conf.readEntry("LastOpenDir", ""); QString path = QFileDialog::getSaveFileName(this, i18n("Save Project As"), dir, i18n("LabPlot Projects (*.lml *.lml.gz *.lml.bz2 *.lml.xz *.LML *.LML.GZ *.LML.BZ2 *.LML.XZ)")); if (path.isEmpty())// "Cancel" was clicked return false; if (path.contains(QLatin1String(".lml"), Qt::CaseInsensitive) == false) path.append(QLatin1String(".lml")); //save new "last open directory" int pos = path.lastIndexOf(QDir::separator()); if (pos != -1) { const QString& newDir = path.left(pos); if (newDir != dir) conf.writeEntry("LastOpenDir", newDir); } return save(path); } /*! * auxiliary function that does the actual saving of the project */ bool MainWin::save(const QString& fileName) { QTemporaryFile tempFile(QDir::tempPath() + "/" + QLatin1String("labplot_save_XXXXXX")); if (!tempFile.open()) { KMessageBox::error(this, i18n("Couldn't open the temporary file for writing.")); return false; } WAIT_CURSOR; const QString& tempFileName = tempFile.fileName(); DEBUG("Using temporary file " << tempFileName.toStdString()) tempFile.close(); // use file ending to find out how to compress file QIODevice* file; // if ending is .lml, do gzip compression anyway if (fileName.endsWith(QLatin1String(".lml"))) file = new KCompressionDevice(tempFileName, KCompressionDevice::GZip); else file = new KFilterDev(tempFileName); if (file == nullptr) file = new QFile(tempFileName); bool ok; if (file->open(QIODevice::WriteOnly)) { m_project->setFileName(fileName); QPixmap thumbnail = centralWidget()->grab(); QXmlStreamWriter writer(file); m_project->setFileName(fileName); m_project->save(thumbnail, &writer); m_project->undoStack()->clear(); m_project->setChanged(false); file->close(); // target file must not exist if (QFile::exists(fileName)) QFile::remove(fileName); // do not rename temp file. Qt still holds a handle (which fails renaming on Windows) and deletes it bool rc = QFile::copy(tempFileName, fileName); if (rc) { setCaption(m_project->name()); statusBar()->showMessage(i18n("Project saved")); m_saveAction->setEnabled(false); m_recentProjectsAction->addUrl( QUrl(fileName) ); ok = true; //if the project dock is visible, refresh the shown content //(version and modification time might have been changed) if (stackedWidget->currentWidget() == projectDock) projectDock->setProject(m_project); //we have a file name now // -> auto save can be activated now if not happened yet if (m_autoSaveActive && !m_autoSaveTimer.isActive()) m_autoSaveTimer.start(); } else { RESET_CURSOR; KMessageBox::error(this, i18n("Couldn't save the file '%1'.", fileName)); ok = false; } } else { RESET_CURSOR; KMessageBox::error(this, i18n("Couldn't open the file '%1' for writing.", fileName)); ok = false; } delete file; RESET_CURSOR; return ok; } /*! * automatically saves the project in the specified time interval. */ void MainWin::autoSaveProject() { //don't auto save when there are no changes or the file name //was not provided yet (the project was never explicitly saved yet). if ( !m_project->hasChanged() || m_project->fileName().isEmpty()) return; this->saveProject(); } /*! prints the current sheet (worksheet, spreadsheet or matrix) */ void MainWin::print() { QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return; AbstractPart* part = dynamic_cast(win)->part(); statusBar()->showMessage(i18n("Preparing printing of %1", part->name())); if (part->printView()) statusBar()->showMessage(i18n("%1 printed", part->name())); else statusBar()->clearMessage(); } void MainWin::printPreview() { QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return; AbstractPart* part = dynamic_cast(win)->part(); statusBar()->showMessage(i18n("Preparing printing of %1", part->name())); if (part->printPreview()) statusBar()->showMessage(i18n("%1 printed", part->name())); else statusBar()->clearMessage(); } /**************************************************************************************/ /*! adds a new Folder to the project. */ void MainWin::newFolder() { Folder* folder = new Folder(i18n("Folder")); this->addAspectToProject(folder); } /*! adds a new Workbook to the project. */ void MainWin::newWorkbook() { Workbook* workbook = new Workbook(i18n("Workbook")); this->addAspectToProject(workbook); } /*! adds a new Datapicker to the project. */ void MainWin::newDatapicker() { Datapicker* datapicker = new Datapicker(i18n("Datapicker")); this->addAspectToProject(datapicker); } /*! adds a new Spreadsheet to the project. */ void MainWin::newSpreadsheet() { Spreadsheet* spreadsheet = new Spreadsheet(i18n("Spreadsheet")); //if the current active window is a workbook and no folder/project is selected in the project explorer, //add the new spreadsheet to the workbook Workbook* workbook = dynamic_cast(m_currentAspect); if (workbook) { QModelIndex index = m_projectExplorer->currentIndex(); const auto* aspect = static_cast(index.internalPointer()); if (!aspect->inherits(AspectType::Folder)) { workbook->addChild(spreadsheet); return; } } this->addAspectToProject(spreadsheet); } /*! adds a new Matrix to the project. */ void MainWin::newMatrix() { Matrix* matrix = new Matrix(i18n("Matrix")); //if the current active window is a workbook and no folder/project is selected in the project explorer, //add the new matrix to the workbook Workbook* workbook = dynamic_cast(m_currentAspect); if (workbook) { QModelIndex index = m_projectExplorer->currentIndex(); const auto* aspect = static_cast(index.internalPointer()); if (!aspect->inherits(AspectType::Folder)) { workbook->addChild(matrix); return; } } this->addAspectToProject(matrix); } /*! adds a new Worksheet to the project. */ void MainWin::newWorksheet() { Worksheet* worksheet = new Worksheet(i18n("Worksheet")); this->addAspectToProject(worksheet); } /*! adds a new Note to the project. */ void MainWin::newNotes() { Note* notes = new Note(i18n("Note")); this->addAspectToProject(notes); } /*! returns a pointer to a \c Spreadsheet object, if the currently active Mdi-Subwindow or if the currently selected tab in a \c WorkbookView is a \c SpreadsheetView Otherwise returns \c 0. */ Spreadsheet* MainWin::activeSpreadsheet() const { if(dynamic_cast(centralWidget()) != nullptr) { return nullptr; } if (!m_currentAspect) return nullptr; Spreadsheet* spreadsheet = nullptr; if (m_currentAspect->type() == AspectType::Spreadsheet) spreadsheet = dynamic_cast(m_currentAspect); else { //check whether one of spreadsheet columns is selected and determine the spreadsheet auto* parent = m_currentAspect->parentAspect(); if (parent && parent->type() == AspectType::Spreadsheet) spreadsheet = dynamic_cast(parent); } return spreadsheet; } #ifdef HAVE_CANTOR_LIBS /* adds a new Cantor Spreadsheet to the project. */ void MainWin::newCantorWorksheet(QAction* action) { CantorWorksheet* cantorworksheet = new CantorWorksheet(action->data().toString()); this->addAspectToProject(cantorworksheet); } /********************************************************************************/ #endif /*! called if there were changes in the project. Adds "changed" to the window caption and activates the save-Action. */ void MainWin::projectChanged() { setCaption(i18n("%1 [Changed]", m_project->name())); m_saveAction->setEnabled(true); m_undoAction->setEnabled(true); return; } void MainWin::handleCurrentSubWindowChanged(QMdiSubWindow* win) { if (!win) return; auto* view = qobject_cast(win); if (!view) { updateGUI(); return; } if (view == m_currentSubWindow) { //do nothing, if the current sub-window gets selected again. //This event happens, when labplot loses the focus (modal window is opened or the user switches to another application) //and gets it back (modal window is closed or the user switches back to labplot). return; } else m_currentSubWindow = view; updateGUI(); if (!m_suppressCurrentSubWindowChangedEvent) m_projectExplorer->setCurrentAspect(view->part()); } void MainWin::handleAspectAdded(const AbstractAspect* aspect) { const auto* part = dynamic_cast(aspect); if (part) { // connect(part, &AbstractPart::importFromFileRequested, this, &MainWin::importFileDialog); connect(part, &AbstractPart::importFromFileRequested, this, [=]() {importFileDialog();}); connect(part, &AbstractPart::importFromSQLDatabaseRequested, this, &MainWin::importSqlDialog); //TODO: export, print and print preview should be handled in the views and not in MainWin. connect(part, &AbstractPart::exportRequested, this, &MainWin::exportDialog); connect(part, &AbstractPart::printRequested, this, &MainWin::print); connect(part, &AbstractPart::printPreviewRequested, this, &MainWin::printPreview); connect(part, &AbstractPart::showRequested, this, &MainWin::handleShowSubWindowRequested); const auto* worksheet = dynamic_cast(aspect); if (worksheet) connect(worksheet, &Worksheet::cartesianPlotMouseModeChanged, this, &MainWin::cartesianPlotMouseModeChanged); } } // void MainWin::cartesianPlotMouseModeChanged(CartesianPlot::MouseMode void MainWin::handleAspectRemoved(const AbstractAspect* parent,const AbstractAspect* before,const AbstractAspect* aspect) { Q_UNUSED(before); Q_UNUSED(aspect); //no need to react on AbstractSimpleFilter if (!dynamic_cast(aspect)) m_projectExplorer->setCurrentAspect(parent); } void MainWin::handleAspectAboutToBeRemoved(const AbstractAspect *aspect) { const auto* part = qobject_cast(aspect); if (!part) return; const auto* workbook = dynamic_cast(aspect->parentAspect()); auto* datapicker = dynamic_cast(aspect->parentAspect()); if (!datapicker) datapicker = dynamic_cast(aspect->parentAspect()->parentAspect()); if (!workbook && !datapicker) { PartMdiView* win = part->mdiSubWindow(); if (win) m_mdiArea->removeSubWindow(win); } } /*! called when the current aspect in the tree of the project explorer was changed. Selects the new aspect. */ void MainWin::handleCurrentAspectChanged(AbstractAspect *aspect) { if (!aspect) aspect = m_project; // should never happen, just in case m_suppressCurrentSubWindowChangedEvent = true; if (aspect->folder() != m_currentFolder) { m_currentFolder = aspect->folder(); updateMdiWindowVisibility(); } m_currentAspect = aspect; //activate the corresponding MDI sub window for the current aspect activateSubWindowForAspect(aspect); m_suppressCurrentSubWindowChangedEvent = false; updateGUI(); } void MainWin::activateSubWindowForAspect(const AbstractAspect* aspect) const { const auto* part = dynamic_cast(aspect); if (part) { //for LiveDataSource we currently don't show any view /*if (dynamic_cast(part)) return;*/ PartMdiView* win; //for aspects being children of a Workbook, we show workbook's window, otherwise the window of the selected part const auto* workbook = dynamic_cast(aspect->parentAspect()); auto* datapicker = dynamic_cast(aspect->parentAspect()); if (!datapicker) datapicker = dynamic_cast(aspect->parentAspect()->parentAspect()); if (workbook) win = workbook->mdiSubWindow(); else if (datapicker) win = datapicker->mdiSubWindow(); else win = part->mdiSubWindow(); if (m_mdiArea->subWindowList().indexOf(win) == -1) { if (dynamic_cast(part)) m_mdiArea->addSubWindow(win, Qt::Tool); else m_mdiArea->addSubWindow(win); win->show(); //Qt provides its own "system menu" for every sub-window. The shortcut for the close-action //in this menu collides with our global m_closeAction. //remove the shortcuts in the system menu to avoid this collision. QMenu* menu = win->systemMenu(); if (menu) { for (QAction* action : menu->actions()) action->setShortcut(QKeySequence()); } } m_mdiArea->setActiveSubWindow(win); } else { //activate the mdiView of the parent, if a child was selected const AbstractAspect* parent = aspect->parentAspect(); if (parent) { activateSubWindowForAspect(parent); //if the parent's parent is a Workbook (a column of a spreadsheet in workbook was selected), //we need to select the corresponding tab in WorkbookView too if (parent->parentAspect()) { auto* workbook = dynamic_cast(parent->parentAspect()); auto* datapicker = dynamic_cast(parent->parentAspect()); if (!datapicker) datapicker = dynamic_cast(parent->parentAspect()->parentAspect()); if (workbook) workbook->childSelected(parent); else if (datapicker) datapicker->childSelected(parent); } } } return; } void MainWin::setMdiWindowVisibility(QAction* action) { m_project->setMdiWindowVisibility((Project::MdiWindowVisibility)(action->data().toInt())); } /*! shows the sub window of a worksheet, matrix or a spreadsheet. Used if the window was closed before and the user asks to show the window again via the context menu in the project explorer. */ void MainWin::handleShowSubWindowRequested() { activateSubWindowForAspect(m_currentAspect); } /*! this is called on a right click on the root folder in the project explorer */ void MainWin::createContextMenu(QMenu* menu) const { QAction* firstAction = nullptr; // if we're populating the context menu for the project explorer, then //there're already actions available there. Skip the first title-action //and insert the action at the beginning of the menu. if (menu->actions().size()>1) firstAction = menu->actions().at(1); menu->insertMenu(firstAction, m_newMenu); //The tabbed view collides with the visibility policy for the subwindows. //Hide the menus for the visibility policy if the tabbed view is used. if (m_mdiArea->viewMode() != QMdiArea::TabbedView) { menu->insertSeparator(firstAction); menu->insertMenu(firstAction, m_visibilityMenu); menu->insertSeparator(firstAction); } } /*! this is called on a right click on a non-root folder in the project explorer */ void MainWin::createFolderContextMenu(const Folder* folder, QMenu* menu) const { Q_UNUSED(folder); //Folder provides it's own context menu. Add a separator before adding additional actions. menu->addSeparator(); this->createContextMenu(menu); } void MainWin::undo() { WAIT_CURSOR; m_project->undoStack()->undo(); if (m_project->undoStack()->index() == 0) { setCaption(m_project->name()); m_saveAction->setEnabled(false); m_undoAction->setEnabled(false); m_project->setChanged(false); } m_redoAction->setEnabled(true); RESET_CURSOR; } void MainWin::redo() { WAIT_CURSOR; m_project->undoStack()->redo(); projectChanged(); if (m_project->undoStack()->index() == m_project->undoStack()->count()) m_redoAction->setEnabled(false); RESET_CURSOR; } /*! Shows/hides mdi sub-windows depending on the current visibility policy. */ void MainWin::updateMdiWindowVisibility() const { QList windows = m_mdiArea->subWindowList(); PartMdiView* part_view; switch (m_project->mdiWindowVisibility()) { case Project::allMdiWindows: for (auto* window : windows) window->show(); break; case Project::folderOnly: for (auto* window : windows) { part_view = qobject_cast(window); Q_ASSERT(part_view); if (part_view->part()->folder() == m_currentFolder) part_view->show(); else part_view->hide(); } break; case Project::folderAndSubfolders: for (auto* window : windows) { part_view = qobject_cast(window); if (part_view->part()->isDescendantOf(m_currentFolder)) part_view->show(); else part_view->hide(); } break; } } void MainWin::toggleDockWidget(QAction* action) { if (action->objectName() == "toggle_project_explorer_dock") { if (m_projectExplorerDock->isVisible()) m_projectExplorerDock->hide(); // toggleHideWidget(m_projectExplorerDock, true); else m_projectExplorerDock->show(); // toggleShowWidget(m_projectExplorerDock, true); } else if (action->objectName() == "toggle_properties_explorer_dock") { if (m_propertiesDock->isVisible()) m_propertiesDock->hide(); // toggleHideWidget(m_propertiesDock, false); else m_propertiesDock->show(); // toggleShowWidget(m_propertiesDock, false); } } /* void MainWin::toggleHideWidget(QWidget* widget, bool hideToLeft) { auto* timeline = new QTimeLine(800, this); timeline->setEasingCurve(QEasingCurve::InOutQuad); connect(timeline, &QTimeLine::valueChanged, [=] { const qreal value = timeline->currentValue(); const int widgetWidth = widget->width(); const int widgetPosY = widget->pos().y(); int moveX = 0; if (hideToLeft) { moveX = static_cast(value * widgetWidth) - widgetWidth; } else { const int frameRight = this->frameGeometry().right(); moveX = frameRight - static_cast(value * widgetWidth); } widget->move(moveX, widgetPosY); }); timeline->setDirection(QTimeLine::Backward); timeline->start(); connect(timeline, &QTimeLine::finished, [widget] {widget->hide();}); connect(timeline, &QTimeLine::finished, timeline, &QTimeLine::deleteLater); } void MainWin::toggleShowWidget(QWidget* widget, bool showToRight) { auto* timeline = new QTimeLine(800, this); timeline->setEasingCurve(QEasingCurve::InOutQuad); connect(timeline, &QTimeLine::valueChanged, [=]() { if (widget->isHidden()) { widget->show(); } const qreal value = timeline->currentValue(); const int widgetWidth = widget->width(); const int widgetPosY = widget->pos().y(); int moveX = 0; if (showToRight) { moveX = static_cast(value * widgetWidth) - widgetWidth; } else { const int frameRight = this->frameGeometry().right(); moveX = frameRight - static_cast(value * widgetWidth); } widget->move(moveX, widgetPosY); }); timeline->setDirection(QTimeLine::Forward); timeline->start(); connect(timeline, &QTimeLine::finished, timeline, &QTimeLine::deleteLater); } */ void MainWin::projectExplorerDockVisibilityChanged(bool visible) { m_toggleProjectExplorerDockAction->setChecked(visible); } void MainWin::propertiesDockVisibilityChanged(bool visible) { m_togglePropertiesDockAction->setChecked(visible); } void MainWin::cursorDockVisibilityChanged(bool visible) { //if the cursor dock was closed, switch to the "Select and Edit" mouse mode if (!visible) { // auto* worksheet = activeWorksheet(); //TODO: } } void MainWin::cartesianPlotMouseModeChanged(CartesianPlot::MouseMode mode) { if (mode != CartesianPlot::Cursor) { if (cursorDock) cursorDock->hide(); } else { if (!cursorDock) { cursorDock = new QDockWidget(i18n("Cursor"), this); cursorWidget = new CursorDock(cursorDock); cursorDock->setWidget(cursorWidget); connect(cursorDock, &QDockWidget::visibilityChanged, this, &MainWin::cursorDockVisibilityChanged); // cursorDock->setFloating(true); // does not work. Don't understand why // if (m_propertiesDock) // tabifyDockWidget(cursorDock, m_propertiesDock); // else addDockWidget(Qt::DockWidgetArea::RightDockWidgetArea, cursorDock); } auto* worksheet = static_cast(QObject::sender()); cursorWidget->setWorksheet(worksheet); cursorDock->show(); } } void MainWin::toggleFullScreen() { if (this->windowState() == Qt::WindowFullScreen) this->setWindowState(m_lastWindowState); else { m_lastWindowState = this->windowState(); this->showFullScreen(); } } void MainWin::closeEvent(QCloseEvent* event) { m_closing = true; if (!this->closeProject()) { m_closing = false; event->ignore(); } } void MainWin::dragEnterEvent(QDragEnterEvent* event) { event->accept(); } void MainWin::dropEvent(QDropEvent* event) { if (event->mimeData() && !event->mimeData()->urls().isEmpty()) { QUrl url = event->mimeData()->urls().at(0); const QString& f = url.toLocalFile(); #ifdef HAVE_LIBORIGIN if (Project::isLabPlotProject(f) || OriginProjectParser::isOriginProject(f)) #else if (Project::isLabPlotProject(f)) #endif openProject(f); else { if (!m_project) newProject(); importFileDialog(f); } event->accept(); } else event->ignore(); } void MainWin::handleSettingsChanges() { const KConfigGroup group = KSharedConfig::openConfig()->group( "Settings_General" ); if(dynamic_cast(centralWidget()) == nullptr) { QMdiArea::ViewMode viewMode = QMdiArea::ViewMode(group.readEntry("ViewMode", 0)); if (m_mdiArea->viewMode() != viewMode) { m_mdiArea->setViewMode(viewMode); if (viewMode == QMdiArea::SubWindowView) this->updateMdiWindowVisibility(); } if (m_mdiArea->viewMode() == QMdiArea::TabbedView) { m_tileWindows->setVisible(false); m_cascadeWindows->setVisible(false); QTabWidget::TabPosition tabPosition = QTabWidget::TabPosition(group.readEntry("TabPosition", 0)); if (m_mdiArea->tabPosition() != tabPosition) m_mdiArea->setTabPosition(tabPosition); } else { m_tileWindows->setVisible(true); m_cascadeWindows->setVisible(true); } } //autosave bool autoSave = group.readEntry("AutoSave", 0); if (m_autoSaveActive != autoSave) { m_autoSaveActive = autoSave; if (autoSave) m_autoSaveTimer.start(); else m_autoSaveTimer.stop(); } int interval = group.readEntry("AutoSaveInterval", 1); interval *= 60*1000; if (interval != m_autoSaveTimer.interval()) m_autoSaveTimer.setInterval(interval); //show memory info bool showMemoryInfo = group.readEntry(QLatin1String("ShowMemoryInfo"), true); if (m_showMemoryInfo != showMemoryInfo) { m_showMemoryInfo = showMemoryInfo; if (showMemoryInfo) { m_memoryInfoWidget = new MemoryWidget(statusBar()); statusBar()->addPermanentWidget(m_memoryInfoWidget); } else { if (m_memoryInfoWidget) { statusBar()->removeWidget(m_memoryInfoWidget); delete m_memoryInfoWidget; m_memoryInfoWidget = nullptr; } } } bool showWelcomeScreen = group.readEntry(QLatin1String("ShowWelcomeScreen"), true); if(m_showWelcomeScreen != showWelcomeScreen) { m_showWelcomeScreen = showWelcomeScreen; } } void MainWin::openDatasetExample() { newProject(); addAspectToProject(m_welcomeScreenHelper->releaseConfiguredSpreadsheet()); } /***************************************************************************************/ /************************************** dialogs ***************************************/ /***************************************************************************************/ /*! shows the dialog with the Undo-history. */ void MainWin::historyDialog() { if (!m_project->undoStack()) return; auto* dialog = new HistoryDialog(this, m_project->undoStack(), m_undoViewEmptyLabel); int index = m_project->undoStack()->index(); if (dialog->exec() != QDialog::Accepted) { if (m_project->undoStack()->count() != 0) m_project->undoStack()->setIndex(index); } //disable undo/redo-actions if the history was cleared //(in both cases, when accepted or rejected in the dialog) if (m_project->undoStack()->count() == 0) { m_undoAction->setEnabled(false); m_redoAction->setEnabled(false); } } /*! Opens the dialog to import data to the selected workbook, spreadsheet or matrix */ void MainWin::importFileDialog(const QString& fileName) { DEBUG("MainWin::importFileDialog()"); auto* dlg = new ImportFileDialog(this, false, fileName); // select existing container if (m_currentAspect->type() == AspectType::Spreadsheet || m_currentAspect->type() == AspectType::Matrix || m_currentAspect->type() == AspectType::Workbook) dlg->setCurrentIndex(m_projectExplorer->currentIndex()); else if (m_currentAspect->type() == AspectType::Column && m_currentAspect->parentAspect()->type() == AspectType::Spreadsheet) dlg->setCurrentIndex(m_aspectTreeModel->modelIndexOfAspect(m_currentAspect->parentAspect())); if (dlg->exec() == QDialog::Accepted) { dlg->importTo(statusBar()); m_project->setChanged(true); } delete dlg; DEBUG("MainWin::importFileDialog() DONE"); } void MainWin::importSqlDialog() { DEBUG("MainWin::importSqlDialog()"); auto* dlg = new ImportSQLDatabaseDialog(this); // select existing container if (m_currentAspect->type() == AspectType::Spreadsheet || m_currentAspect->type() == AspectType::Matrix || m_currentAspect->type() == AspectType::Workbook) dlg->setCurrentIndex(m_projectExplorer->currentIndex()); else if (m_currentAspect->type() == AspectType::Column && m_currentAspect->parentAspect()->type() == AspectType::Spreadsheet) dlg->setCurrentIndex(m_aspectTreeModel->modelIndexOfAspect(m_currentAspect->parentAspect())); if (dlg->exec() == QDialog::Accepted) { dlg->importTo(statusBar()); m_project->setChanged(true); } delete dlg; DEBUG("MainWin::importSqlDialog() DONE"); } void MainWin::importProjectDialog() { DEBUG("MainWin::importProjectDialog()"); ImportProjectDialog::ProjectType type; if (QObject::sender() == m_importOpjAction) type = ImportProjectDialog::ProjectOrigin; else type = ImportProjectDialog::ProjectLabPlot; auto* dlg = new ImportProjectDialog(this, type); // set current folder dlg->setCurrentFolder(m_currentFolder); if (dlg->exec() == QDialog::Accepted) { dlg->importTo(statusBar()); m_project->setChanged(true); } delete dlg; DEBUG("MainWin::importProjectDialog() DONE"); } +/*! + * \brief opens a dialog to import datasets + */ +void MainWin::importDatasetDialog() { + ImportDatasetDialog* dlg = new ImportDatasetDialog(this); + if (dlg->exec() == QDialog::Accepted) { + Spreadsheet* spreadsheet = new Spreadsheet(i18n("Dataset%1", 1)); + DatasetHandler* dataset = new DatasetHandler(spreadsheet); + dlg->importToDataset(dataset, statusBar()); + + QTimer timer; + timer.setSingleShot(true); + QEventLoop loop; + connect(dataset, &DatasetHandler::downloadCompleted, &loop, &QEventLoop::quit); + connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); + timer.start(1500); + loop.exec(); + + if(timer.isActive()){ + timer.stop(); + addAspectToProject(spreadsheet); + delete dataset; + } + else + delete dataset; + } + delete dlg; +} + /*! opens the dialog for the export of the currently active worksheet, spreadsheet or matrix. */ void MainWin::exportDialog() { QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return; AbstractPart* part = dynamic_cast(win)->part(); if (part->exportView()) statusBar()->showMessage(i18n("%1 exported", part->name())); } void MainWin::editFitsFileDialog() { auto* editDialog = new FITSHeaderEditDialog(this); if (editDialog->exec() == QDialog::Accepted) { if (editDialog->saved()) statusBar()->showMessage(i18n("FITS files saved")); } } /*! adds a new file data source to the current project. */ void MainWin::newLiveDataSourceActionTriggered() { ImportFileDialog* dlg = new ImportFileDialog(this, true); if (dlg->exec() == QDialog::Accepted) { if (static_cast(dlg->sourceType()) == LiveDataSource::MQTT) { #ifdef HAVE_MQTT MQTTClient* mqttClient = new MQTTClient(i18n("MQTT Client%1", 1)); dlg->importToMQTT(mqttClient); mqttClient->setName(mqttClient->clientHostName()); QVector existingClients = m_project->children(AbstractAspect::Recursive); //doesn't make sense to have more MQTTClients connected to the same broker bool found = false; for (const auto* client : existingClients) { if (client->clientHostName() == mqttClient->clientHostName() && client->clientPort() == mqttClient->clientPort()) { found = true; break; } } if (!found) addAspectToProject(mqttClient); else { delete mqttClient; QMessageBox::warning(this, "Warning", "There already is a MQTTClient with this host!"); } #endif } else { LiveDataSource* dataSource = new LiveDataSource(i18n("Live data source%1", 1), false); dlg->importToLiveDataSource(dataSource, statusBar()); addAspectToProject(dataSource); } } delete dlg; } -/*! - * \brief adds a new dataset to the current project - */ -void MainWin::newDatasetActionTriggered() { - ImportDatasetDialog* dlg = new ImportDatasetDialog(this); - if (dlg->exec() == QDialog::Accepted) { - Spreadsheet* spreadsheet = new Spreadsheet(i18n("Dataset%1", 1)); - DatasetHandler* dataset = new DatasetHandler(spreadsheet); - dlg->importToDataset(dataset, statusBar()); - - QTimer timer; - timer.setSingleShot(true); - QEventLoop loop; - connect(dataset, &DatasetHandler::downloadCompleted, &loop, &QEventLoop::quit); - connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); - timer.start(1500); - loop.exec(); - - if(timer.isActive()){ - timer.stop(); - addAspectToProject(spreadsheet); - delete dataset; - } - else - delete dataset; - } - delete dlg; -} - void MainWin::addAspectToProject(AbstractAspect* aspect) { const QModelIndex& index = m_projectExplorer->currentIndex(); if (index.isValid()) { auto* parent = static_cast(index.internalPointer()); #ifdef HAVE_MQTT //doesn't make sense to add a new MQTTClient to an existing MQTTClient or to any of its successors QString className = parent->metaObject()->className(); MQTTClient* clientAncestor = parent->ancestor(); if (className == "MQTTClient") parent = parent->parentAspect(); else if (clientAncestor != nullptr) parent = clientAncestor->parentAspect(); #endif parent->folder()->addChild(aspect); } else m_project->addChild(aspect); } void MainWin::settingsDialog() { auto* dlg = new SettingsDialog(this); connect (dlg, &SettingsDialog::settingsChanged, this, &MainWin::handleSettingsChanges); connect (dlg, &SettingsDialog::resetWelcomeScreen, this, &MainWin::resetWelcomeScreen); dlg->exec(); } #ifdef HAVE_CANTOR_LIBS void MainWin::cantorSettingsDialog() { static KCoreConfigSkeleton* emptyConfig = new KCoreConfigSkeleton(); KConfigDialog *cantorDialog = new KConfigDialog(this, QLatin1String("Cantor Settings"), emptyConfig); for (auto* backend : Cantor::Backend::availableBackends()) if (backend->config()) //It has something to configure, so add it to the dialog cantorDialog->addPage(backend->settingsWidget(cantorDialog), backend->config(), backend->name(), backend->icon()); cantorDialog->show(); } #endif diff --git a/src/kdefrontend/MainWin.h b/src/kdefrontend/MainWin.h index b07a67a6c..8a7715433 100644 --- a/src/kdefrontend/MainWin.h +++ b/src/kdefrontend/MainWin.h @@ -1,330 +1,330 @@ /*************************************************************************** File : MainWin.h Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2011-2018 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2008-2018 by Stefan Gerlach (stefan.gerlach@uni.kn) Description : Main window of the application ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #ifndef MAINWIN_H #define MAINWIN_H #include "backend/worksheet/plots/cartesian/CartesianPlot.h" #include #include class AbstractAspect; class AspectTreeModel; class Folder; class ProjectExplorer; class Project; class Worksheet; class Note; class Workbook; class Datapicker; class Image; class Spreadsheet; class Matrix; class GuiObserver; class AxisDock; class CursorDock; class NoteDock; class CartesianPlotDock; class HistogramDock; class BarChartPlotDock; class CartesianPlotLegendDock; class CustomPointDock; class ColumnDock; class LiveDataDock; class MatrixDock; class ProjectDock; class SpreadsheetDock; class XYCurveDock; class XYEquationCurveDock; class XYDataReductionCurveDock; class XYDifferentiationCurveDock; class XYIntegrationCurveDock; class XYInterpolationCurveDock; class XYSmoothCurveDock; class XYFitCurveDock; class XYFourierFilterCurveDock; class XYFourierTransformCurveDock; class XYConvolutionCurveDock; class XYCorrelationCurveDock; class WorksheetDock; class LabelWidget; class DatapickerImageWidget; class DatapickerCurveWidget; class MemoryWidget; class CartesianPlot; #ifdef HAVE_CANTOR_LIBS class CantorWorksheet; class CantorWorksheetDock; #endif class QDockWidget; class QStackedWidget; class QDragEnterEvent; class QDropEvent; class QMdiArea; class QMdiSubWindow; class QToolButton; class KRecentFilesAction; class QQuickWidget; class WelcomeScreenHelper; class ImportDatasetWidget; class TreeModel; class MainWin : public KXmlGuiWindow { Q_OBJECT public: explicit MainWin(QWidget* parent = nullptr, const QString& filename = nullptr); ~MainWin() override; void showPresenter(); AspectTreeModel* model() const; Project* project() const; void addAspectToProject(AbstractAspect*); private: QMdiArea* m_mdiArea; QMdiSubWindow* m_currentSubWindow{nullptr}; Project* m_project{nullptr}; AspectTreeModel* m_aspectTreeModel{nullptr}; ProjectExplorer* m_projectExplorer{nullptr}; QDockWidget* m_projectExplorerDock{nullptr}; QDockWidget* m_propertiesDock{nullptr}; AbstractAspect* m_currentAspect{nullptr}; Folder* m_currentFolder{nullptr}; QString m_currentFileName; QString m_undoViewEmptyLabel; bool m_suppressCurrentSubWindowChangedEvent{false}; bool m_closing{false}; bool m_projectClosing{false}; bool m_autoSaveActive{false}; QTimer m_autoSaveTimer; bool m_showMemoryInfo{true}; bool m_showWelcomeScreen{false}; bool m_saveWelcomeScreen{true}; MemoryWidget* m_memoryInfoWidget{nullptr}; Qt::WindowStates m_lastWindowState; //< last window state before switching to full screen mode QMdiSubWindow* m_welcomeWindow{nullptr}; QQuickWidget* m_welcomeWidget{nullptr}; WelcomeScreenHelper* m_welcomeScreenHelper{nullptr}; ImportDatasetWidget* m_importDatasetWidget{nullptr}; KRecentFilesAction* m_recentProjectsAction; QAction* m_saveAction; QAction* m_saveAsAction; QAction* m_printAction; QAction* m_printPreviewAction; QAction* m_importFileAction; QAction* m_importSqlAction; QAction* m_importDatasetAction; QAction* m_importLabPlotAction; QAction* m_importOpjAction; QAction* m_exportAction; QAction* m_closeAction; QAction* m_newFolderAction; QAction* m_newWorkbookAction; QAction* m_newSpreadsheetAction; QAction* m_newMatrixAction; QAction* m_newWorksheetAction; QAction* m_newNotesAction; QAction* m_newLiveDataSourceAction; QAction* m_newSqlDataSourceAction; QAction* m_newScriptAction; QAction* m_newProjectAction; QAction* m_historyAction; QAction* m_undoAction; QAction* m_redoAction; QAction* m_tileWindows; QAction* m_cascadeWindows; QAction* m_newDatapickerAction; QAction* m_editFitsFileAction; //toggling doch widgets QAction* m_toggleProjectExplorerDocQAction; QAction* m_togglePropertiesDocQAction; //worksheet actions QAction* worksheetZoomInAction; QAction* worksheetZoomOutAction; QAction* worksheetZoomOriginAction; QAction* worksheetZoomFitPageHeightAction; QAction* worksheetZoomFitPageWidthAction; QAction* worksheetZoomFitSelectionAction; QAction* worksheetNavigationModeAction; QAction* worksheetZoomModeAction; QAction* worksheetSelectionModeAction; QAction* worksheetVerticalLayoutAction; QAction* worksheetHorizontalLayoutAction; QAction* worksheetGridLayoutAction; QAction* worksheetBreakLayoutAction; QAction* m_visibilityFolderAction; QAction* m_visibilitySubfolderAction; QAction* m_visibilityAllAction; QAction* m_toggleProjectExplorerDockAction; QAction* m_togglePropertiesDockAction; //Menus QMenu* m_visibilityMenu{nullptr}; QMenu* m_newMenu{nullptr}; QMenu* m_importMenu; QMenu* m_editMenu{nullptr}; //Docks QStackedWidget* stackedWidget; AxisDock* axisDock{nullptr}; QDockWidget* cursorDock{nullptr}; CursorDock* cursorWidget{nullptr}; NoteDock* notesDock{nullptr}; CartesianPlotDock* cartesianPlotDock{nullptr}; CartesianPlotLegendDock* cartesianPlotLegendDock{nullptr}; ColumnDock* columnDock{nullptr}; LiveDataDock* m_liveDataDock{nullptr}; MatrixDock* matrixDock{nullptr}; SpreadsheetDock* spreadsheetDock{nullptr}; ProjectDock* projectDock{nullptr}; XYCurveDock* xyCurveDock{nullptr}; XYEquationCurveDock* xyEquationCurveDock{nullptr}; XYDataReductionCurveDock* xyDataReductionCurveDock{nullptr}; XYDifferentiationCurveDock* xyDifferentiationCurveDock{nullptr}; XYIntegrationCurveDock* xyIntegrationCurveDock{nullptr}; XYInterpolationCurveDock* xyInterpolationCurveDock{nullptr}; XYSmoothCurveDock* xySmoothCurveDock{nullptr}; XYFitCurveDock* xyFitCurveDock{nullptr}; XYFourierFilterCurveDock* xyFourierFilterCurveDock{nullptr}; XYFourierTransformCurveDock* xyFourierTransformCurveDock{nullptr}; XYConvolutionCurveDock* xyConvolutionCurveDock{nullptr}; XYCorrelationCurveDock* xyCorrelationCurveDock{nullptr}; HistogramDock* histogramDock{nullptr}; WorksheetDock* worksheetDock{nullptr}; LabelWidget* textLabelDock{nullptr}; CustomPointDock* customPointDock{nullptr}; DatapickerImageWidget* datapickerImageDock{nullptr}; DatapickerCurveWidget* datapickerCurveDock{nullptr}; void initActions(); void initMenus(); bool warnModified(); void activateSubWindowForAspect(const AbstractAspect*) const; bool save(const QString&); // void toggleShowWidget(QWidget* widget, bool showToRight); // void toggleHideWidget(QWidget* widget, bool hideToLeft); Spreadsheet* activeSpreadsheet() const; //Cantor #ifdef HAVE_CANTOR_LIBS QMenu* m_newCantorWorksheetMenu; CantorWorksheetDock* cantorWorksheetDock{nullptr}; #endif friend class GuiObserver; GuiObserver* m_guiObserver{nullptr}; protected: void closeEvent(QCloseEvent*) override; void dragEnterEvent(QDragEnterEvent*) override; void dropEvent(QDropEvent*) override; private slots: void initGUI(const QString&); QQuickWidget* createWelcomeScreen(); void resetWelcomeScreen(); void createMdiArea(); void updateGUI(); void updateGUIOnProjectChanges(); void undo(); void redo(); bool newProject(); void openProject(); void openProject(const QString&); void openRecentProject(const QUrl&); bool closeProject(); bool saveProject(); bool saveProjectAs(); void autoSaveProject(); void print(); void printPreview(); void historyDialog(); void importFileDialog(const QString& fileName = QString()); void importSqlDialog(); void importProjectDialog(); + void importDatasetDialog(); void exportDialog(); void editFitsFileDialog(); void settingsDialog(); void projectChanged(); void colorSchemeChanged(QAction*); void openDatasetExample(); //Cantor #ifdef HAVE_CANTOR_LIBS void newCantorWorksheet(QAction* action); void cantorSettingsDialog(); #endif void newFolder(); void newWorkbook(); void newSpreadsheet(); void newMatrix(); void newWorksheet(); void newNotes(); void newDatapicker(); //TODO: void newScript(); void newLiveDataSourceActionTriggered(); - void newDatasetActionTriggered(); void createContextMenu(QMenu*) const; void createFolderContextMenu(const Folder*, QMenu*) const; void handleAspectAdded(const AbstractAspect*); void handleAspectAboutToBeRemoved(const AbstractAspect*); void handleAspectRemoved(const AbstractAspect*,const AbstractAspect*,const AbstractAspect*); void handleCurrentAspectChanged(AbstractAspect* ); void handleCurrentSubWindowChanged(QMdiSubWindow*); void handleShowSubWindowRequested(); void handleSettingsChanges(); void setMdiWindowVisibility(QAction*); void updateMdiWindowVisibility() const; void toggleDockWidget(QAction*); void toggleFullScreen(); void projectExplorerDockVisibilityChanged(bool); void propertiesDockVisibilityChanged(bool); void cursorDockVisibilityChanged(bool); void cartesianPlotMouseModeChanged(CartesianPlot::MouseMode); }; #endif diff --git a/src/kdefrontend/WelcomeScreenHelper.cpp b/src/kdefrontend/WelcomeScreenHelper.cpp index 15a6e8083..8f04924f8 100644 --- a/src/kdefrontend/WelcomeScreenHelper.cpp +++ b/src/kdefrontend/WelcomeScreenHelper.cpp @@ -1,452 +1,452 @@ /*************************************************************************** File : WelcomeScreenHelper.cpp Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2019 Ferencz Kovacs (kferike98@gmail.com) Description : Helper class for the welcome screen ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "WelcomeScreenHelper.h" #include "DatasetModel.h" #include "kdefrontend/datasources/ImportDatasetWidget.h" #include "backend/datasources/DatasetHandler.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*! \class WelcomeScreenHelper \brief Helper class for the welcome screen \ingroup kdefrontend */ WelcomeScreenHelper::WelcomeScreenHelper() { m_datasetWidget = new ImportDatasetWidget(0); m_datasetWidget->hide(); QIcon icon = QIcon::fromTheme("labplot-maximize"); m_maxIcon = icon.pixmap(icon.availableSizes().first()); icon = QIcon::fromTheme("labplot-minimize"); m_minIcon = icon.pixmap(icon.availableSizes().first()); m_datasetModel = new DatasetModel(m_datasetWidget->getDatasetsMap()); loadConfig(); processExampleProjects(); } WelcomeScreenHelper::~WelcomeScreenHelper() { //save width&height ratio values KConfigGroup conf(KSharedConfig::openConfig(), "WelcomeScreenHelper"); int widthCount = m_widthScale.size(); conf.writeEntry("width_count", widthCount); int currentWidthIndex = 0; for(auto item = m_widthScale.begin(); item != m_widthScale.end() && currentWidthIndex < widthCount; item++) { conf.writeEntry("widthName_" + QString::number(currentWidthIndex), item.key()); conf.writeEntry("widthValue_" + QString::number(currentWidthIndex), QString::number(item.value())); currentWidthIndex++; } int heightCount = m_heightScale.size(); conf.writeEntry("height_count", widthCount); int currentHeightIndex = 0; for(auto item = m_heightScale.begin(); item != m_heightScale.end() && currentHeightIndex < heightCount; item++) { conf.writeEntry("heightName_" + QString::number(currentHeightIndex), item.key()); conf.writeEntry("heightValue_" + QString::number(currentHeightIndex), QString::number(item.value())); currentHeightIndex++; } } /** * @brief Loads the saved configuration */ void WelcomeScreenHelper::loadConfig() { KConfigGroup conf(KSharedConfig::openConfig(), "WelcomeScreenHelper"); int widthCount = conf.readEntry("width_count", -1); for(int i = 0; i < widthCount; ++i) { QString id = conf.readEntry("widthName_" + QString::number(i), ""); double value = QString(conf.readEntry("widthValue_" + QString::number(i), "-1")).toDouble(); if(!id.isEmpty() && value != -1) m_widthScale[id] = value; } int heightCount = conf.readEntry("height_count", -1); for(int i = 0; i < heightCount; ++i) { QString id = conf.readEntry("heightName_" + QString::number(i), ""); double value = QString(conf.readEntry("heightValue_" + QString::number(i), "-1")).toDouble(); if(!id.isEmpty() && value != -1) m_heightScale[id] = value; } } /** * @brief Handles a dataset being clicked in the dataset section of the WelcomeScreen. * Initiates listing information about the dataset in the previously mentioned section. * * @param category the category the dataset belongs to * @param subcategory the subcategory the dataset belongs to * @param datasetName the name of the dataset */ void WelcomeScreenHelper::datasetClicked(const QString& category, const QString& subcategory, const QString& datasetName) { m_datasetWidget->setCollection("All"); m_datasetWidget->setCategory(category); m_datasetWidget->setSubcategory(subcategory); m_datasetWidget->setDataset(datasetName); m_spreadsheet.reset(new Spreadsheet(i18n("Dataset%1", 1))); if(m_datasetHandler != nullptr) delete m_datasetHandler; m_datasetHandler = new DatasetHandler(m_spreadsheet.get()); - m_datasetWidget->loadDatasetToProcess(m_datasetHandler); + m_datasetWidget->import(m_datasetHandler); QTimer timer; timer.setSingleShot(true); QEventLoop loop; connect(m_datasetHandler, &DatasetHandler::downloadCompleted, &loop, &QEventLoop::quit); connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); timer.start(1500); loop.exec(); if(timer.isActive()){ timer.stop(); emit datasetFound(); } else emit datasetNotFound(); } /** * @brief Returns the dataset's full name. */ QVariant WelcomeScreenHelper::datasetName() { return QVariant(m_spreadsheet->name()); } /** * @brief Returns the dataset's descripton. */ QVariant WelcomeScreenHelper::datasetDescription() { return QVariant(m_spreadsheet->comment()); } /** * @brief Returns the number of the dataset's columns. */ QVariant WelcomeScreenHelper::datasetColumns() { return QVariant(m_spreadsheet->columnCount()); } /** * @brief Returns the number of the dataset's rows. */ QVariant WelcomeScreenHelper::datasetRows() { return QVariant(m_spreadsheet->rowCount()); } /** * @brief Returns a pointer to the spreadsheet in which the data of the dataset was loaded. */ Spreadsheet* WelcomeScreenHelper::releaseConfiguredSpreadsheet() { return m_spreadsheet.release(); } /** * @brief Returns the thumbnail image saved with the project. * @param url the path to the saved project file. */ QVariant WelcomeScreenHelper::getProjectThumbnail(const QUrl& url) { QString filename; if (url.isLocalFile()) // fix for Windows filename = url.toLocalFile(); else filename = url.path(); QIODevice* file; // first try gzip compression, because projects can be gzipped and end with .lml if (filename.endsWith(QLatin1String(".lml"), Qt::CaseInsensitive)) file = new KCompressionDevice(filename,KFilterDev::compressionTypeForMimeType("application/x-gzip")); else // opens filename using file ending file = new KFilterDev(filename); if (!file) file = new QFile(filename); if (!file->open(QIODevice::ReadOnly)) { qDebug() << "Could not open file for reading."; return QVariant(); } char c; bool rc = file->getChar(&c); if (!rc) { qDebug() << "The project file is empty."; file->close(); delete file; return false; } file->seek(0); //parse XML XmlStreamReader reader(file); while (!(reader.isStartDocument() || reader.atEnd())) reader.readNext(); if (!(reader.atEnd())) { if (!reader.skipToNextTag()) return false; if (reader.name() == "project") { QString thumbnail = reader.attributes().value("thumbnail").toString(); thumbnail.prepend("data:image/jpg;base64,"); return QVariant(thumbnail); } } return QVariant(); } /** * @brief Returns a pointer to the datasetModel of the class. */ DatasetModel* WelcomeScreenHelper::getDatasetModel() { return m_datasetModel; } /** * @brief Processes metadata file containing example projects. */ void WelcomeScreenHelper::processExampleProjects() { const QString filePath = QStandardPaths::locate(QStandardPaths::AppDataLocation, "example_projects/example_projects.json"); QFile file(filePath); if (file.open(QIODevice::ReadOnly)) { QJsonDocument document = QJsonDocument::fromJson(file.readAll()); QJsonArray exampleArray = document.array(); //processing examples for(int i = 0 ; i < exampleArray.size(); ++i) { const QJsonObject currentExample = exampleArray[i].toObject(); const QString exampleName = currentExample.value("name").toString(); if(m_projectNameList.contains(exampleName)) { qDebug() << "There is already an example file with this name"; } else { m_projectNameList.append(exampleName); const QString exampleFile = currentExample.value("fileName").toString(); m_pathMap[exampleName] = exampleFile; //processing tags const QJsonArray tags = currentExample.value("tags").toArray(); for(int j = 0; j < tags.size(); ++j) { QString tagName = tags[j].toString(); m_tagMap[tagName].append(exampleName); m_datasetTag[exampleName].append(tagName); } } } file.close(); } else { qDebug("Couldn't open dataset category file"); } } /** * @brief Returns in string format the thumbnail for the given example file */ QVariant WelcomeScreenHelper::getExampleProjectThumbnail(const QString& exampleName) { const QString filePath = QStandardPaths::locate(QStandardPaths::AppDataLocation, "example_projects/" + m_pathMap[exampleName]); return getProjectThumbnail(filePath); } /** * @brief Returns the list of example projects. */ QVariant WelcomeScreenHelper::getExampleProjects() { return QVariant(m_projectNameList); } /** * @brief Returns the tags of the given example project. */ QVariant WelcomeScreenHelper::getExampleProjectTags(const QString& exampleName) { QString tags; const QStringList& tagList = m_datasetTag[exampleName]; for(int i = 0; i < tagList.size(); i++) { tags.append(tagList[i]); if(i < tagList.size() - 1) tags.append(", "); } return QVariant(tags); } /** * @brief Handles an example project being clicked in the welcome screen. * @param exampleName the name of the clicked example project */ void WelcomeScreenHelper::exampleProjectClicked(const QString& exampleName) { QString path = QStandardPaths::locate(QStandardPaths::AppDataLocation, "example_projects/" + m_pathMap[exampleName]); emit openExampleProject(path); } /** * @brief Searches among the example projects based on the text introduce in the search bar of the welcome screen. * @param searchtext - the text based on which we'll have to search * @return Returns the results */ QVariant WelcomeScreenHelper::searchExampleProjects(const QString& searchtext) { QStringList results; //search based on tags for(auto tag = m_tagMap.begin(); tag != m_tagMap.end(); tag++) { if (tag.key().contains(searchtext)) { for(QString example : tag.value()) { if(!results.contains(example)) results.append(example); } } } //search based on name for(QString example : m_projectNameList) { if(example.contains(searchtext) && !results.contains(example)) results.append(example); } return QVariant(results); } /** * @brief Sets the width scale for the given section, which will be saved. */ void WelcomeScreenHelper::setWidthScale(QString sectionID, double scale) { m_widthScale[sectionID] = scale; } /** * @brief Sets the height scale for the given section, which will be saved. */ void WelcomeScreenHelper::setHeightScale(QString sectionID, double scale) { m_heightScale[sectionID] = scale; } /** * @brief Returns the width scale for the given section. */ QVariant WelcomeScreenHelper::getWidthScale(QString sectionID) { if(m_widthScale.keys().contains(sectionID)) { return QVariant(m_widthScale[sectionID]); } return QVariant(-1); } /** * @brief Returns the height scale for the given section. */ QVariant WelcomeScreenHelper::getHeightScale(QString sectionID) { if(m_heightScale.keys().contains(sectionID)) { return QVariant(m_heightScale[sectionID]); } return QVariant(-1);; } /** * @brief Returns the maximize icon. */ QVariant WelcomeScreenHelper::getMaxIcon() { QByteArray bArray; QBuffer buffer(&bArray); buffer.open(QIODevice::WriteOnly); m_maxIcon.save(&buffer, "PNG"); QString image = QString::fromLatin1(bArray.toBase64().data()); image.prepend("data:image/png;base64,"); return QVariant(image); } /** * @brief Returns the minimize icon. */ QVariant WelcomeScreenHelper::getMinIcon() { QByteArray bArray; QBuffer buffer(&bArray); buffer.open(QIODevice::WriteOnly); m_minIcon.save(&buffer, "PNG"); QString image = QString::fromLatin1(bArray.toBase64().data()); image.prepend("data:image/png;base64,"); return QVariant(image); } /** * @brief Returns the go-back icon. */ QVariant WelcomeScreenHelper::getBackIcon() { QIcon icon = QIcon::fromTheme("labplot-back"); QPixmap pixmap = icon.pixmap(icon.availableSizes().first()); QByteArray bArray; QBuffer buffer(&bArray); buffer.open(QIODevice::WriteOnly); pixmap.save(&buffer, "PNG"); QString image = QString::fromLatin1(bArray.toBase64().data()); image.prepend("data:image/png;base64,"); return QVariant(image); } /** * @brief Returns the go-forward icon. */ QVariant WelcomeScreenHelper::getForwardIcon() { QIcon icon = QIcon::fromTheme("labplot-forward"); QPixmap pixmap = icon.pixmap(icon.availableSizes().first()); QByteArray bArray; QBuffer buffer(&bArray); buffer.open(QIODevice::WriteOnly); pixmap.save(&buffer, "PNG"); QString image = QString::fromLatin1(bArray.toBase64().data()); image.prepend("data:image/png;base64,"); return QVariant(image); } diff --git a/src/kdefrontend/datasources/ImportDatasetDialog.cpp b/src/kdefrontend/datasources/ImportDatasetDialog.cpp index 23e2a238d..06b125a5b 100644 --- a/src/kdefrontend/datasources/ImportDatasetDialog.cpp +++ b/src/kdefrontend/datasources/ImportDatasetDialog.cpp @@ -1,128 +1,128 @@ /*************************************************************************** File : ImportDatasetDialog.cpp Project : LabPlot Description : import dataset data dialog -------------------------------------------------------------------- Copyright : (C) 2019 Ferencz Koovacs (kferike98@gmail.com) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "ImportDatasetDialog.h" #include "ImportDatasetWidget.h" #include "backend/datasources/DatasetHandler.h" #include "QWindow" #include "QProgressBar" #include "QDialogButtonBox" #include "QStatusBar" #include "KConfigGroup" #include "KSharedConfig" #include "KWindowConfig" /*! \class ImportDatasetDialog \brief Dialog for importing data from a dataset. Embeds \c ImportDatasetWidget and provides the standard buttons. \ingroup kdefrontend */ ImportDatasetDialog::ImportDatasetDialog(MainWin* parent) : ImportDialog(parent), m_importDatasetWidget(new ImportDatasetWidget(this)){ vLayout->addWidget(m_importDatasetWidget); connect(m_importDatasetWidget, &ImportDatasetWidget::datasetSelected, this, &ImportDatasetDialog::checkOkButton); connect(m_importDatasetWidget, &ImportDatasetWidget::datasetDoubleClicked, [this]() { checkOkButton(); if(okButton->isEnabled()) accept(); }); //dialog buttons QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok |QDialogButtonBox::Cancel); okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setEnabled(false); //ok is only available if a valid container was selected vLayout->addWidget(buttonBox); connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); setWindowTitle(i18nc("@title:window", "Import from Dataset Collection")); create(); QApplication::processEvents(QEventLoop::AllEvents, 0); KConfigGroup conf(KSharedConfig::openConfig(), "ImportDatasetDialog"); if (conf.exists()) { KWindowConfig::restoreWindowSize(windowHandle(), conf); resize(windowHandle()->size()); } else { resize(QSize(0, 0).expandedTo(minimumSize())); } checkOkButton(); } ImportDatasetDialog::~ImportDatasetDialog() { KConfigGroup conf(KSharedConfig::openConfig(), "ImportDatasetDialog"); KWindowConfig::saveWindowSize(windowHandle(), conf); } QString ImportDatasetDialog::selectedObject() const { return QString(); } /*! triggers the import of a dataset's data */ void ImportDatasetDialog::importToDataset(DatasetHandler* datasetHandler, QStatusBar* statusBar) const { //show a progress bar in the status bar auto* progressBar = new QProgressBar(); progressBar->setRange(0, 100); connect(datasetHandler, &DatasetHandler::downloadProgress, progressBar, &QProgressBar::setValue); statusBar->clearMessage(); statusBar->addWidget(progressBar, 1); WAIT_CURSOR; QApplication::processEvents(QEventLoop::AllEvents, 100); QTime timer; timer.start(); - m_importDatasetWidget->loadDatasetToProcess(datasetHandler); + m_importDatasetWidget->import(datasetHandler); statusBar->showMessage(i18n("Dataset imported in %1 seconds.", static_cast(timer.elapsed()/1000))); RESET_CURSOR; statusBar->removeWidget(progressBar); } /** * @brief Checks whether the OK button of the dialog can be pressed or not */ void ImportDatasetDialog::checkOkButton() { bool enable = (!m_importDatasetWidget->getSelectedDataset().isEmpty()); okButton->setEnabled(enable); } void ImportDatasetDialog::importTo(QStatusBar* statusBar) const { Q_UNUSED(statusBar); } diff --git a/src/kdefrontend/datasources/ImportDatasetWidget.cpp b/src/kdefrontend/datasources/ImportDatasetWidget.cpp index 7d5c24d79..ba065a3d6 100644 --- a/src/kdefrontend/datasources/ImportDatasetWidget.cpp +++ b/src/kdefrontend/datasources/ImportDatasetWidget.cpp @@ -1,860 +1,863 @@ /*************************************************************************** File : ImportDatasetWidget.cpp Project : LabPlot Description : import online dataset widget -------------------------------------------------------------------- Copyright : (C) 2019 Kovacs Ferencz (kferike98@gmail.com) Copyright : (C) 2019 by Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "backend/datasources/DatasetHandler.h" #include "kdefrontend/datasources/ImportDatasetWidget.h" #include "kdefrontend/datasources/DatasetMetadataManagerDialog.h" #include "kdefrontend/DatasetModel.h" #include #include #include #include #include #include #include #include #include #include #include #include // #include // #include // #include // #include /*! \class ImportDatasetWidget \brief Widget for importing data from a dataset. \ingroup kdefrontend */ ImportDatasetWidget::ImportDatasetWidget(QWidget* parent) : QWidget(parent), m_categoryCompleter(new QCompleter), - m_datasetCompleter(new QCompleter) { + m_datasetCompleter(new QCompleter), + m_networkManager(new QNetworkAccessManager(this)) { ui.setupUi(this); m_jsonDir = QStandardPaths::locate(QStandardPaths::AppDataLocation, QLatin1String("datasets"), QStandardPaths::LocateDirectory); loadCategories(); ui.lwDatasets->setSelectionMode(QAbstractItemView::SingleSelection); ui.twCategories->setSelectionMode(QAbstractItemView::SingleSelection); const int size = ui.leSearchCategories->height(); ui.lSearchCategories->setPixmap( QIcon::fromTheme(QLatin1String("go-next")).pixmap(size, size) ); ui.lSearchDatasets->setPixmap( QIcon::fromTheme(QLatin1String("go-next")).pixmap(size, size) ); QString info = i18n("Enter the name of the category to navigate to it."); ui.lSearchCategories->setToolTip(info); ui.leSearchCategories->setToolTip(info); ui.leSearchCategories->setPlaceholderText(i18n("Category...")); info = i18n("Enter the name of the dataset to navigate to it."); ui.lSearchDatasets->setToolTip(info); ui.leSearchDatasets->setToolTip(info); ui.leSearchDatasets->setPlaceholderText(i18n("Dataset...")); connect(ui.cbCollections, static_cast(&QComboBox::currentIndexChanged), this, &ImportDatasetWidget::collectionChanged); connect(ui.twCategories, &QTreeWidget::itemDoubleClicked, this, &ImportDatasetWidget::listDatasetsForSubcategory); connect(ui.twCategories, &QTreeWidget::itemSelectionChanged, [this] { if(!m_loadingCategories) listDatasetsForSubcategory(ui.twCategories->selectedItems().first()); }); connect(ui.leSearchDatasets, &QLineEdit::textChanged, this, &ImportDatasetWidget::scrollToDatasetListItem); // connect(ui.bClearCache, &QPushButton::clicked, this, &ImportDatasetWidget::clearCache); connect(ui.leSearchCategories, &QLineEdit::textChanged, this, &ImportDatasetWidget::scrollToCategoryTreeItem); // connect(ui.bRefresh, &QPushButton::clicked, this, &ImportDatasetWidget::refreshCategories); // connect(ui.bRestore, &QPushButton::clicked, this, &ImportDatasetWidget::restoreBackup); connect(ui.bNewDataset, &QPushButton::clicked, this, &ImportDatasetWidget::showDatasetMetadataManager); connect(ui.lwDatasets, &QListWidget::itemSelectionChanged, [this]() { datasetChanged(); }); connect(ui.lwDatasets, &QListWidget::doubleClicked, [this]() {emit datasetDoubleClicked(); }); + connect(m_networkManager, &QNetworkAccessManager::finished, this, &ImportDatasetWidget::downloadFinished); + ui.bRefresh->hide(); ui.bClearCache->hide(); ui.bRestore->hide(); ui.bNewDataset->hide(); } ImportDatasetWidget::~ImportDatasetWidget() { if(m_categoryCompleter != nullptr) delete m_categoryCompleter; if(m_datasetCompleter != nullptr) delete m_datasetCompleter; } /** * @brief Processes the json metadata file that contains the list of categories and subcategories and their datasets. */ void ImportDatasetWidget::loadCategories() { m_datasetsMap.clear(); ui.cbCollections->clear(); const QString collectionsFileName = m_jsonDir + QLatin1String("/DatasetCollections.json"); QFile file(collectionsFileName); if (file.open(QIODevice::ReadOnly)) { QJsonDocument document = QJsonDocument::fromJson(file.readAll()); file.close(); if (!document.isArray()) { QDEBUG("Invalid definition of " + collectionsFileName); return; } m_collections = document.array(); for (int col = 0; col < m_collections.size(); col++) { const QJsonObject& collection = m_collections[col].toObject(); const QString& collectionName = collection["name"].toString(); QString path = m_jsonDir + QLatin1Char('/') + collectionName + ".json"; QFile collectionFile(path); if (collectionFile.open(QIODevice::ReadOnly)) { QJsonDocument collectionDocument = QJsonDocument::fromJson(collectionFile.readAll()); if (!collectionDocument.isObject()) { QDEBUG("Invalid definition of " + path); continue; } QJsonObject collectionObject = collectionDocument.object(); QJsonArray categoryArray = collectionObject.value("categories").toArray(); //processing categories for(int i = 0 ; i < categoryArray.size(); ++i) { const QJsonObject& currentCategory = categoryArray[i].toObject(); const QString& categoryName = currentCategory.value("category_name").toString(); const QJsonArray& subcategories = currentCategory.value("subcategories").toArray(); //processing subcategories for(int j = 0; j < subcategories.size(); ++j) { QJsonObject currentSubCategory = subcategories[j].toObject(); QString subcategoryName = currentSubCategory.value("subcategory_name").toString(); const QJsonArray& datasetArray = currentSubCategory.value("datasets").toArray(); //processing the datasets of the actual subcategory for (const auto& dataset : datasetArray) m_datasetsMap[collectionName][categoryName][subcategoryName].push_back(dataset.toObject().value("filename").toString()); } } } } if(m_datasetModel) delete m_datasetModel; m_datasetModel = new DatasetModel(m_datasetsMap); //Fill up collections combo box ui.cbCollections->addItem(i18n("All") + QString(" (" + QString::number(m_datasetModel->allDatasetsList().toStringList().size()) + ")"), QLatin1String("All")); for (QString collection : m_datasetModel->collections()) ui.cbCollections->addItem(collection + " (" + QString::number(m_datasetModel->datasetCount(collection)) + ")", collection); collectionChanged(ui.cbCollections->currentIndex()); } else QMessageBox::critical(this, i18n("File not found"), i18n("Couldn't open the dataset collections file %1. Please check your installation.", collectionsFileName)); } /** * Shows all categories and sub-categories for the currently selected collection */ void ImportDatasetWidget::collectionChanged(int index) { bool allCollections = (index == 0); QString collectionName = ui.cbCollections->itemData(index).toString(); //update the info field QString info; if (!allCollections) { for (int i = 0; i < m_collections.size(); ++i) { const QJsonObject& collection = m_collections[i].toObject(); if ( collectionName == collection["name"].toString() ) { info += collection["description"].toString(); info += "



"; break; } } } ui.lInfo->setText(info); m_loadingCategories = true; ui.lwDatasets->clear(); ui.twCategories->clear(); QStringList categories = allCollections ? m_datasetModel->allCategories().toStringList() : m_datasetModel->categories(collectionName); //Go through every category that was previously processed. for(auto category : categories) { QStringList categoryList(category); categoryList.append(QString::number(m_datasetModel->datasetCount(collectionName, category))); QTreeWidgetItem* const currentCategoryItem = new QTreeWidgetItem(categoryList); ui.twCategories->addTopLevelItem(currentCategoryItem); QStringList subcategories = allCollections ? m_datasetModel->allSubcategories(category).toStringList() : m_datasetModel->subcategories(collectionName, category); //Go through every subcategory of the current category, that was previously processed. for(auto subcategory : subcategories) { QStringList subcategoryList(subcategory); subcategoryList.append(QString::number(m_datasetModel->datasetCount(collectionName, category, subcategory))); currentCategoryItem->addChild(new QTreeWidgetItem(QStringList(subcategoryList))); } } if(m_selectedCollection == collectionName) { restoreSelectedSubcategory(collectionName); } else { m_selectedCollection = collectionName; m_selectedCategory = ""; m_selectedSubcategory = ""; } m_loadingCategories = false; updateCategoryCompleter(); } /** * @brief Restores the lastly selected collection, category and subcategory making it the selected QTreeWidgetItem and also lists the datasets belonigng to it */ void ImportDatasetWidget::restoreSelectedSubcategory(const QString& collectionName) { if(m_datasetModel->categories(collectionName).contains(m_selectedCategory)) { const QTreeWidgetItem* const categoryItem = ui.twCategories->findItems(m_selectedCategory, Qt::MatchExactly).first(); if(m_datasetModel->subcategories(collectionName, m_selectedCategory).contains(m_selectedSubcategory)) { for(int i = 0; i < categoryItem->childCount(); ++i) { if(categoryItem->child(i)->text(0).compare(m_selectedSubcategory) == 0) { QTreeWidgetItem* const subcategoryItem = categoryItem->child(i); ui.twCategories->setCurrentItem(subcategoryItem); subcategoryItem->setSelected(true); m_selectedSubcategory.clear(); listDatasetsForSubcategory(subcategoryItem); break; } } } } } /** * @brief Populates lwDatasets with the datasets of the selected subcategory. * @param item the selected subcategory */ void ImportDatasetWidget::listDatasetsForSubcategory(QTreeWidgetItem* item) { if(item->childCount() == 0) { if(m_selectedSubcategory.compare(item->text(0)) != 0) { m_selectedSubcategory = item->text(0); m_selectedCategory = item->parent()->text(0); QString categoryName = item->parent()->text(0); ui.lwDatasets->clear(); for(QString dataset : m_datasetModel->datasets(m_selectedCollection, categoryName, m_selectedSubcategory)) ui.lwDatasets->addItem(new QListWidgetItem(dataset)); updateDatasetCompleter(); } } else { if(item->text(0).compare(m_selectedCategory) != 0) { m_selectedCategory = item->text(0); m_selectedSubcategory = ""; ui.lwDatasets->clear(); item->setExpanded(true); } } } /** * @brief Updates the completer used for searching among datasets. */ void ImportDatasetWidget::updateDatasetCompleter() { QStringList datasetList; for(int i = 0; i count(); ++i) { datasetList.append(ui.lwDatasets->item(i)->text()); } if(!datasetList.isEmpty()) { if(m_datasetCompleter != nullptr) delete m_datasetCompleter; m_datasetCompleter = new QCompleter(datasetList); m_datasetCompleter->setCompletionMode(QCompleter::PopupCompletion); m_datasetCompleter->setCaseSensitivity(Qt::CaseSensitive); ui.leSearchDatasets->setCompleter(m_datasetCompleter); } else ui.leSearchDatasets->setCompleter(nullptr); } /** * @brief Updates the completer used for searching among categories and subcategories. */ void ImportDatasetWidget::updateCategoryCompleter() { QStringList categoryList; for (int i = 0; i < ui.twCategories->topLevelItemCount(); ++i) { categoryList.append(ui.twCategories->topLevelItem(i)->text(0)); for(int j = 0; j < ui.twCategories->topLevelItem(i)->childCount(); ++j) { QString text = ui.twCategories->topLevelItem(i)->text(0) + QLatin1Char(':') + ui.twCategories->topLevelItem(i)->child(j)->text(0); categoryList.append(text); } } if(!categoryList.isEmpty()) { if(m_categoryCompleter != nullptr) delete m_categoryCompleter; m_categoryCompleter = new QCompleter(categoryList); m_categoryCompleter->setCompletionMode(QCompleter::PopupCompletion); m_categoryCompleter->setCaseSensitivity(Qt::CaseSensitive); ui.leSearchCategories->setCompleter(m_categoryCompleter); } else ui.leSearchCategories->setCompleter(nullptr); } /** * @brief Scrolls the twCategories to the given category or subcategory * @param rootName the name of the category or category+subcategory */ void ImportDatasetWidget::scrollToCategoryTreeItem(const QString& rootName) { int topItemIdx = -1; for (int i = 0; i < ui.twCategories->topLevelItemCount(); ++i) if (rootName.startsWith(ui.twCategories->topLevelItem(i)->text(0))) { topItemIdx = i; break; } if (topItemIdx >= 0) { if (!rootName.contains(QLatin1Char(':'))) { ui.twCategories->scrollToItem(ui.twCategories->topLevelItem(topItemIdx), QAbstractItemView::ScrollHint::PositionAtTop); } else { int childIdx = -1; for (int j = 0; j < ui.twCategories->topLevelItem(topItemIdx)->childCount(); ++j) { if(rootName.endsWith(ui.twCategories->topLevelItem(topItemIdx)->child(j)->text(0))) { childIdx = j; break; } } if (childIdx >= 0) ui.twCategories->scrollToItem(ui.twCategories->topLevelItem(topItemIdx)->child(childIdx), QAbstractItemView::ScrollHint::PositionAtTop); else ui.twCategories->scrollToItem(ui.twCategories->topLevelItem(topItemIdx), QAbstractItemView::ScrollHint::PositionAtTop); } } } /** * @brief Scrolls the lwDatasets to the given dataset name. * @param rootName the name of the dataset */ void ImportDatasetWidget::scrollToDatasetListItem(const QString& rootName) { int itemIdx = -1; for (int i = 0; i < ui.lwDatasets->count(); ++i) if (ui.lwDatasets->item(i)->text() == rootName) { itemIdx = i; break; } if (itemIdx >= 0) ui.lwDatasets->scrollToItem(ui.lwDatasets->item(itemIdx), QAbstractItemView::ScrollHint::PositionAtTop); } /** * @brief Returns the name of the selected dataset */ QString ImportDatasetWidget::getSelectedDataset() const { if (ui.lwDatasets->selectedItems().count() > 0) return ui.lwDatasets->selectedItems().at(0)->text(); else return QString(); } /** * @brief Initiates the processing of the dataset's metadata file and of the dataset itself. * @param datasetHandler the DatasetHanlder that downloads processes the dataset */ -void ImportDatasetWidget::loadDatasetToProcess(DatasetHandler* datasetHandler) { +void ImportDatasetWidget::import(DatasetHandler* datasetHandler) { datasetHandler->processMetadata(m_datasetObject); } /** * @brief Returns the QJsonObject associated with the currently selected dataset. */ QJsonObject ImportDatasetWidget::loadDatasetObject() { bool allCollections = (ui.cbCollections->currentIndex() == 0); for (int i = 0; i < m_collections.size(); ++i) { const QJsonObject& collection = m_collections[i].toObject(); const QString& collectionName = collection["name"].toString(); //we have to find the selected collection in the metadata file. if(allCollections || collectionName == m_selectedCollection) { QFile file(m_jsonDir + QLatin1Char('/') + collectionName + ".json"); //open the metadata file of the current collection if (file.open(QIODevice::ReadOnly)) { QJsonDocument collectionDocument = QJsonDocument::fromJson(file.readAll()); file.close(); if(!collectionDocument.isObject()) { qDebug()<< "The " + collectionName + ".json file is invalid"; return QJsonObject(); } QJsonObject collectionObject = collectionDocument.object(); QJsonArray categoryArray = collectionObject.value("categories").toArray(); //processing categories for(int i = 0 ; i < categoryArray.size(); ++i) { const QJsonObject currentCategory = categoryArray[i].toObject(); const QString categoryName = currentCategory.value("category_name").toString(); if(categoryName.compare(m_selectedCategory) == 0) { const QJsonArray subcategories = currentCategory.value("subcategories").toArray(); //processing subcategories for(int j = 0; j < subcategories.size(); ++j) { QJsonObject currentSubCategory = subcategories[j].toObject(); QString subcategoryName = currentSubCategory.value("subcategory_name").toString(); if(subcategoryName.compare(m_selectedSubcategory) == 0) { const QJsonArray datasetArray = currentSubCategory.value("datasets").toArray(); //processing the datasets o the actual subcategory for (const auto& dataset : datasetArray) { if(getSelectedDataset().compare(dataset.toObject().value("filename").toString()) == 0) return dataset.toObject(); } } } } } } if (!allCollections) break; } } return QJsonObject(); } /** * @brief Opens the DatasetMetadataManagerDialog when the user wants to add a new dataset. */ void ImportDatasetWidget::showDatasetMetadataManager() { DatasetMetadataManagerDialog* dlg = new DatasetMetadataManagerDialog(this, m_datasetsMap); if (dlg->exec() == QDialog::Accepted) { const QString pathToJson = m_jsonDir + QLatin1String("DatasetCategories.json"); const QString dirPath = QFileInfo(pathToJson).dir().absolutePath(); //update the metadata document dlg->updateDocument(m_jsonDir); //Not working due to problems with KNS3 library /*uploadCategoryFile(); uploadDatasetFile(dlg->getMetadataFilePath());*/ //process the changes made in the metadata files loadCategories(); } delete dlg; } /** * @brief Places the metadata file containing the list of collections into a specific directory. */ /* void ImportDatasetWidget::downloadCollectionsFile() { const QString fileNameOld = QStandardPaths::locate(QStandardPaths::AppDataLocation, "datasets/DatasetCollections.json"); const QString fileNameNew =m_jsonDir + QLatin1String("DatasetCollections.json"); const QString parentDir = m_jsonDir.left(m_jsonDir.left(m_jsonDir.length() - 1).lastIndexOf(QDir::separator())); if(!QDir(m_jsonDir).exists()) { qDebug() << parentDir; QDir(parentDir).mkdir(QLatin1String("labplot_data")); } QFile::copy(fileNameOld, fileNameNew); } */ /** * @brief Places the metadata file of the given dataset into a specific directory. * @param datasetName the name of the dataset */ /* void ImportDatasetWidget::downloadCollectionFile(const QString& collectionName) { const QString fileNameOld = QStandardPaths::locate(QStandardPaths::AppDataLocation, QLatin1String("datasets") + QDir::separator() + collectionName); const QString fileNameNew =m_jsonDir + collectionName; QFile::copy(fileNameOld, fileNameNew); } */ /** * @brief Refreshes the categories, subcategories and datasets. */ /* void ImportDatasetWidget::refreshCategories() { QMessageBox::StandardButton reply; reply = QMessageBox::question(this, "Refresh metadata files", "Are you sure to refresh all of the metadata files? (every change will be removed, but a backup will be created)", QMessageBox::Yes|QMessageBox::No); if(reply == QMessageBox::Yes) { QString fileNameNew = m_jsonDir + QLatin1String("DatasetCollections.json"); QFile existingCategoriesFile(fileNameNew); if(existingCategoriesFile.exists()) { //Delete old backup QFile oldBackup(m_jsonDir + QLatin1String("DatasetCollections_backup.json")); if(oldBackup.exists()) { oldBackup.remove(); } oldBackup.close(); //Create new backup if(!existingCategoriesFile.rename(m_jsonDir + QLatin1String("DatasetCollections_backup.json"))) qDebug() << " Couldn't create backup because " << existingCategoriesFile.errorString(); } //Obtain the new file downloadCollectionsFile(); QString filePath = m_jsonDir + "DatasetCollections.json"; QFile file(filePath); if (file.open(QIODevice::ReadOnly)) { m_datasetsMap.clear(); QJsonDocument document = QJsonDocument::fromJson(file.readAll()); QJsonArray collections; if(document.isArray()) collections = document.array(); else { qDebug()<< "The DatasetCollections.json file is invalid"; return; } //Go trough every collection's metadata file for (int collectionIndex = 0; collectionIndex < collections.size(); collectionIndex++) { const QString currentCollection = collections[collectionIndex].toString(); QFile existingCollectionFile(m_jsonDir + currentCollection + ".json"); //we copy the file to the data location if it doesn't exist if(!existingCollectionFile.exists()) { downloadCollectionFile(currentCollection + ".json"); } //otherwise we have to create a backup first else { QFile oldBackupCollection(m_jsonDir + currentCollection + "_backup.json"); if(oldBackupCollection.exists()) { oldBackupCollection.remove(); } oldBackupCollection.close(); if(!existingCollectionFile.rename(m_jsonDir + currentCollection + "_backup.json")) qDebug() << " Couldn't create backup because " << existingCollectionFile.errorString(); } } } //process the "refreshed" files and update the widget accordingly loadCategories(); } } */ /** * @brief Restores the saved metadata files. Revokes the effect of refreshCategories(). */ /* void ImportDatasetWidget::restoreBackup() { QMessageBox::StandardButton reply; reply = QMessageBox::question(this, "Restore backup", "Are you sure to restore the backup metadata files?", QMessageBox::Yes|QMessageBox::No); if(reply == QMessageBox::Yes) { //Restore the collection list first QFile backup(m_jsonDir + QLatin1String("DatasetCollections_backup.json")); if(backup.exists()) { QFile deleteFile(m_jsonDir + QLatin1String("DatasetCollections.json")); deleteFile.remove(); if(!backup.rename(m_jsonDir + QLatin1String("DatasetCollections.json"))) { qDebug() << " Couldn't create backup because " << backup.errorString(); downloadCollectionsFile(); } } QString filePath = m_jsonDir + "DatasetCollections.json"; QFile file(filePath); if (file.open(QIODevice::ReadOnly)) { m_datasetsMap.clear(); QJsonDocument document = QJsonDocument::fromJson(file.readAll()); QJsonArray collections; if(document.isArray()) collections = document.array(); else { qDebug()<< "The DatasetCollections.json file is invalid"; return; } //Restore every collection's metadata file for (int collectionIndex = 0; collectionIndex < collections.size(); collectionIndex++) { const QString currentCollection = collections[collectionIndex].toString(); QFile backupCollection(m_jsonDir + currentCollection + "_backup.json"); if(backupCollection.exists()) { QFile collectionFile(m_jsonDir + currentCollection + ".json"); collectionFile.remove(); if(!backupCollection.rename(m_jsonDir + currentCollection + ".json")) { qDebug() << " Couldn't create backup because " << backupCollection.errorString(); downloadCollectionFile(currentCollection + ".json"); } } } } //process the restored files and update the widget accordingly loadCategories(); } } */ /** * @brief Clears the content of the directory in which the download of metadata files was done. */ /* void ImportDatasetWidget::clearCache() { QMessageBox::StandardButton reply = QMessageBox::question(this, "Clear cache", "Are you sure to remove every downloaded dataset?", QMessageBox::Yes|QMessageBox::No); if(reply == QMessageBox::Yes) { QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QLatin1String("/datasets_local/")); if(dir.exists()) { for(const auto& entry : dir.entryList()) { //delete every file that isn't potentially a metadata file if(!(entry.endsWith(QLatin1String(".json")) || entry.startsWith(QLatin1Char('.')))) { QFile deleteFile (dir.path() + QLatin1Char('/') + entry); if(deleteFile.exists()) { deleteFile.remove(); } } } } else { qDebug("Couldn't clear cache, containing folder doesn't exist!"); } } } */ /** * @brief TODO: uploads the metadata file that contains the categories to store.kde.org -- Library doesn't work for indefinite time. */ /* void ImportDatasetWidget::uploadCategoryFile() { KNS3::UploadDialog dialog("labplot2_datasets.knsrc", this); QFile file(m_jsonDir + "DatasetCategories.json"); qDebug() << "file " << m_jsonDir + "DatasetCategories.json "<< file.exists(); qDebug() << "file can be opened: " << file.open(QIODevice::ReadOnly) << " " << file.errorString(); file.close(); QUrl payloadFile ="file:" + m_jsonDir + "DatasetCategories.json"; QFile file2(payloadFile.toLocalFile()); qDebug() << "Local file: " << payloadFile.toLocalFile(); if (!file2.open(QIODevice::ReadOnly)) { qDebug() << i18n("File not found: %1 ", payloadFile.url()); } else { qDebug() << i18n("File found: %1 ", payloadFile.url()); } file2.close(); dialog.setUploadFile("file:" + m_jsonDir + "DatasetCategories.json"); qDebug("Upload file set!"); dialog.setUploadName("Dataset Categories"); qDebug() << "Upload name set: "; dialog.exec(); } */ /** * @brief TODO: uploads the metadata file of a dataset to store.kde.org -- Library doesn't work for indefinite time. */ /* void ImportDatasetWidget::uploadDatasetFile(const QString& filePath) { KNS3::UploadDialog dialog("labplot2_datasets.knsrc", this); QFile file(filePath); qDebug() << filePath + " " << file.exists(); qDebug() << "file can be opened: " << file.open(QIODevice::ReadOnly) << " " << file.errorString(); file.close(); QUrl payloadFile ="file:" + filePath; QFile file2(payloadFile.toLocalFile()); qDebug() << "Local file: " << payloadFile.toLocalFile(); if (!file2.open(QIODevice::ReadOnly)) { qDebug() << i18n("File not found: %1 ", payloadFile.url()); } else { qDebug() << i18n("File found: %1 ", payloadFile.url()); } file2.close(); dialog.setUploadFile("file:" + filePath); qDebug("Upload file set!"); dialog.setUploadName("Dataset Categories"); qDebug() << "Upload name set: "; dialog.exec(); } */ /** * @brief Returns the structure containing the categories, subcategories and datasets. * @return the structure containing the categories, subcategories and datasets */ const DatasetsMap& ImportDatasetWidget::getDatasetsMap() { return m_datasetsMap; } /** * @brief Sets the currently selected collection * @param category the name of the collection */ void ImportDatasetWidget::setCollection(const QString& collection) { ui.cbCollections->setCurrentText(collection + " (" + QString(m_datasetModel->datasetCount(collection)) + ")"); } /** * @brief Sets the currently selected category * @param category the name of the category */ void ImportDatasetWidget::setCategory(const QString &category) { for(int i = 0; i < ui.twCategories->topLevelItemCount(); i++) { if (ui.twCategories->topLevelItem(i)->text(0).compare(category) == 0) { listDatasetsForSubcategory(ui.twCategories->topLevelItem(i)); break; } } } /** * @brief Sets the currently selected subcategory * @param subcategory the name of the subcategory */ void ImportDatasetWidget::setSubcategory(const QString &subcategory) { for(int i = 0; i < ui.twCategories->topLevelItemCount(); i++) { if (ui.twCategories->topLevelItem(i)->text(0).compare(m_selectedCategory) == 0) { QTreeWidgetItem* categoryItem = ui.twCategories->topLevelItem(i); for(int j = 0; j childCount(); j++) { if(categoryItem->child(j)->text(0).compare(subcategory) == 0) { listDatasetsForSubcategory(categoryItem->child(j)); break; } } break; } } } /** * @brief Sets the currently selected dataset * @param the currently selected dataset */ void ImportDatasetWidget::setDataset(const QString &datasetName) { for(int i = 0; i < ui.lwDatasets->count() ; i++) { if(ui.lwDatasets->item(i)->text().compare(datasetName) == 0) { ui.lwDatasets->item(i)->setSelected(true); break; } } } /** * @brief Updates the details of the currently selected dataset */ void ImportDatasetWidget::datasetChanged() { QString info; if (ui.cbCollections->currentIndex() != 0) { const QString& collectionName = ui.cbCollections->itemData(ui.cbCollections->currentIndex()).toString(); for (int i = 0; i < m_collections.size(); ++i) { const QJsonObject& collection = m_collections[i].toObject(); if ( collectionName.startsWith(collection["name"].toString()) ) { info += collection["description"].toString(); info += "

"; break; } } } if(!getSelectedDataset().isEmpty()) { m_datasetObject = loadDatasetObject(); info += "" + i18n("Dataset") + ":
"; info += m_datasetObject["name"].toString(); info += "

"; info += "" + i18n("Description") + ":
"; if (m_datasetObject.contains("description_url")) { WAIT_CURSOR; - QNetworkAccessManager* manager = new QNetworkAccessManager(this); - if (manager->networkAccessible() == QNetworkAccessManager::Accessible) { - connect(manager, &QNetworkAccessManager::finished, [this] (QNetworkReply* reply) { - RESET_CURSOR; - if (reply->error() == QNetworkReply::NoError) { - QByteArray ba = reply->readAll(); - QString info(ba); - info = info.replace(QLatin1Char('\n'), QLatin1String("
")); - ui.lInfo->setText(ui.lInfo->text() + info); - } else { - DEBUG("Failed to fetch the description."); - ui.lInfo->setText(ui.lInfo->text() + m_datasetObject["description"].toString()); - } - } - ); - manager->get(QNetworkRequest(QUrl(m_datasetObject["description_url"].toString()))); - } else + if (m_networkManager->networkAccessible() == QNetworkAccessManager::Accessible) + m_networkManager->get(QNetworkRequest(QUrl(m_datasetObject["description_url"].toString()))); + else info += m_datasetObject["description"].toString(); } else info += m_datasetObject["description"].toString(); } else m_datasetObject = QJsonObject(); ui.lInfo->setText(info); emit datasetSelected(); } + +void ImportDatasetWidget::downloadFinished(QNetworkReply* reply) { + if (reply->error() == QNetworkReply::NoError) { + QByteArray ba = reply->readAll(); + QString info(ba); + info = info.replace(QLatin1Char('\n'), QLatin1String("
")); + ui.lInfo->setText(ui.lInfo->text() + info); + } else { + DEBUG("Failed to fetch the description."); + ui.lInfo->setText(ui.lInfo->text() + m_datasetObject["description"].toString()); + } + reply->deleteLater(); + RESET_CURSOR; +} diff --git a/src/kdefrontend/datasources/ImportDatasetWidget.h b/src/kdefrontend/datasources/ImportDatasetWidget.h index fafd7884e..983a413df 100644 --- a/src/kdefrontend/datasources/ImportDatasetWidget.h +++ b/src/kdefrontend/datasources/ImportDatasetWidget.h @@ -1,103 +1,107 @@ /*************************************************************************** File : ImportDatasetWidget.h Project : LabPlot Description : import online dataset widget -------------------------------------------------------------------- Copyright : (C) 2019 Kovacs Ferencz (kferike98@gmail.com) Copyright : (C) 2019 by Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #ifndef IMPORTDATASETWIDGET_H #define IMPORTDATASETWIDGET_H #include "ui_importdatasetwidget.h" #include "QWidget" #include "QMap" #include #include -class QCompleter; class DatasetHandler; class DatasetModel; +class QCompleter; +class QNetworkAccessManager; +class QNetworkReply; typedef QMap< QString, QMap>>> DatasetsMap; class ImportDatasetWidget : public QWidget { Q_OBJECT public: explicit ImportDatasetWidget(QWidget* parent); ~ImportDatasetWidget() override; QString getSelectedDataset() const; - void loadDatasetToProcess(DatasetHandler*); + void import(DatasetHandler*); const DatasetsMap& getDatasetsMap(); void setCollection(const QString&); void setCategory(const QString&); void setSubcategory(const QString&); void setDataset(const QString&); void processTest(const QString& category, const QString& subcategory, const QString& dataset, DatasetHandler*); private: Ui::ImportDatasetWidget ui; DatasetsMap m_datasetsMap; QString m_selectedSubcategory; QCompleter* m_categoryCompleter; QCompleter* m_datasetCompleter; QString m_jsonDir; bool m_loadingCategories{false}; QString m_selectedCategory; QString m_selectedCollection; DatasetModel* m_datasetModel{nullptr}; + QNetworkAccessManager* m_networkManager; QJsonArray m_collections; QJsonObject m_datasetObject; // void downloadCollectionsFile(); // void downloadCollectionFile(const QString&); // void uploadCategoryFile(); // void uploadDatasetFile(const QString&); void updateDatasetCompleter(); void updateCategoryCompleter(); void loadCategories(); void listDatasetsForSubcategory(QTreeWidgetItem*); void restoreSelectedSubcategory(const QString&); // void highlightLocalMetadataFiles(); QJsonObject loadDatasetObject(); private slots: void datasetChanged(); void collectionChanged(int); void scrollToCategoryTreeItem(const QString&); void scrollToDatasetListItem(const QString&); void showDatasetMetadataManager(); + void downloadFinished(QNetworkReply*); // void refreshCategories(); // void clearCache(); // void restoreBackup(); signals: void datasetSelected(); void datasetDoubleClicked(); }; #endif // IMPORTDATASETWIDGET_H