diff --git a/src/kdefrontend/MainWin.cpp b/src/kdefrontend/MainWin.cpp index 4fd7d85c9..b89e35e26 100644 --- a/src/kdefrontend/MainWin.cpp +++ b/src/kdefrontend/MainWin.cpp @@ -1,2284 +1,2285 @@ /*************************************************************************** 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" #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 "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 #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() { Worksheet* w = activeWorksheet(); 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() ); } } } //show memory info const bool showMemoryInfo = group.readEntry(QLatin1String("ShowMemoryInfo"), true); if (showMemoryInfo) { m_memoryInfoWidget = new MemoryWidget(statusBar()); statusBar()->addPermanentWidget(m_memoryInfoWidget); } updateGUIOnProjectChanges(); //load welcome screen m_showWelcomeScreen = group.readEntry(QLatin1String("ShowWelcomeScreen"), true); if(m_showWelcomeScreen) { m_welcomeWidget = createWelcomeScreen(); setCentralWidget(m_welcomeWidget); } } /** * @brief Creates a new welcome screen to be set as central widget. * @return */ QQuickWidget* MainWin::createWelcomeScreen() { //showMaximized(); //QSize maxSize = qApp->primaryScreen()->availableSize(); - setFixedSize(qApp->screens().first()->availableGeometry().size()); + /*setFixedSize(qApp->screens().first()->availableGeometry().size()); setGeometry( QStyle::alignedRect( Qt::LeftToRight, Qt::AlignCenter, size(), qApp->desktop()->availableGeometry() ) - ); + );*/ + setMinimumSize(700, 400); qDebug() << "Main win size " << size(); 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)); //qmlRegisterUncreatableType("labplot.datasetmodel", 1, 0, "DatasetModel", ""); QQuickWidget* quickWidget = new QQuickWidget(this); QUrl source("qrc:///main.qml"); //qmlRegisterType("labplot.frontend.datasetmodel", 1, 0, "DatasetModel"); QQmlContext *ctxt = quickWidget->rootContext(); QVariant variant(recentList); ctxt->setContextProperty("recentProjects", variant); //m_importDatasetWidget = new ImportDatasetWidget(nullptr); //m_importDatasetWidget->hide(); 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); qDebug() << "Categories: " << m_welcomeScreenHelper->getDatasetModel()->allCategories(); quickWidget->setSource(source); quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); QObject *item = quickWidget->rootObject(); qDebug() << "Start connecting welcome screen"; 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&))); qDebug() << "Finished connecting welcome screen"; m_welcomeScreenHelper->showFirstDataset(); return quickWidget; } 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() { //layout()->setSizeConstraint(QLayout::SetDefaultConstraint); 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"; } 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()); KStandardAction::fullScreen(this, SLOT(toggleFullScreen()), this, actionCollection()); //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); m_newDatasetAction = new QAction(QIcon::fromTheme("application-octet-stream"), i18n("From Dataset Collection"), this); m_newDatasetAction->setWhatsThis(i18n("Imports data from an online dataset")); actionCollection()->addAction("import_dataset_datasource", m_newDatasetAction); connect(m_newDatasetAction, &QAction::triggered, this, &MainWin::newDatasetActionTriggered); //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("document-import-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_importLabPlotAction = new QAction(QIcon::fromTheme("document-import"), 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("document-import-database"), 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 explorr 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_newMenu->addAction(m_newDatasetAction); 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 menue 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 Worksheet* w = this->activeWorksheet(); if (w != nullptr) { //enable worksheet related menus factory->container("worksheet", this)->setEnabled(true); factory->container("analysis", this)->setEnabled(true); //TODO factory->container("drawing", this)->setEnabled(true); //disable spreadsheet and matrix related menus factory->container("spreadsheet", this)->setEnabled(false); factory->container("matrix", this)->setEnabled(false); //populate worksheet menu auto* view = qobject_cast(w->view()); auto* menu = qobject_cast(factory->container("worksheet", this)); menu->clear(); view->createContextMenu(menu); //populate analysis menu menu = qobject_cast(factory->container("analysis", this)); menu->clear(); view->createAnalysisMenu(menu); //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("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) { //enable spreadsheet related menus factory->container("spreadsheet", this)->setEnabled(true); //populate spreadsheet-menu auto* view = qobject_cast(spreadsheet->view()); auto* menu = qobject_cast(factory->container("spreadsheet", this)); menu->clear(); view->createContextMenu(menu); //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)->setEnabled(false); } //Handle the Matrix-object const Matrix* matrix = this->activeMatrix(); if (matrix) { factory->container("matrix", this)->setEnabled(true); //populate matrix-menu auto* view = qobject_cast(matrix->view()); auto* menu = qobject_cast(factory->container("matrix", this)); menu->clear(); view->createContextMenu(menu); } else factory->container("matrix", this)->setEnabled(false); #ifdef HAVE_CANTOR_LIBS CantorWorksheet* cantorworksheet = this->activeCantorWorksheet(); if (cantorworksheet) { // enable Cantor Worksheet related menus factory->container("cas_worksheet", this)->setEnabled(true); auto* view = qobject_cast(cantorworksheet->view()); auto* menu = qobject_cast(factory->container("cas_worksheet", this)); menu->clear(); view->createContextMenu(menu); 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 = this->activeDatapicker(); if (datapicker) { factory->container("datapicker", this)->setEnabled(true); //populate datapicker-menu auto* view = qobject_cast(datapicker->view()); auto* menu = qobject_cast(factory->container("datapicker", this)); menu->clear(); view->createContextMenu(menu); //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)) 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->setFileName(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; 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(); } 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) { WAIT_CURSOR; // 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(fileName, KCompressionDevice::GZip); else file = new KFilterDev(fileName); if (file == nullptr) file = new QFile(fileName); bool ok; if (file->open(QIODevice::WriteOnly)) { m_project->setFileName(fileName); QPixmap thumbnail = centralWidget()->grab(); QXmlStreamWriter writer(file); m_project->save(thumbnail, &writer); m_project->undoStack()->clear(); m_project->setChanged(false); file->close(); 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 { KMessageBox::error(this, i18n("Sorry. Could not open file for writing.")); 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()->showMessage(QString()); } 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()->showMessage(QString()); } /**************************************************************************************/ /*! 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 = activeWorkbook(); 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 = activeWorkbook(); 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 Workbook-object, if the currently active Mdi-Subwindow is \a WorkbookView. Otherwise returns \a 0. */ Workbook* MainWin::activeWorkbook() const { if(dynamic_cast(centralWidget()) != nullptr) { return nullptr; } QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return nullptr; AbstractPart* part = dynamic_cast(win)->part(); Q_ASSERT(part); return dynamic_cast(part); } /*! returns a pointer to a Datapicker-object, if the currently active Mdi-Subwindow is \a DatapickerView. Otherwise returns \a 0. */ Datapicker* MainWin::activeDatapicker() const { if(dynamic_cast(centralWidget()) != nullptr) { return nullptr; } QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return nullptr; AbstractPart* part = dynamic_cast(win)->part(); Q_ASSERT(part); return dynamic_cast(part); } /*! 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; } QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return nullptr; AbstractPart* part = dynamic_cast(win)->part(); Q_ASSERT(part); Spreadsheet* spreadsheet = nullptr; const auto* workbook = dynamic_cast(part); if (workbook) { spreadsheet = workbook->currentSpreadsheet(); if (!spreadsheet) { //potentially, the spreadsheet was not selected in workbook yet since the selection in project explorer //arrives in workbook's slot later than in this function //->check whether we have a spreadsheet or one of its columns currently selected in the project explorer spreadsheet = dynamic_cast(m_currentAspect); if (!spreadsheet) { if (m_currentAspect->parentAspect()) spreadsheet = dynamic_cast(m_currentAspect->parentAspect()); } } } else spreadsheet = dynamic_cast(part); return spreadsheet; } /*! returns a pointer to a \c Matrix object, if the currently active Mdi-Subwindow or if the currently selected tab in a \c WorkbookView is a \c MatrixView Otherwise returns \c 0. */ Matrix* MainWin::activeMatrix() const { if(dynamic_cast(centralWidget()) != nullptr) { return nullptr; } QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return nullptr; AbstractPart* part = dynamic_cast(win)->part(); Q_ASSERT(part); Matrix* matrix = nullptr; const auto* workbook = dynamic_cast(part); if (workbook) { matrix = workbook->currentMatrix(); if (!matrix) { //potentially, the matrix was not selected in workbook yet since the selection in project explorer //arrives in workbook's slot later than in this function //->check whether we have a matrix currently selected in the project explorer matrix = dynamic_cast(m_currentAspect); } } else matrix = dynamic_cast(part); return matrix; } /*! returns a pointer to a Worksheet-object, if the currently active Mdi-Subwindow is \a WorksheetView Otherwise returns \a 0. */ Worksheet* MainWin::activeWorksheet() const { QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return nullptr; if(dynamic_cast(centralWidget()) != nullptr) { return nullptr; } AbstractPart* part = dynamic_cast(win)->part(); Q_ASSERT(part); return dynamic_cast(part); } #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); } /********************************************************************************/ /*! returns a pointer to a CantorWorksheet-object, if the currently active Mdi-Subwindow is \a CantorWorksheetView Otherwise returns \a 0. */ CantorWorksheet* MainWin::activeCantorWorksheet() const { QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return nullptr; AbstractPart* part = dynamic_cast(win)->part(); Q_ASSERT(part); return dynamic_cast(part); } #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); } } 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::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->inherits(AspectType::Spreadsheet) || m_currentAspect->inherits(AspectType::Matrix) || m_currentAspect->inherits(AspectType::Workbook)) dlg->setCurrentIndex(m_projectExplorer->currentIndex()); else if (m_currentAspect->inherits(AspectType::Column) && m_currentAspect->parentAspect()->inherits(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->inherits(AspectType::Spreadsheet) || m_currentAspect->inherits(AspectType::Matrix) || m_currentAspect->inherits(AspectType::Workbook)) dlg->setCurrentIndex(m_projectExplorer->currentIndex()); else if (m_currentAspect->inherits(AspectType::Column) && m_currentAspect->parentAspect()->inherits(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"); } /*! 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/WelcomeScreenHelper.cpp b/src/kdefrontend/WelcomeScreenHelper.cpp index 9e84a22ff..537e4e72b 100644 --- a/src/kdefrontend/WelcomeScreenHelper.cpp +++ b/src/kdefrontend/WelcomeScreenHelper.cpp @@ -1,426 +1,447 @@ /*************************************************************************** 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; + qDebug() << "Destructor save width " << m_widthScale; + for(auto item = m_widthScale.begin(); item != m_widthScale.end() && currentWidthIndex < widthCount; item++) { + qDebug() << "Save width " << item.key() << item.value(); conf.writeEntry("widthName_" + QString::number(currentWidthIndex), item.key()); - conf.writeEntry("widthValue_" + QString::number(currentWidthIndex), item.value()); + conf.writeEntry("widthValue_" + QString::number(currentWidthIndex), QString::number(item.value())); currentWidthIndex++; } int heightCount = m_heightScale.size(); conf.writeEntry("height_count", widthCount); int currentHeightIndex = 0; + qDebug() << "Destructor save height " << m_widthScale; + for(auto item = m_heightScale.begin(); item != m_heightScale.end() && currentHeightIndex < heightCount; item++) { + qDebug() << "Save height " << item.key() << item.value(); conf.writeEntry("heightName_" + QString::number(currentHeightIndex), item.key()); - conf.writeEntry("heightValue_" + QString::number(currentHeightIndex), item.value()); + conf.writeEntry("heightValue_" + QString::number(currentHeightIndex), QString::number(item.value())); currentHeightIndex++; } } 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 = conf.readEntry("widthValue_" + QString::number(i), -1); + double value = QString(conf.readEntry("widthValue_" + QString::number(i), "-1")).toDouble(); + + qDebug() << "Welcome screen helper load width: " << id << " " << value; 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 = conf.readEntry("heightValue_" + QString::number(i), -1); + double value = QString(conf.readEntry("heightValue_" + QString::number(i), "-1")).toDouble(); + + + qDebug() << "Welcome screen helper load height: " << id << " " << value; 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->clear() //if(m_spreadsheet.get() != nullptr) //delete m_spreadsheet.get(); 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); 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(); qDebug() << "Get thumbnail for: " << filename; 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,"); //qDebug() << "Return thumbnail " < #include #include #include #include #include #include /*! \class DatasetMetadataManagerDialog \brief Dialog for adding a new dataset to LabPlot's current collection. Embeds \c DatasetMetadataManagerWidget and provides the standard buttons. \ingroup kdefrontend */ DatasetMetadataManagerDialog::DatasetMetadataManagerDialog(QWidget* parent, const QMap< QString, QMap>>>& datasetMap) : QDialog(parent), m_mainWidget(new DatasetMetadataManagerWidget(this, datasetMap)), m_buttonBox(nullptr), m_okButton(nullptr) { connect(m_mainWidget, &DatasetMetadataManagerWidget::checkOk, this, &DatasetMetadataManagerDialog::checkOkButton); setWindowTitle(i18nc("@title:window", "Dataset metadata manager")); m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); m_okButton = m_buttonBox->button(QDialogButtonBox::Ok); m_okButton->setEnabled(false); QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(m_mainWidget); layout->addWidget(m_buttonBox); connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); //restore saved settings if available create(); // ensure there's a window created KConfigGroup conf(KSharedConfig::openConfig(), "DatasetMetadataManagerDialog"); if (conf.exists()) { KWindowConfig::restoreWindowSize(windowHandle(), conf); resize(windowHandle()->size()); // workaround for QTBUG-40584 } else resize(QSize(0, 0).expandedTo(minimumSize())); checkOkButton(); } DatasetMetadataManagerDialog::~DatasetMetadataManagerDialog() { //save current settings KConfigGroup conf(KSharedConfig::openConfig(), "DatasetMetadataManagerDialog"); KWindowConfig::saveWindowSize(windowHandle(), conf); } /** * @brief Checks whether the OK button of the dialog can be pressed or not */ void DatasetMetadataManagerDialog::checkOkButton() { bool enable = m_mainWidget->checkDataValidity(); m_okButton->setEnabled(enable); } /** * @brief Triggers updating the metadata file containing the categories, subcategories and datasets. * @param fileName the name of the metadata file (path) */ void DatasetMetadataManagerDialog::updateDocument(const QString& fileName) { m_mainWidget->updateDocument(fileName); } -/** - * @brief initiates creating a new metadata file for the new dataset. - * @param dirPath the path of the new file - */ -void DatasetMetadataManagerDialog::createNewMetadata(const QString& dirPath) { - m_mainWidget->createNewMetadata(dirPath); -} - /** * @brief returns the path to the new metadata file of the new dataset. */ QString DatasetMetadataManagerDialog::getMetadataFilePath() const { return m_mainWidget->getMetadataFilePath(); } diff --git a/src/kdefrontend/datasources/DatasetMetadataManagerDialog.h b/src/kdefrontend/datasources/DatasetMetadataManagerDialog.h index d33198647..6764d47f2 100644 --- a/src/kdefrontend/datasources/DatasetMetadataManagerDialog.h +++ b/src/kdefrontend/datasources/DatasetMetadataManagerDialog.h @@ -1,56 +1,55 @@ /*************************************************************************** File : DatasetMetadataManagerDialog.h Project : LabPlot Description : Dialog for managing a metadata file of a dataset -------------------------------------------------------------------- Copyright : (C) 2019 Ferencz Kovacs (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 DATASETMETADATAMANAGERDIALOG_H #define DATASETMETADATAMANAGERDIALOG_H #include class QDialogButtonBox; class DatasetMetadataManagerWidget; class DatasetMetadataManagerDialog : public QDialog { Q_OBJECT public: explicit DatasetMetadataManagerDialog(QWidget*, const QMap< QString, QMap>>>&); virtual ~DatasetMetadataManagerDialog() override; - void updateDocument(const QString& fileName); - void createNewMetadata(const QString& dirPath); + void updateDocument(const QString& fileName); QString getMetadataFilePath() const; private: DatasetMetadataManagerWidget* m_mainWidget; QDialogButtonBox* m_buttonBox; QPushButton* m_okButton; protected slots: void checkOkButton(); }; #endif // DATASETMETADATAMANAGERDIALOG_H diff --git a/src/kdefrontend/datasources/DatasetMetadataManagerWidget.cpp b/src/kdefrontend/datasources/DatasetMetadataManagerWidget.cpp index b8c827b9a..4167ccdaf 100644 --- a/src/kdefrontend/datasources/DatasetMetadataManagerWidget.cpp +++ b/src/kdefrontend/datasources/DatasetMetadataManagerWidget.cpp @@ -1,616 +1,561 @@ /*************************************************************************** File : DatasetMetadataManagerWidget.cpp Project : LabPlot Description : widget for managing a metadata file of a dataset -------------------------------------------------------------------- Copyright : (C) 2019 Ferencz Kovacs (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 "backend/datasources/filters/AsciiFilter.h" #include "src/kdefrontend/DatasetModel.h" #include "src/kdefrontend/datasources/DatasetMetadataManagerWidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*! \class DatasetMetadataManagerWidget \brief Widget for adding a new dataset to LabPlot's current collection. \ingroup kdefrontend */ DatasetMetadataManagerWidget::DatasetMetadataManagerWidget(QWidget* parent, const QMap< QString, QMap>>>& datasetMap) : QWidget(parent) { ui.setupUi(this); m_datasetModel = new DatasetModel(datasetMap); m_baseColor = (palette().color(QPalette::Base).lightness() < 128) ? QLatin1String("#5f5f5f") : QLatin1String("#ffffff"); m_textColor = (palette().color(QPalette::Base).lightness() < 128) ? QLatin1String("#ffffff") : QLatin1String("#000000"); ui.cbCollection->addItems(m_datasetModel->collections()); ui.cbCategory->addItems(m_datasetModel->categories(ui.cbCollection->currentText())); ui.cbSubcategory->addItems(m_datasetModel->subcategories(ui.cbCollection->currentText(), ui.cbCategory->currentText())); ui.cbSeparatingCharacter->addItems(AsciiFilter::separatorCharacters()); ui.cbCommentCharacter->addItems(AsciiFilter::commentCharacters()); ui.cbNumberFormat->addItems(AbstractFileFilter::numberFormats()); ui.cbDateTimeFormat->addItems(AbstractColumn::dateTimeFormats()); connect(ui.leDatasetName, &QLineEdit::textChanged, [this] { emit checkOk(); }); connect(ui.leDownloadURL, &QLineEdit::textChanged, [this] { emit checkOk(); }); connect(ui.teDescription, &QTextEdit::textChanged, [this] { emit checkOk(); }); connect(ui.leFileName, &QLineEdit::textChanged, [this] { emit checkOk(); }); connect(ui.cbSubcategory, &QComboBox::currentTextChanged, [this] { emit checkOk(); }); connect(ui.cbCollection, &QComboBox::currentTextChanged, this, &DatasetMetadataManagerWidget::updateCategories); connect(ui.cbCategory, &QComboBox::currentTextChanged, this, &DatasetMetadataManagerWidget::updateSubcategories); connect(ui.bNewColumn, &QPushButton::clicked, this, &DatasetMetadataManagerWidget::addColumnDescription); connect(ui.bDelete, &QPushButton::clicked, this, &DatasetMetadataManagerWidget::removeColumnDescription); loadSettings(); } DatasetMetadataManagerWidget::~DatasetMetadataManagerWidget() { KConfigGroup conf(KSharedConfig::openConfig(), "DatasetMetadataManagerWidget"); //filter settings conf.writeEntry("Separator", ui.cbSeparatingCharacter->currentText()); conf.writeEntry("CommentChar", ui.cbCommentCharacter->currentText()); conf.writeEntry("NumberFormat", ui.cbNumberFormat->currentIndex()); conf.writeEntry("DateTimeFormat", ui.cbDateTimeFormat->currentText()); conf.writeEntry("create_index_column", ui.chbCreateIndex->isChecked()); conf.writeEntry("skip_empty_parts", ui.chbSkipEmptyParts->isChecked()); conf.writeEntry("simplify_whitespaces", ui.chbSimplifyWhitespaces->isChecked()); conf.writeEntry("remove_quotes", ui.chbRemoveQuotes->isChecked()); conf.writeEntry("use_first_row_for_vectorname", ui.chbHeader->isChecked()); } /** * @brief Loads the settings of the widget. */ void DatasetMetadataManagerWidget::loadSettings() { KConfigGroup conf(KSharedConfig::openConfig(), "DatasetMetadataManagerWidget"); ui.cbCommentCharacter->setCurrentItem(conf.readEntry("CommentChar", "#")); ui.cbSeparatingCharacter->setCurrentItem(conf.readEntry("Separator", "auto")); ui.cbNumberFormat->setCurrentIndex(conf.readEntry("NumberFormat", (int)QLocale::AnyLanguage)); ui.cbDateTimeFormat->setCurrentItem(conf.readEntry("DateTimeFormat", "yyyy-MM-dd hh:mm:ss.zzz")); ui.chbCreateIndex->setChecked(conf.readEntry("create_index_column", false)); ui.chbSimplifyWhitespaces->setChecked(conf.readEntry("simplify_whitespaces", true)); ui.chbRemoveQuotes->setChecked(conf.readEntry("remove_quotes", false)); ui.chbSkipEmptyParts->setChecked(conf.readEntry("skip_empty_parts", false)); ui.chbHeader->setChecked(conf.readEntry("use_first_row_for_vectorname", true)); } /** * @brief Checks whether leFileName contains a valid file name. */ bool DatasetMetadataManagerWidget::checkFileName() { const QString fileName = ui.leFileName->text(); const QRegularExpression re("^[\\w\\d-]+$"); const QRegularExpressionMatch match = re.match(fileName); //check whether it contains only digits, letters, -, _ or not bool hasMatch = match.hasMatch(); qDebug() << hasMatch; if(!hasMatch || fileName.isEmpty()) { qDebug("File name invalid"); QPalette palette; palette.setColor(QPalette::Base, Qt::red); palette.setColor(QPalette::Text, Qt::black); ui.leFileName->setPalette(palette); ui.leFileName->setToolTip("Invalid name for a file (it can contain:digits, letters, -, _)"); } else { qDebug("File name valid"); QPalette palette; palette.setColor(QPalette::Base, m_baseColor); palette.setColor(QPalette::Text, m_textColor); ui.leFileName->setPalette(palette); ui.leFileName->setToolTip(""); } //check whether there already is a file named like this or not. bool found = false; if(m_datasetModel->allDatasetsList().toStringList().contains(fileName)) { qDebug("There already is a metadata file with this name"); QPalette palette; palette.setColor(QPalette::Base, Qt::red); palette.setColor(QPalette::Text, Qt::black); ui.leFileName->setPalette(palette); ui.leFileName->setToolTip("There already is a dataset metadata file with this name!"); found = true; } else { qDebug("Dataset metadata file name is unique"); if(hasMatch) { QPalette palette; palette.setColor(QPalette::Base, m_baseColor); palette.setColor(QPalette::Text, m_textColor); ui.leFileName->setPalette(palette); ui.leFileName->setToolTip(""); } } return hasMatch && !found; } /** * @brief Checks whether leDownloadURL contains a valid URL. */ bool DatasetMetadataManagerWidget::urlExists() { const QRegularExpression re("^(?:http(s)?:\\/\\/)?[\\w.-]+(?:\\.[\\w\\.-]+)+[\\w\\-\\._~:/?#[\\]@!\\$&'\\(\\)\\*\\+,;=.]+$"); const QRegularExpressionMatch match = re.match(ui.leDownloadURL->text()); bool hasMatch = match.hasMatch(); const bool urlExists_ = hasMatch && !ui.leDownloadURL->text().isEmpty(); if(!urlExists_){ QPalette palette; palette.setColor(QPalette::Base, Qt::red); palette.setColor(QPalette::Text, Qt::black); ui.leDownloadURL->setPalette(palette); ui.leDownloadURL->setToolTip("The URL is invalid!"); } else { QPalette palette; palette.setColor(QPalette::Base, m_baseColor); palette.setColor(QPalette::Text, m_textColor);; ui.leDownloadURL->setPalette(palette); ui.leDownloadURL->setToolTip(""); } return urlExists_; } /** * @brief Checks whether leDatasetName is empty or not. */ bool DatasetMetadataManagerWidget::checkDatasetName() { const bool longNameOk = !ui.leDatasetName->text().isEmpty(); if(!longNameOk) { QPalette palette; palette.setColor(QPalette::Base, Qt::red); palette.setColor(QPalette::Text, Qt::black); ui.leDatasetName->setPalette(palette); ui.leDatasetName->setToolTip("Please fill this out!"); } else { QPalette palette; palette.setColor(QPalette::Base, m_baseColor); palette.setColor(QPalette::Text, m_textColor); ui.leDatasetName->setPalette(palette); ui.leDatasetName->setToolTip(""); } return longNameOk; } /** * @brief Checks whether teDescription is empty or not. */ bool DatasetMetadataManagerWidget::checkDescription() { const bool descriptionOk = !ui.teDescription->toPlainText().isEmpty(); if(!descriptionOk) { QPalette palette; palette.setColor(QPalette::Base, Qt::red); palette.setColor(QPalette::Text, Qt::black); ui.teDescription->setPalette(palette); ui.teDescription->setToolTip("Please fill this out!"); } else { QPalette palette; palette.setColor(QPalette::Base, m_baseColor); palette.setColor(QPalette::Text, m_textColor); ui.teDescription->setPalette(palette); ui.teDescription->setToolTip(""); } return descriptionOk; } /** * @brief Checks whether the given QComboBox's current text is empty or not. */ bool DatasetMetadataManagerWidget::checkCategories(QComboBox* comboBox) { const QString fileName = comboBox->currentText(); const QRegularExpression re("^[\\w\\d]+$"); const QRegularExpressionMatch match = re.match(fileName); const bool hasMatch = match.hasMatch(); qDebug() << hasMatch; if(!hasMatch || fileName.isEmpty()) { qDebug("categoty/subcategory name invalid"); QPalette palette; palette.setColor(QPalette::Base,Qt::red); palette.setColor(QPalette::Text,Qt::black); comboBox->setPalette(palette); comboBox->setToolTip("Invalid or empty name for a category/subcategory (only digits and letters)"); } else { qDebug("categoty/subcategory name valid"); QPalette palette; palette.setColor(QPalette::Base, m_baseColor); palette.setColor(QPalette::Text, m_textColor); comboBox->setPalette(palette); comboBox->setToolTip(""); } return hasMatch; } /** * @brief Enables/disables the widget's components meant to configure the metadata file of the new dataset. */ void DatasetMetadataManagerWidget::enableDatasetSettings(bool enable) { ui.leFileName->setEnabled(enable); ui.leFileName->setReadOnly(!enable); ui.leDatasetName->setEnabled(enable); ui.leDatasetName->setReadOnly(!enable); ui.leDownloadURL->setEnabled(enable); ui.leDownloadURL->setReadOnly(!enable); ui.teDescription->setEnabled(enable); ui.teDescription->setReadOnly(!enable); ui.gbColumnDescriptions->setEnabled(enable); ui.gbFilter->setEnabled(enable); } /** * @brief Checks whether the introduced data is valid or not. Used by DatasetMetadataManagerDialog. */ bool DatasetMetadataManagerWidget::checkDataValidity() { const bool fileNameOK = checkFileName(); const bool urlOk = urlExists(); const bool longNameOk = checkDatasetName(); const bool descriptionOk = checkDescription(); const bool categoryOk = checkCategories(ui.cbCategory); const bool subcategoryOk = checkCategories(ui.cbSubcategory); const bool collectionOk = checkCategories(ui.cbCollection); enableDatasetSettings(categoryOk && subcategoryOk && collectionOk); return fileNameOK && urlOk && longNameOk && descriptionOk && subcategoryOk && categoryOk && collectionOk; } void DatasetMetadataManagerWidget::updateCategories(const QString& collection) { ui.cbCategory->clear(); if( m_datasetModel->collections().contains(collection)) { ui.cbCategory->addItems(m_datasetModel->categories(collection)); } emit checkOk(); } /** * @brief Updates content of cbSubcategory based on current category. */ void DatasetMetadataManagerWidget::updateSubcategories(const QString& category) { ui.cbSubcategory->clear(); const QString collection = ui.cbCollection->currentText(); if( m_datasetModel->categories(collection).contains(category)) { ui.cbSubcategory->addItems(m_datasetModel->subcategories(collection, category)); } emit checkOk(); } /** * @brief Updates the metadata file containing the categories, subcategories and datasets. * @param fileName the name of the metadata file (path) */ void DatasetMetadataManagerWidget::updateDocument(const QString& dirPath) { if(m_datasetModel->collections().contains(ui.cbCollection->currentText())) { QString fileName = dirPath + ui.cbCollection->currentText() + ".json"; qDebug() << "updating: " << fileName; QFile file(fileName); if (file.open(QIODevice::ReadWrite)) { QJsonDocument document = QJsonDocument::fromJson(file.readAll()); //qDebug() <currentText()) == 0) { foundCategory = true; QJsonValueRef subcategoryArrayRef = currentCategory.find("subcategories").value(); QJsonArray subcategoryArray = subcategoryArrayRef.toArray(); qDebug() << "subcategoryArray: " << subcategoryArray.toVariantList(); bool subcategoryFound = false; for(int j = 0; j < subcategoryArray.size(); ++j) { QJsonValueRef subcategoryRef = subcategoryArray[j]; QJsonObject currentSubcategory = subcategoryRef.toObject(); QString subcategoryName = currentSubcategory.value("subcategory_name").toString(); qDebug() << "Subcat name: " << subcategoryName; if(subcategoryName.compare(ui.cbSubcategory->currentText()) == 0) { subcategoryFound = true; QJsonValueRef datasetsRef = currentSubcategory.find("datasets").value(); QJsonArray datasets = datasetsRef.toArray(); qDebug() <<"Datasets content: " << datasets.toVariantList(); datasets.append(createDatasetObject()); datasetsRef = datasets; subcategoryRef = currentSubcategory; subcategoryArrayRef = subcategoryArray; categoryRef = currentCategory; categoryArrayRef = categoryArray; document.setObject(rootObject); break; } } if(!subcategoryFound) { qDebug() << "Subcat not found"; QJsonObject newSubcategory; newSubcategory.insert("subcategory_name", ui.cbSubcategory->currentText()); QJsonArray datasets; datasets.append(createDatasetObject()); newSubcategory.insert("datasets", datasets); subcategoryArray.append(newSubcategory); subcategoryArrayRef = subcategoryArray; categoryRef = currentCategory; categoryArrayRef = categoryArray; document.setObject(rootObject); } break; } } if(!foundCategory) { qDebug() << "Cat not found"; QJsonObject newCategory; newCategory.insert("category_name", ui.cbCategory->currentText()); QJsonArray subcategoryArray; QJsonObject newSubcategory; newSubcategory.insert("subcategory_name", ui.cbSubcategory->currentText()); QJsonArray datasets; datasets.append(createDatasetObject()); newSubcategory.insert("datasets", datasets); subcategoryArray.append(newSubcategory); newCategory.insert("subcategories", subcategoryArray); categoryArray.append(newCategory); categoryArrayRef = categoryArray; document.setObject(rootObject); } qDebug() <collections()) collectionArray.append(collection); collectionArray.append(ui.cbCollection->currentText()); QJsonDocument newDocument; newDocument.setArray(collectionArray); file.write(newDocument.toJson()); file.close(); } QJsonObject rootObject; rootObject.insert("collection_name", ui.cbCollection->currentText()); QJsonArray categoryArray; QJsonObject newCategory; newCategory.insert("category_name", ui.cbCategory->currentText()); QJsonArray subcategoryArray; QJsonObject newSubcategory; newSubcategory.insert("subcategory_name", ui.cbSubcategory->currentText()); QJsonArray datasets; datasets.append(createDatasetObject()); newSubcategory.insert("datasets", datasets); subcategoryArray.append(newSubcategory); newCategory.insert("subcategories", subcategoryArray); categoryArray.append(newCategory); rootObject.insert("categories", categoryArray); QJsonDocument document; document.setObject(rootObject); qDebug() <currentText() + ".json"); if (collectionFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { collectionFile.write(document.toJson()); collectionFile.close(); } } } QJsonObject DatasetMetadataManagerWidget::createDatasetObject() { QJsonObject rootObject; rootObject.insert("filename", ui.leFileName->text()); rootObject.insert("name", ui.leDatasetName->text()); rootObject.insert("download", ui.leDownloadURL->text()); rootObject.insert("description", ui.teDescription->toPlainText()); rootObject.insert("separator", ui.cbSeparatingCharacter->currentText()); rootObject.insert("comment_character", ui.cbCommentCharacter->currentText()); rootObject.insert("DateTime_format", ui.cbDateTimeFormat->currentText()); rootObject.insert("number_format", ui.cbNumberFormat->currentIndex()); rootObject.insert("create_index_column", ui.chbCreateIndex->isChecked()); rootObject.insert("skip_empty_parts", ui.chbSkipEmptyParts->isChecked()); rootObject.insert("simplify_whitespaces", ui.chbSimplifyWhitespaces->isChecked()); rootObject.insert("remove_quotes", ui.chbRemoveQuotes->isChecked()); rootObject.insert("use_first_row_for_vectorname", ui.chbHeader->isChecked()); for(int i = 0; i < m_columnDescriptions.size(); ++i) { rootObject.insert(i18n("column_description_%1", i), m_columnDescriptions[i]); } return rootObject; } -/** - * @brief Creates a new metadata file for the new dataset based on the introduced data. - * @param dirPath the path of the new file - */ -void DatasetMetadataManagerWidget::createNewMetadata(const QString& dirPath) { - if (QDir(dirPath).exists()) - { - QString path = dirPath + QDir::separator() + ui.cbCategory->currentText() + QDir::separator(); - - if(!QDir(path).exists()) { - qDebug() <currentText()); - } - - if(!QDir(path + ui.cbSubcategory->currentText()).exists()) { - qDebug() <currentText(); - QDir(path).mkdir(ui.cbSubcategory->currentText()); - } - - path = path + ui.cbSubcategory->currentText() + QDir::separator(); - m_metadataFilePath = path + ui.leFileName->text() + ".json"; - qDebug() << "Creating " << m_metadataFilePath; - - QFile file(m_metadataFilePath); - if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { - QJsonObject rootObject; - - rootObject.insert("name", ui.leDatasetName->text()); - rootObject.insert("download", ui.leDownloadURL->text()); - rootObject.insert("description", ui.teDescription->toPlainText()); - rootObject.insert("separator", ui.cbSeparatingCharacter->currentText()); - rootObject.insert("comment_character", ui.cbCommentCharacter->currentText()); - rootObject.insert("DateTime_format", ui.cbDateTimeFormat->currentText()); - rootObject.insert("number_format", ui.cbNumberFormat->currentIndex()); - rootObject.insert("create_index_column", ui.chbCreateIndex->isChecked()); - rootObject.insert("skip_empty_parts", ui.chbSkipEmptyParts->isChecked()); - rootObject.insert("simplify_whitespaces", ui.chbSimplifyWhitespaces->isChecked()); - rootObject.insert("remove_quotes", ui.chbRemoveQuotes->isChecked()); - rootObject.insert("use_first_row_for_vectorname", ui.chbHeader->isChecked()); - - for(int i = 0; i < m_columnDescriptions.size(); ++i) { - rootObject.insert(i18n("column_description_%1", i), m_columnDescriptions[i]); - } - - QJsonDocument document; - document.setObject(rootObject); - qDebug() <setText(i18n("Description for column %1", m_columnDescriptions.size() + 1)); QLineEdit* lineEdit = new QLineEdit; int layoutIndex = m_columnDescriptions.size() + 1; qDebug() << "Layout index " << layoutIndex; ui.columnLayout->addWidget(label, layoutIndex, 0); ui.columnLayout->addWidget(lineEdit, layoutIndex, 1, 1, -1); connect(lineEdit, &QLineEdit::textChanged, [this, layoutIndex] (const QString& text) { m_columnDescriptions[layoutIndex - 1] = text; qDebug() << m_columnDescriptions; }); m_columnDescriptions.append(""); } /** * @brief Removes the lastly added QLineEdit (used to set a column description). */ void DatasetMetadataManagerWidget::removeColumnDescription() { const int index = ui.columnLayout->count() - 1; QLayoutItem *item; if ((item = ui.columnLayout->takeAt(index)) != nullptr) { delete item->widget(); delete item; } if ((item = ui.columnLayout->takeAt(index - 1)) != nullptr){ delete item->widget(); delete item; } m_columnDescriptions.removeLast(); } /** * @brief returns the path to the new metadata file of the new dataset. */ QString DatasetMetadataManagerWidget::getMetadataFilePath() const { return m_metadataFilePath; } diff --git a/src/kdefrontend/datasources/DatasetMetadataManagerWidget.h b/src/kdefrontend/datasources/DatasetMetadataManagerWidget.h index 79fb605fc..1db182121 100644 --- a/src/kdefrontend/datasources/DatasetMetadataManagerWidget.h +++ b/src/kdefrontend/datasources/DatasetMetadataManagerWidget.h @@ -1,77 +1,76 @@ /*************************************************************************** File : DatasetMetadataManagerWidget.h Project : LabPlot Description : widget for managing a metadata file of a dataset -------------------------------------------------------------------- Copyright : (C) 2019 Ferencz Kovacs (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 DATASETMETADATAMANAGERWIDGET_H #define DATASETMETADATAMANAGERWIDGET_H #include "ui_datasetmetadatamanagerwidget.h" class DatasetModel; class DatasetMetadataManagerWidget : public QWidget { Q_OBJECT public: explicit DatasetMetadataManagerWidget(QWidget*, const QMap< QString, QMap>>>&); virtual ~DatasetMetadataManagerWidget() override; bool checkDataValidity(); - void updateDocument(const QString& fileName); - void createNewMetadata(const QString& dirPath); + void updateDocument(const QString& fileName); QString getMetadataFilePath() const; private: Ui::DatasetMetadataManagerWidget ui; DatasetModel* m_datasetModel; QStringList m_columnDescriptions; QString m_metadataFilePath; QString m_baseColor; QString m_textColor; void initCategories(const QMap>>&); void initSubcategories(const QMap>>&); void initDatasets(const QMap>>&); bool checkFileName(); bool urlExists(); bool checkDatasetName(); bool checkDescription(); bool checkCategories(QComboBox*); void loadSettings(); void enableDatasetSettings(bool); QJsonObject createDatasetObject(); private slots: void updateCategories(const QString&); void updateSubcategories(const QString&); void addColumnDescription(); void removeColumnDescription(); signals: void checkOk(); }; #endif // DATASETMETADATAMANAGERWIDGET_H diff --git a/src/kdefrontend/datasources/ImportDatasetWidget.cpp b/src/kdefrontend/datasources/ImportDatasetWidget.cpp index f510982e6..385eea26f 100644 --- a/src/kdefrontend/datasources/ImportDatasetWidget.cpp +++ b/src/kdefrontend/datasources/ImportDatasetWidget.cpp @@ -1,768 +1,767 @@ /*************************************************************************** File : ImportDatasetWidget.cpp Project : LabPlot Description : import online dataset widget -------------------------------------------------------------------- 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 * * * ***************************************************************************/ #include "src/backend/datasources/DatasetHandler.h" #include "src/kdefrontend/datasources/ImportDatasetWidget.h" #include "src/kdefrontend/datasources/DatasetMetadataManagerDialog.h" #include "src/kdefrontend/DatasetModel.h" #include #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_loadingCategories(false) { const QString baseDir = QStandardPaths::standardLocations(QStandardPaths::HomeLocation).first(); QString containingDir = "labplot_data"; m_jsonDir = baseDir + QDir::separator() + containingDir + QDir::separator(); ui.setupUi(this); if(!QFile(m_jsonDir + "DatasetCollections.json").exists()) downloadCollectionsFile(); loadDatasetCategoriesFromJson(); ui.lwDatasets->setSelectionMode(QAbstractItemView::SingleSelection); ui.twCategories->setSelectionMode(QAbstractItemView::SingleSelection); connect(ui.cbCollections, &QComboBox::currentTextChanged, this, &ImportDatasetWidget::updateCategoryTree); 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.bNewDataset, &QPushButton::clicked, this, &ImportDatasetWidget::showDatasetMetadataManager); connect(ui.lwDatasets, &QListWidget::itemSelectionChanged, [this]() { emit datasetSelected(); }); connect(ui.lwDatasets, &QListWidget::doubleClicked, [this]() { emit datasetDoubleClicked(); }); } ImportDatasetWidget::~ImportDatasetWidget() { if(m_categoryCompleter != nullptr) delete m_categoryCompleter; if(m_datasetCompleter != nullptr) delete m_datasetCompleter; //TODO: Save selected state } /** * @brief Locates in the file system the json metadata file that contains the list of categories and subcategories. * @return The location of the file */ QString ImportDatasetWidget::locateCategoryJsonFile() const { qDebug() << "Locating category file" << QStandardPaths::locate(QStandardPaths::AppDataLocation, "datasets/DatasetCategories.json"); return QStandardPaths::locate(QStandardPaths::AppDataLocation, "datasets/DatasetCategories.json"); } /** * @brief Processes the json metadata file that contains the list of categories and subcategories and their datasets. */ void ImportDatasetWidget::loadDatasetCategoriesFromJson() { qDebug() << "loading Collections"; QString filePath = m_jsonDir + "DatasetCollections.json"; QFile file(filePath); if (file.open(QIODevice::ReadOnly)) { m_datasetsMap.clear(); ui.cbCollections->clear(); ui.cbCollections->addItem("All"); QJsonDocument document = QJsonDocument::fromJson(file.readAll()); QJsonArray collections; if(document.isArray()) collections = document.array(); else { qDebug()<< "The DatasetCollections.json file is invalid"; return; } for (int collectionIndex = 0; collectionIndex < collections.size(); collectionIndex++) { const QString currentCollection = collections[collectionIndex].toString(); ui.cbCollections->addItem(currentCollection); if(!QFile(m_jsonDir + currentCollection + ".json").exists()) downloadCollectionFile(currentCollection + ".json"); QFile collectionFile(m_jsonDir + currentCollection + ".json"); if (collectionFile.open(QIODevice::ReadOnly)) { QJsonDocument collectionDocument = QJsonDocument::fromJson(collectionFile.readAll()); QJsonObject collectionObject; //qDebug() << "Collection document: " << collectionDocument; //qDebug() << "Is object" << collectionDocument.isObject() << collectionDocument.isArray() << collectionDocument.isEmpty(); if(collectionDocument.isObject()) { collectionObject = collectionDocument.object(); } else { qDebug()<< "The " + currentCollection + ".json file is invalid"; return; } if(collectionObject.value("collection_name").toString().compare(currentCollection) != 0) { qDebug()<< "The " + currentCollection + ".json file name is invalid"; return; } 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 o the actual subcategory for (const auto& dataset : datasetArray) { m_datasetsMap[currentCollection][categoryName][subcategoryName].push_back(dataset.toObject().value("filename").toString()); } } } } } if(m_datasetModel != nullptr) delete m_datasetModel; m_datasetModel = new DatasetModel(m_datasetsMap); updateCategoryTree(ui.cbCollections->currentText()); restoreSelectedSubcategory(ui.cbCollections->currentText()); file.close(); } else { qDebug("Couldn't open dataset collections file"); } } void ImportDatasetWidget::updateCategoryTree(const QString& collection) { m_loadingCategories = true; ui.lwDatasets->clear(); ui.twCategories->clear(); QStringList categories = (collection.compare("All") == 0) ? m_datasetModel->allCategories().toStringList() : m_datasetModel->categories(collection); for(auto category : categories) { QTreeWidgetItem* const currentCategoryItem = new QTreeWidgetItem(QStringList(category)); ui.twCategories->addTopLevelItem(currentCategoryItem); QStringList subcategories = (collection.compare("All") == 0) ? m_datasetModel->allSubcategories(category).toStringList() : m_datasetModel->subcategories(collection, category); for(auto subcategory : subcategories) { currentCategoryItem->addChild(new QTreeWidgetItem(QStringList(subcategory))); } } if(m_selectedCollection.compare(collection) == 0) { restoreSelectedSubcategory(ui.cbCollections->currentText()); } else { m_selectedCollection = collection; m_selectedCategory = ""; m_selectedSubcategory = ""; } m_loadingCategories = false; updateCategoryCompleter(); } /** * @brief Restores the lastly selected subcategory making it the selected QTreeWidgetItem and also lists the datasets belonigng to it/ */ void ImportDatasetWidget::restoreSelectedSubcategory(const QString& collection) { if(m_datasetModel->categories(collection).contains(m_selectedCategory)) { const QTreeWidgetItem* const categoryItem = ui.twCategories->findItems(m_selectedCategory, Qt::MatchExactly).first(); if(m_datasetModel->subcategories(collection, 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(); highlightLocalMetadataFiles(); } } 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) { categoryList.append(ui.twCategories->topLevelItem(i)->text(0) + QLatin1Char(':') + ui.twCategories->topLevelItem(i)->child(j)->text(0)); } } 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) { const QString fileName = getSelectedDataset() + QLatin1String(".json"); QString filePath = m_jsonDir;// + m_selectedCategory + QDir::separator() + m_selectedSubcategory + QDir::separator(); QJsonObject datasetObject = loadDatasetObject(); qDebug()<<"Dataset object" << datasetObject; if(!datasetObject.isEmpty()) { datasetHandler->processMetadata(datasetObject, filePath); } else { QMessageBox::critical(this, i18n("Invalid metadata file"), i18n("The metadata file for the choosen dataset isn't valid")); } } QJsonObject ImportDatasetWidget::loadDatasetObject() { QString filePath = m_jsonDir + "DatasetCollections.json"; QFile file(filePath); bool allCollections = (m_selectedCollection.compare("All") == 0); if (file.open(QIODevice::ReadOnly)) { QJsonDocument document = QJsonDocument::fromJson(file.readAll()); QJsonArray collections; if(document.isArray()) collections = document.array(); else { qDebug()<< "The DatasetCollections.json file is invalid"; return QJsonObject(); } for (int collectionIndex = 0; collectionIndex < collections.size(); collectionIndex++) { const QString currentCollection = collections[collectionIndex].toString(); if(currentCollection.compare(m_selectedCollection) == 0 || allCollections) { QFile collectionFile(m_jsonDir + currentCollection + ".json"); if (collectionFile.open(QIODevice::ReadOnly)) { QJsonDocument collectionDocument = QJsonDocument::fromJson(collectionFile.readAll()); QJsonObject collectionObject; qDebug() << "Collection document: " << collectionDocument; if(collectionDocument.isObject()) { collectionObject = collectionDocument.object(); } else { qDebug()<< "The " + currentCollection + ".json file is invalid"; return QJsonObject(); } if(collectionObject.value("collection_name").toString().compare(currentCollection) != 0) { qDebug()<< "The " + currentCollection + ".json file's name is invalid"; return QJsonObject(); } 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(); } } } } } } } } } 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(); dlg->updateDocument(m_jsonDir); - //dlg->createNewMetadata(dirPath); uploadCategoryFile(); uploadDatasetFile(dlg->getMetadataFilePath()); loadDatasetCategoriesFromJson(); } delete dlg; } /** * @brief Places the metadata file containing the categories and subcategories into a specific directory. */ void ImportDatasetWidget::downloadCollectionsFile() { qDebug() << "Downloading collections file"; 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; /*QString pathToNewFile = m_jsonDir + m_selectedCategory; //Create directory structure based on category and subcategory name if(!QDir(m_jsonDir + m_selectedCategory).exists()) { qDebug() <count(); ++i) { QListWidgetItem* const currentItem = ui.lwDatasets->item(i); bool found = false; for(QString entry : dir.entryList()) { if(entry.startsWith(currentItem->text()) && !entry.endsWith(".json")) { found = true; break; } } if(found) currentItem->setBackgroundColor(Qt::yellow); else currentItem->setBackgroundColor(Qt::white); } } /** * @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 QMap< QString, QMap>>>& ImportDatasetWidget::getDatasetsMap() { return m_datasetsMap; } void ImportDatasetWidget::setCollection(const QString& collection) { ui.cbCollections->setCurrentText(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; } } } diff --git a/src/kdefrontend/main.qml b/src/kdefrontend/main.qml index 94c18eb17..82398d4ac 100644 --- a/src/kdefrontend/main.qml +++ b/src/kdefrontend/main.qml @@ -1,1908 +1,1937 @@ import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick 2.4 import QtQuick.Controls 2.3 import QtQuick.Scene2D 2.9 import QtQuick.Controls.Universal 2.0 import QtQuick.Extras 1.4 import QtQuick.Layouts 1.3 import QtWebView 1.1 import QtWebEngine 1.8 //import labplot.datasetmodel 1.0 Rectangle { id: mainWindow width: 1920 height: 1080 property int spacing: 10 visible: true property string currentCategory: '' property string currentSubcategory: '' property string currentDataset: '' property string initialUrl : "https://labplot.kde.org/2019/04/19/labplot-2-6-released/" property alias mainWindow: mainWindow signal recentProjectClicked(url path) signal datasetClicked(string category, string subcategory, string dataset) signal openDataset() signal openExampleProject(string name) function restoreOriginalLayout() { console.log("Restore widget dimensions") - recentProjectsFrame.width = mainWindow.width / 5 - 4*mainWindow.spacing - recentProjectsFrame.height = mainWindow.height / 4 - 4*mainWindow.spacing - exampleProjects.width = 3 * mainWindow.width / 5 - 4*mainWindow.spacing - exampleProjects.height = mainWindow.height / 4 - 4*mainWindow.spacing - newsSection.width = mainWindow.width / 5 - 4*mainWindow.spacing - newsSection.height = mainWindow.height- 4*mainWindow.spacing - helpFrame.width = mainWindow.width / 5 - 4*mainWindow.spacing - helpFrame.height = mainWindow.height / 4 - 4*mainWindow.spacing - datasetFrame.width = 3 * mainWindow.width / 5 - 4*mainWindow.spacing - datasetFrame.height = mainWindow.height / 4 - 4*mainWindow.spacing - releaseSection.width = 4 * mainWindow.width / 5 - 4*mainWindow.spacing - releaseSection.height = 2*mainWindow.height / 4 - 4*mainWindow.spacing + recentProjectsFrame.widthRate = (mainWindow.width / 5 - 4*mainWindow.spacing)/ mainWindow.width + recentProjectsFrame.heightRate = (mainWindow.height / 4 - 4*mainWindow.spacing)/ mainWindow.height + exampleProjects.widthRate = (3 * mainWindow.width / 5 - 4*mainWindow.spacing) / mainWindow.width + exampleProjects.heightRate =(mainWindow.height / 4 - 4*mainWindow.spacing) / mainWindow.height + newsSection.widthRate = (mainWindow.width / 5 - 4*mainWindow.spacing) / mainWindow.width + newsSection.heightRate = (mainWindow.height- 4*mainWindow.spacing) / mainWindow.height + helpFrame.widthRate = (mainWindow.width / 5 - 4*mainWindow.spacing) / mainWindow.width + helpFrame.heightRate = (mainWindow.height / 4 - 4*mainWindow.spacing) / mainWindow.height + datasetFrame.widthRate = (3 * mainWindow.width / 5 - 4*mainWindow.spacing) / mainWindow.width + datasetFrame.heightRate = (mainWindow.height / 4 - 4*mainWindow.spacing) / mainWindow.height + releaseSection.widthRate = (4 * mainWindow.width / 5 - 4*mainWindow.spacing) / mainWindow.width + releaseSection.heightRate = (2*mainWindow.height / 4 - 4*mainWindow.spacing) / mainWindow.height } function saveWidgetDimensions() { console.log("Save welcome screen widget dimensions") - helper.setHeightScale(recentProjectsFrame.sectionName, recentProjectsFrame.height) - helper.setWidthScale(recentProjectsFrame.sectionName, recentProjectsFrame.width) - helper.setHeightScale(exampleProjects.sectionName, exampleProjects.height) - helper.setWidthScale(exampleProjects.sectionName, exampleProjects.width) - helper.setHeightScale(newsSection.sectionName, newsSection.height) - helper.setWidthScale(newsSection.sectionName, newsSection.width) - helper.setHeightScale(helpFrame.sectionName, helpFrame.height) - helper.setWidthScale(helpFrame.sectionName, helpFrame.width) - helper.setHeightScale(datasetFrame.sectionName, datasetFrame.height) - helper.setWidthScale(datasetFrame.sectionName, datasetFrame.width) - helper.setHeightScale(releaseSection.sectionName, releaseSection.height) - helper.setWidthScale(releaseSection.sectionName, releaseSection.width) + helper.setHeightScale(recentProjectsFrame.sectionName, recentProjectsFrame.heightRate) + helper.setWidthScale(recentProjectsFrame.sectionName, recentProjectsFrame.widthRate) + helper.setHeightScale(exampleProjects.sectionName, exampleProjects.heightRate) + helper.setWidthScale(exampleProjects.sectionName, exampleProjects.widthRate) + helper.setHeightScale(newsSection.sectionName, newsSection.heightRate) + helper.setWidthScale(newsSection.sectionName, newsSection.widthRate) + helper.setHeightScale(helpFrame.sectionName, helpFrame.heightRate) + helper.setWidthScale(helpFrame.sectionName, helpFrame.widthRate) + helper.setHeightScale(datasetFrame.sectionName, datasetFrame.heightRate) + helper.setWidthScale(datasetFrame.sectionName, datasetFrame.widthRate) + helper.setHeightScale(releaseSection.sectionName, releaseSection.heightRate) + helper.setWidthScale(releaseSection.sectionName, releaseSection.widthRate) } function hideTiles() { recentProjectsFrame.visible = false recentProjectsFrame.z = 0 exampleProjects.visible = false exampleProjects.z = 0 newsSection.visible = false newsSection.z = 0 helpFrame.visible = false helpFrame.z = 0 datasetFrame.visible = false datasetFrame.z = 0 releaseSection.visible = false releaseSection.z = 0 } function showTiles() { recentProjectsFrame.visible = true recentProjectsFrame.z = 0 exampleProjects.visible = true exampleProjects.z = 0 newsSection.visible = true newsSection.z = 0 helpFrame.visible = true helpFrame.z = 0 datasetFrame.visible = true datasetFrame.z = 0 releaseSection.visible = true releaseSection.z = 0 } Connections { target: helper onDatasetFound:{ datasetTitle.text = helper.datasetName() console.log("Title width: " + datasetTitle.width) datasetDescription.text = helper.datasetDescription() console.log("Description width: " + datasetDescription.width) console.log("Description height: " + datasetDescription.paintedHeight + " " + height) datasetRows.text = helper.datasetRows() datasetColumns.text = helper.datasetColumns() } onDatasetNotFound:{ datasetTitle.text = "-" datasetDescription.text = "-" datasetRows.text = "-" datasetColumns.text = "-"; } onShowFirstDataset:{ datasetClicked(mainWindow.currentCategory, mainWindow.currentSubcategory, mainWindow.currentDataset) } } Frame { id: recentProjectsFrame property string sectionName: "recentProjectsFrame" - width: helper.getWidthScale(sectionName) === -1 ? mainWindow.width / 5 - 4*mainWindow.spacing : helper.getWidthScale(sectionName) - height: helper.getHeightScale(sectionName) === -1 ? mainWindow.height / 4 - 4*mainWindow.spacing : helper.getHeightScale(sectionName) + property double widthRate : helper.getWidthScale(sectionName) === -1 ? (mainWindow.width / 5 - 4*mainWindow.spacing) / mainWindow.width : helper.getWidthScale(sectionName) + property double heightRate : helper.getHeightScale(sectionName) === -1 ? (mainWindow.height / 4 - 4*mainWindow.spacing) / mainWindow.height : helper.getHeightScale(sectionName) + width: mainWindow.width * widthRate + height: mainWindow.height * heightRate anchors.top: parent.top anchors.topMargin: mainWindow.spacing anchors.left: parent.left anchors.leftMargin: mainWindow.spacing visible: true opacity: 1 padding: 5 property bool fullScreen: false property double prevWidth: 0 property double prevHeight: 0 Component.onCompleted: { console.log("Recent projects saved iwdth " + helper.getWidthScale(sectionName) + " height " + helper.getHeightScale(sectionName)) if(helper.getWidthScale(sectionName) === -1 || helper.getHeightScale(sectionName) === -1) mainWindow.restoreOriginalLayout() } Rectangle { width: 3 height: parent.height color: "gray" anchors.left: parent.right anchors.rightMargin: 0 opacity: 0 MouseArea { anchors.fill: parent hoverEnabled: true onEntered: {parent.opacity = 1} onExited: { if(!drag.active && !pressed) parent.opacity = 0 } onPressed:parent.opacity = 1 onPressAndHold:parent.opacity = 1 onReleased: parent.opacity = 0 drag{ target: parent; axis: Drag.XAxis;} onMouseXChanged: { if(drag.active){ - recentProjectsFrame.width = recentProjectsFrame.width + mouseX - exampleProjects.width = exampleProjects.width - mouseX + recentProjectsFrame.widthRate = (recentProjectsFrame.width + mouseX) / mainWindow.width + exampleProjects.widthRate = (exampleProjects.width - mouseX) / mainWindow.width if(recentProjectsFrame.width < 150){ - recentProjectsFrame.width = 150 - exampleProjects.width = mainWindow.width - newsSection.width - 150 - 4*mainWindow.spacing + recentProjectsFrame.widthRate = 150 / mainWindow.width + exampleProjects.widthRate = (mainWindow.width - newsSection.width - 150 - 4*mainWindow.spacing) / mainWindow.width } if(exampleProjects.width < 300) { - exampleProjects.width = 300 - recentProjectsFrame.width = mainWindow.width - newsSection.width - 300 - 4*mainWindow.spacing + exampleProjects.widthRate = 300 / mainWindow.width + recentProjectsFrame.widthRate = (mainWindow.width - newsSection.width - 300 - 4*mainWindow.spacing) / mainWindow.width } } } } } Rectangle { height: 3 width : parent.width color: "gray" anchors.top: parent.bottom anchors.bottomMargin: 0 opacity: 0 MouseArea { anchors.fill: parent hoverEnabled: true onEntered: {parent.opacity = 1} onExited: { if(!drag.active && !pressed) parent.opacity = 0 } onPressed:parent.opacity = 1 onPressAndHold:parent.opacity = 1 onReleased: parent.opacity = 0 drag{ target: parent; axis: Drag.YAxis } onMouseYChanged: { if(drag.active){ - recentProjectsFrame.height = recentProjectsFrame.height + mouseY - exampleProjects.height = exampleProjects.height + mouseY - helpFrame.height = helpFrame.height - mouseY - datasetFrame.height = datasetFrame.height - mouseY + recentProjectsFrame.heightRate = (recentProjectsFrame.height + mouseY) / mainWindow.height + exampleProjects.heightRate = (exampleProjects.height + mouseY) / mainWindow.height + helpFrame.heightRate = (helpFrame.height - mouseY) / mainWindow.height + datasetFrame.heightRate = (datasetFrame.height - mouseY) / mainWindow.height if(recentProjectsFrame.height < 100 || exampleProjects.height < 100) { - recentProjectsFrame.height = 100 - exampleProjects.height = 100 - helpFrame.height = mainWindow.height - releaseSection.height - 100 - 4*mainWindow.spacing - datasetFrame.height = mainWindow.height - releaseSection.height - 100 - 4*mainWindow.spacing + recentProjectsFrame.heightRate = 100 / mainWindow.height + exampleProjects.heightRate = 100 / mainWindow.height + helpFrame.heightRate = (mainWindow.height - releaseSection.height - 100 - 4*mainWindow.spacing) / mainWindow.height + datasetFrame.heightRate = (mainWindow.height - releaseSection.height - 100 - 4*mainWindow.spacing) / mainWindow.height } if(helpFrame.height < 100 || datasetFrame.height < 100) { - helpFrame.height = 100 - datasetFrame.height = 100 - recentProjectsFrame.height = mainWindow.height - releaseSection.height - 100 - 4*mainWindow.spacing - exampleProjects.height = mainWindow.height - releaseSection.height - 100 - 4*mainWindow.spacing + helpFrame.heightRate = 100 / mainWindow.height + datasetFrame.heightRate = 100 / mainWindow.height + recentProjectsFrame.heightRate = (mainWindow.height - releaseSection.height - 100 - 4*mainWindow.spacing) / mainWindow.height + exampleProjects.heightRate = (mainWindow.height - releaseSection.height - 100 - 4*mainWindow.spacing) / mainWindow.height } } } } } ColumnLayout { anchors.fill: parent spacing: 20 clip: true RowLayout { Layout.fillWidth: true Layout.minimumHeight: Math.min(parent.height*0.2, 100) Layout.preferredHeight: Math.min(parent.height*0.2, 100) Image { Layout.preferredHeight: recentProjectsFrame.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) Layout.minimumHeight: recentProjectsFrame.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) Layout.preferredWidth: recentProjectsFrame.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) Layout.minimumWidth: recentProjectsFrame.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) Layout.alignment: Qt.AlignVCenter source: recentProjectsFrame.fullScreen ? helper.getMinIcon() : helper.getMaxIcon() sourceSize.width: recentProjectsFrame.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) sourceSize.height: recentProjectsFrame.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) MouseArea { anchors.fill: parent onClicked: { if(!recentProjectsFrame.fullScreen) { hideTiles() - recentProjectsFrame.prevWidth = recentProjectsFrame.width - recentProjectsFrame.prevHeight = recentProjectsFrame.height + recentProjectsFrame.prevWidth = recentProjectsFrame.widthRate + recentProjectsFrame.prevHeight = recentProjectsFrame.heightRate recentProjectsFrame.visible = true recentProjectsFrame.z = 1 recentProjectsFrame.anchors.fill = undefined recentProjectsFrame.anchors.right = undefined recentProjectsFrame.anchors.bottom = undefined recentProjectsFrame.anchors.centerIn = undefined recentProjectsFrame.anchors.top = undefined recentProjectsFrame.anchors.left = undefined - recentProjectsFrame.width = mainWindow.width - recentProjectsFrame.height = mainWindow.height + recentProjectsFrame.widthRate = 1 + recentProjectsFrame.heightRate = 1 recentProjectsFrame.anchors.fill = mainWindow } else { recentProjectsFrame.anchors.fill = undefined recentProjectsFrame.anchors.right = undefined recentProjectsFrame.anchors.bottom = undefined recentProjectsFrame.anchors.centerIn = undefined recentProjectsFrame.anchors.top = undefined recentProjectsFrame.anchors.left = undefined recentProjectsFrame.anchors.top = mainWindow.top recentProjectsFrame.anchors.topMargin = mainWindow.spacing recentProjectsFrame.anchors.left = mainWindow.left recentProjectsFrame.anchors.leftMargin = mainWindow.spacing - recentProjectsFrame.width = recentProjectsFrame.prevWidth - recentProjectsFrame.height = recentProjectsFrame.prevHeight + recentProjectsFrame.widthRate = recentProjectsFrame.prevWidth + recentProjectsFrame.heightRate = recentProjectsFrame.prevHeight showTiles(); } recentProjectsFrame.fullScreen = !recentProjectsFrame.fullScreen } } } Label { color: "#000000" text: qsTr("Recent Projects") styleColor: "#979191" opacity: 1 visible: true font.underline: false font.italic: false font.bold: false verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter font.pointSize: recentProjectsFrame.fullScreen ? 60 : 24 minimumPointSize: 10 fontSizeMode: Text.Fit Layout.fillWidth: true Layout.fillHeight: true wrapMode: Text.WordWrap } } RecentProjects { id: recentProjectsList model:recentProjects Layout.fillHeight: true Layout.fillWidth: true Layout.minimumHeight: parent.height*0.8 Layout.preferredHeight: parent.height*0.8 clip: true } } } Frame { id: exampleProjects property string sectionName: "exampleProjects" - width: helper.getWidthScale(sectionName) === -1 ? 3 * mainWindow.width / 5 - 4*mainWindow.spacing : helper.getWidthScale(sectionName) - height: helper.getHeightScale(sectionName) === -1 ? mainWindow.height / 4 - 4*mainWindow.spacing : helper.getHeightScale(sectionName) + property double widthRate : helper.getWidthScale(sectionName) === -1 ? (3 * mainWindow.width / 5 - 4*mainWindow.spacing) / mainWindow.width : helper.getWidthScale(sectionName) + property double heightRate : helper.getHeightScale(sectionName) === -1 ? (mainWindow.height / 4 - 4*mainWindow.spacing) / mainWindow.height : helper.getHeightScale(sectionName) + width: mainWindow.width * widthRate + height: mainWindow.height * heightRate anchors.top: parent.top anchors.topMargin: mainWindow.spacing anchors.left: recentProjectsFrame.right anchors.leftMargin: mainWindow.spacing anchors.right: newsSection.left anchors.rightMargin: mainWindow.spacing visible: true clip: true opacity: 1 padding: 5 property bool fullScreen: false property double prevWidth: 0 property double prevHeight: 0 Component.onCompleted: { if(helper.getWidthScale(sectionName) === -1 || helper.getHeightScale(sectionName) === -1) mainWindow.restoreOriginalLayout() } Rectangle { width: 3 height: parent.height color: "gray" anchors.left: parent.right anchors.rightMargin: 0 opacity: 0 MouseArea { anchors.fill: parent hoverEnabled: true onEntered: {parent.opacity = 1} onExited: { if(!drag.active && !pressed) parent.opacity = 0 } onPressed:parent.opacity = 1 onPressAndHold:parent.opacity = 1 onReleased: parent.opacity = 0 drag{ target: parent; axis: Drag.XAxis } onMouseXChanged: { if(drag.active){ - newsSection.width = newsSection.width - mouseX - exampleProjects.width = exampleProjects.width + mouseX - datasetFrame.width = datasetFrame.width + mouseX - releaseSection.width = releaseSection.width + mouseX + newsSection.widthRate = (newsSection.width - mouseX) / mainWindow.width + exampleProjects.widthRate = (exampleProjects.width + mouseX) / mainWindow.width + datasetFrame.widthRate = (datasetFrame.width + mouseX) / mainWindow.width + releaseSection.widthRate = (releaseSection.width + mouseX) / mainWindow.width if(exampleProjects.width < 300 || datasetFrame.width < 300){ - newsSection.width = mainWindow.width - Math.max(recentProjectsFrame.width, helpFrame.width) - 300 - 4*mainWindow.spacing - exampleProjects.width = mainWindow.width - newsSection.width - recentProjectsFrame.width - 4*mainWindow.spacing - datasetFrame.width = mainWindow.width - newsSection.width - helpFrame.width - 4*mainWindow.spacing - releaseSection.width = Math.max(recentProjectsFrame.width, helpFrame.width) + mainWindow.spacing + 300 + newsSection.widthRate = (mainWindow.width - Math.max(recentProjectsFrame.width, helpFrame.width) - 300 - 4*mainWindow.spacing) / mainWindow.width + exampleProjects.widthRate = (mainWindow.width - newsSection.width - recentProjectsFrame.width - 4*mainWindow.spacing) / mainWindow.width + datasetFrame.widthRate = (mainWindow.width - newsSection.width - helpFrame.width - 4*mainWindow.spacing) / mainWindow.width + releaseSection.widthRate = (Math.max(recentProjectsFrame.width, helpFrame.width) + mainWindow.spacing + 300) / mainWindow.width } if(newsSection.width < 150) { - newsSection.width = 150 - exampleProjects.width = mainWindow.width - recentProjectsFrame.width - 150 - 4*mainWindow.spacing - datasetFrame.width = mainWindow.width - helpFrame.width - 150 - 4*mainWindow.spacing - releaseSection.width = mainWindow.width - newsSection.width - 3*mainWindow.spacing + newsSection.widthRate = 150 / mainWindow.width + exampleProjects.widthRate = (mainWindow.width - recentProjectsFrame.width - 150 - 4*mainWindow.spacing) / mainWindow.width + datasetFrame.widthRate =(mainWindow.width - helpFrame.width - 150 - 4*mainWindow.spacing) / mainWindow.width + releaseSection.widthRate = (mainWindow.width - newsSection.width - 3*mainWindow.spacing) / mainWindow.width } } } } } Rectangle { height: 3 width : parent.width color: "gray" anchors.top: parent.bottom anchors.bottomMargin: 0 opacity: 0 MouseArea { anchors.fill: parent hoverEnabled: true onEntered: {parent.opacity = 1} onExited: { if(!drag.active && !pressed) parent.opacity = 0 } onPressed:parent.opacity = 1 onPressAndHold:parent.opacity = 1 onReleased: parent.opacity = 0 drag{ target: parent; axis: Drag.YAxis } onMouseYChanged: { if(drag.active){ - recentProjectsFrame.height = recentProjectsFrame.height + mouseY - exampleProjects.height = exampleProjects.height + mouseY - helpFrame.height = helpFrame.height - mouseY - datasetFrame.height = datasetFrame.height - mouseY + recentProjectsFrame.heightRate = (recentProjectsFrame.height + mouseY) / mainWindow.height + exampleProjects.heightRate = (exampleProjects.height + mouseY) / mainWindow.height + helpFrame.heightRate = (helpFrame.height - mouseY) / mainWindow.height + datasetFrame.heightRate = (datasetFrame.height - mouseY) / mainWindow.height if(recentProjectsFrame.height < 100 || exampleProjects.height < 100) { - recentProjectsFrame.height = 100 - exampleProjects.height = 100 - helpFrame.height = mainWindow.height - releaseSection.height - 100 - 4*mainWindow.spacing - datasetFrame.height = mainWindow.height - releaseSection.height - 100 - 4*mainWindow.spacing + recentProjectsFrame.heightRate = 100 / mainWindow.height + exampleProjects.heightRate = 100/ mainWindow.height + helpFrame.heightRate = (mainWindow.height - releaseSection.height - 100 - 4*mainWindow.spacing) / mainWindow.height + datasetFrame.heightRate = (mainWindow.height - releaseSection.height - 100 - 4*mainWindow.spacing) / mainWindow.height } if(helpFrame.height < 100 || datasetFrame.height < 100) { - helpFrame.height = 100 - datasetFrame.height = 100 - recentProjectsFrame.height = mainWindow.height - releaseSection.height - 100 - 4*mainWindow.spacing - exampleProjects.height = mainWindow.height - releaseSection.height - 100 - 4*mainWindow.spacing + helpFrame.heightRate = 100 / mainWindow.height + datasetFrame.heightRate = 100 / mainWindow.height + recentProjectsFrame.heightRate = (mainWindow.height - releaseSection.height - 100 - 4*mainWindow.spacing) / mainWindow.height + exampleProjects.heightRate = (mainWindow.height - releaseSection.height - 100 - 4*mainWindow.spacing)/ mainWindow.height } } } } } Rectangle { width: 3 height: parent.height color: "gray" anchors.right: parent.left anchors.leftMargin: 0 opacity: 0 MouseArea { anchors.fill: parent hoverEnabled: true onEntered: {parent.opacity = 1} onExited: { if(!drag.active && !pressed) parent.opacity = 0 } onPressed:parent.opacity = 1 onPressAndHold:parent.opacity = 1 onReleased: parent.opacity = 0 drag{ target: parent; axis: Drag.XAxis } onMouseXChanged: { if(drag.active){ - exampleProjects.width = exampleProjects.width - mouseX - recentProjectsFrame.width = recentProjectsFrame.width + mouseX + exampleProjects.widthRate = (exampleProjects.width - mouseX)/ mainWindow.width + recentProjectsFrame.widthRate = (recentProjectsFrame.width + mouseX) / mainWindow.width if(recentProjectsFrame.width < 150){ - recentProjectsFrame.width = 150 - exampleProjects.width = mainWindow.width - newsSection.width - 150 - 4*mainWindow.spacing + recentProjectsFrame.widthRate = 150 / mainWindow.width + exampleProjects.widthRate = (mainWindow.width - newsSection.width - 150 - 4*mainWindow.spacing) / mainWindow.width } if(exampleProjects.width < 300) { - exampleProjects.width = 300 - recentProjectsFrame.width = mainWindow.width - newsSection.width - 300 - 4*mainWindow.spacing + exampleProjects.widthRate = 300/ mainWindow.width + recentProjectsFrame.widthRate = (mainWindow.width - newsSection.width - 300 - 4*mainWindow.spacing) / mainWindow.width } } } } } ColumnLayout { anchors.fill: parent clip: true spacing: 5 RowLayout { Layout.fillWidth: true Layout.minimumHeight: Math.min((parent.height - 25 - 2*parent.spacing)*0.2, 100) Layout.preferredHeight: Math.min((parent.height - 25 - 2*parent.spacing)*0.2, 100) Image { Layout.preferredHeight: exampleProjects.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) Layout.minimumHeight: exampleProjects.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) Layout.preferredWidth: exampleProjects.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) Layout.minimumWidth: exampleProjects.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) Layout.alignment: Qt.AlignVCenter source: exampleProjects.fullScreen ? helper.getMinIcon() : helper.getMaxIcon() sourceSize.width: exampleProjects.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) sourceSize.height: exampleProjects.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) MouseArea { anchors.fill: parent onClicked: { if(!exampleProjects.fullScreen) { hideTiles() - exampleProjects.prevWidth = exampleProjects.width - exampleProjects.prevHeight = exampleProjects.height + exampleProjects.prevWidth = exampleProjects.widthRate + exampleProjects.prevHeight = exampleProjects.heightRate exampleProjects.visible = true exampleProjects.z = 1 exampleProjects.anchors.fill = undefined exampleProjects.anchors.right = undefined exampleProjects.anchors.bottom = undefined exampleProjects.anchors.centerIn = undefined exampleProjects.anchors.top = undefined exampleProjects.anchors.left = undefined + exampleProjects.widthRate = 1 + exampleProjects.heightRate = 1 + exampleProjects.anchors.fill = mainWindow } else { exampleProjects.anchors.fill = undefined exampleProjects.anchors.right = undefined exampleProjects.anchors.bottom = undefined exampleProjects.anchors.centerIn = undefined exampleProjects.anchors.top = undefined exampleProjects.anchors.left = undefined exampleProjects.anchors.top = mainWindow.top exampleProjects.anchors.topMargin = mainWindow.spacing exampleProjects.anchors.left = recentProjectsFrame.right exampleProjects.anchors.leftMargin = mainWindow.spacing exampleProjects.anchors.right = newsSection.left exampleProjects.anchors.rightMargin = mainWindow.spacing - exampleProjects.width = exampleProjects.prevWidth - exampleProjects.height = exampleProjects.prevHeight + exampleProjects.widthRate = exampleProjects.prevWidth + exampleProjects.heightRate = exampleProjects.prevHeight showTiles(); } exampleProjects.fullScreen = !exampleProjects.fullScreen } } } Label { text: qsTr("Examples") styleColor: "#d41919" verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter font.pointSize: exampleProjects.fullScreen ? 60 : 24 minimumPointSize: 10 fontSizeMode: Text.Fit Layout.fillWidth: true Layout.fillHeight: true wrapMode: Text.WordWrap } } TextField { id: searchText placeholderText: "Search among example projects" Layout.fillWidth: true; onTextChanged: {exampleGrid.model = helper.searchExampleProjects(searchText.text)} height: 25 Layout.minimumHeight: 25 Layout.preferredHeight: 25 } ExampleGrid { id: exampleGrid Layout.fillHeight: true Layout.fillWidth: true clip:true Layout.minimumHeight: Math.max((parent.height - 25 - 2*parent.spacing)*0.8, parent.height - 25 - 100 - 2*parent.spacing) Layout.preferredHeight: Math.max((parent.height - 25 - 2*parent.spacing)*0.8, parent.height - 25 - 100 - 2*parent.spacing) } } } Frame { id: newsSection property string sectionName: "newsSection" - width: helper.getWidthScale(sectionName) === -1 ? mainWindow.width / 5 - 4*mainWindow.spacing : helper.getWidthScale(sectionName) - height: helper.getHeightScale(sectionName) === -1 ? mainWindow.height- 4*mainWindow.spacing : helper.getHeightScale(sectionName) + property double widthRate : helper.getWidthScale(sectionName) === -1 ? (mainWindow.width / 5 - 4*mainWindow.spacing) / mainWindow.width : helper.getWidthScale(sectionName) + property double heightRate : helper.getHeightScale(sectionName) === -1 ? (mainWindow.height- 4*mainWindow.spacing) / mainWindow.height : helper.getHeightScale(sectionName) + width: mainWindow.width * widthRate + height: mainWindow.height * heightRate + anchors.bottom: parent.bottom anchors.bottomMargin: mainWindow.spacing anchors.right: parent.right anchors.rightMargin: mainWindow.spacing anchors.top: parent.top anchors.topMargin: mainWindow.spacing visible: true opacity: 1 padding: 5 clip: true property bool fullScreen: false property double prevWidth: 0 property double prevHeight: 0 Component.onCompleted: { if(helper.getWidthScale(sectionName) === -1 || helper.getHeightScale(sectionName) === -1) mainWindow.restoreOriginalLayout() } Rectangle { width: 3 height: parent.height color: "gray" anchors.right: parent.left anchors.leftMargin: 0 opacity: 0 MouseArea { anchors.fill: parent hoverEnabled: true onEntered: {parent.opacity = 1} onExited: { if(!drag.active && !pressed) parent.opacity = 0 } onPressed:parent.opacity = 1 onPressAndHold:parent.opacity = 1 onReleased: parent.opacity = 0 drag{ target: parent; axis: Drag.XAxis } onMouseXChanged: { if(drag.active){ - newsSection.width = newsSection.width - mouseX - exampleProjects.width = exampleProjects.width + mouseX - datasetFrame.width = datasetFrame.width + mouseX - releaseSection.width = releaseSection.width + mouseX + newsSection.widthRate = (newsSection.width - mouseX) / mainWindow.width + exampleProjects.widthRate = (exampleProjects.width + mouseX) / mainWindow.width + datasetFrame.widthRate = (datasetFrame.width + mouseX) / mainWindow.width + releaseSection.widthRate = (releaseSection.width + mouseX) / mainWindow.width if(exampleProjects.width < 300 || datasetFrame.width < 300){ - newsSection.width = mainWindow.width - Math.max(recentProjectsFrame.width, helpFrame.width) - 300 - 4*mainWindow.spacing - exampleProjects.width = mainWindow.width - newsSection.width - recentProjectsFrame.width - 4*mainWindow.spacing - datasetFrame.width = mainWindow.width - newsSection.width - helpFrame.width - 4*mainWindow.spacing - releaseSection.width = Math.max(recentProjectsFrame.width, helpFrame.width) + mainWindow.spacing + 300 + newsSection.widthRate = (mainWindow.width - Math.max(recentProjectsFrame.width, helpFrame.width) - 300 - 4*mainWindow.spacing) / mainWindow.width + exampleProjects.widthRate = (mainWindow.width - newsSection.width - recentProjectsFrame.width - 4*mainWindow.spacing)/ mainWindow.width + datasetFrame.widthRate = (mainWindow.width - newsSection.width - helpFrame.width - 4*mainWindow.spacing) / mainWindow.width + releaseSection.widthRate = (Math.max(recentProjectsFrame.width, helpFrame.width) + mainWindow.spacing + 300) / mainWindow.width } if(newsSection.width < 150) { - newsSection.width = 150 - exampleProjects.width = mainWindow.width - recentProjectsFrame.width - 150 - 4*mainWindow.spacing - datasetFrame.width = mainWindow.width - helpFrame.width - 150 - 4*mainWindow.spacing - releaseSection.width = mainWindow.width - newsSection.width - 3*mainWindow.spacing + newsSection.widthRate = 150 / mainWindow.width + exampleProjects.widthRate = (mainWindow.width - recentProjectsFrame.width - 150 - 4*mainWindow.spacing) / mainWindow.width + datasetFrame.widthRate = (mainWindow.width - helpFrame.width - 150 - 4*mainWindow.spacing) / mainWindow.width + releaseSection.widthRate = (mainWindow.width - newsSection.width - 3*mainWindow.spacing) / mainWindow.width } } } } } ColumnLayout { anchors.fill: parent RowLayout { Layout.fillWidth: true Layout.minimumHeight: Math.min(parent.height*0.2, 100) Layout.preferredHeight: Math.min(parent.height*0.2, 100) Image { Layout.preferredHeight: newsSection.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) Layout.minimumHeight: newsSection.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) Layout.preferredWidth: newsSection.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) Layout.minimumWidth:newsSection.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) Layout.alignment: Qt.AlignVCenter source: newsSection.fullScreen ? helper.getMinIcon() : helper.getMaxIcon() sourceSize.width: newsSection.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) sourceSize.height: newsSection.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) MouseArea { anchors.fill: parent onClicked: { if(!newsSection.fullScreen) { hideTiles() - newsSection.prevWidth = newsSection.width - newsSection.prevHeight = newsSection.height + newsSection.prevWidth = newsSection.widthRate + newsSection.prevHeight = newsSection.heightRate newsSection.visible = true newsSection.z = 1 newsSection.anchors.fill = undefined newsSection.anchors.right = undefined newsSection.anchors.bottom = undefined newsSection.anchors.centerIn = undefined newsSection.anchors.top = undefined newsSection.anchors.left = undefined + + newsSection.widthRate = 1 + newsSection.heightRate = 1 + newsSection.anchors.fill = mainWindow } else { newsSection.anchors.fill = undefined newsSection.anchors.right = undefined newsSection.anchors.bottom = undefined newsSection.anchors.centerIn = undefined newsSection.anchors.top = undefined newsSection.anchors.left = undefined newsSection.anchors.top = mainWindow.top newsSection.anchors.topMargin = mainWindow.spacing newsSection.anchors.right = mainWindow.right newsSection.anchors.rightMargin = mainWindow.spacing newsSection.anchors.bottom = mainWindow.bottom newsSection.anchors.bottomMargin = mainWindow.spacing - newsSection.width = newsSection.prevWidth - newsSection.height = newsSection.prevHeight + newsSection.widthRate = newsSection.prevWidth + newsSection.heightRate = newsSection.prevHeight showTiles(); } newsSection.fullScreen = !newsSection.fullScreen } } } Label { id: label2 text: qsTr("News") verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter font.pointSize: newsSection.fullScreen ? 60 : 24 minimumPointSize: 10 fontSizeMode: Text.Fit Layout.fillWidth: true Layout.fillHeight: true wrapMode: Text.WordWrap } } RssNews{ id:newsFeed Layout.fillHeight: true Layout.minimumHeight: Math.min(parent.height * 0.8, parent.height - 100) } } } Frame { id: helpFrame property string sectionName: "helpFrame" - width: helper.getWidthScale(sectionName) === -1 ? mainWindow.width / 5 - 4*mainWindow.spacing : helper.getWidthScale(sectionName) - height: helper.getHeightScale(sectionName) === -1 ? mainWindow.height / 4 - 4*mainWindow.spacing : helper.getHeightScale(sectionName) + property double widthRate : helper.getWidthScale(sectionName) === -1 ? (mainWindow.width / 5 - 4*mainWindow.spacing) / mainWindow.width : helper.getWidthScale(sectionName) + property double heightRate : helper.getHeightScale(sectionName) === -1 ? (mainWindow.height / 4 - 4*mainWindow.spacing) / mainWindow.height : helper.getHeightScale(sectionName) + width: mainWindow.width * widthRate + height: mainWindow.height * heightRate + anchors.top: recentProjectsFrame.bottom anchors.topMargin: mainWindow.spacing anchors.left: parent.left anchors.leftMargin: mainWindow.spacing anchors.bottom: releaseSection.top anchors.bottomMargin: mainWindow.spacing visible: true opacity: 1 padding: 5 clip: true property bool fullScreen: false property double prevWidth: 0 property double prevHeight: 0 Component.onCompleted: { if(helper.getWidthScale(sectionName) === -1 || helper.getHeightScale(sectionName) === -1) mainWindow.restoreOriginalLayout() } Rectangle { width: 3 height: parent.height color: "gray" anchors.left: parent.right anchors.rightMargin: 0 opacity: 0 MouseArea { anchors.fill: parent hoverEnabled: true onEntered: {parent.opacity = 1} onExited: { if(!drag.active && !pressed) parent.opacity = 0 } onPressed:parent.opacity = 1 onPressAndHold:parent.opacity = 1 onReleased: parent.opacity = 0 drag{ target: parent; axis: Drag.XAxis } onMouseXChanged: { if(drag.active){ - helpFrame.width = helpFrame.width + mouseX - datasetFrame.width = datasetFrame.width - mouseX + helpFrame.widthRate = (helpFrame.width + mouseX) / mainWindow.width + datasetFrame.widthRate = (datasetFrame.width - mouseX) / mainWindow.width if(helpFrame.width < 150){ - helpFrame.width = 150 - datasetFrame.width = mainWindow.width - newsSection.width - 150 - 4*mainWindow.spacing + helpFrame.widthRate = 150 / mainWindow.width + datasetFrame.widthRate = (mainWindow.width - newsSection.width - 150 - 4*mainWindow.spacing) / mainWindow.width } if(datasetFrame.width < 150) { - datasetFrame.width = 150 - helpFrame.width = mainWindow.width - newsSection.width - 150 - 4*mainWindow.spacing + datasetFrame.widthRate = 150 / mainWindow.width + helpFrame.widthRate = (mainWindow.width - newsSection.width - 150 - 4*mainWindow.spacing) / mainWindow.width } } } } } Rectangle { height: 3 width : parent.width color: "gray" anchors.top: parent.bottom anchors.bottomMargin: 0 opacity: 0 MouseArea { anchors.fill: parent hoverEnabled: true onEntered: {parent.opacity = 1} onExited: { if(!drag.active && !pressed) parent.opacity = 0 } onPressed:parent.opacity = 1 onPressAndHold:parent.opacity = 1 onReleased: parent.opacity = 0 drag{ target: parent; axis: Drag.YAxis } onMouseYChanged: { if(drag.active){ - helpFrame.height = helpFrame.height + mouseY - datasetFrame.height = datasetFrame.height + mouseY - releaseSection.height = releaseSection.height - mouseY + helpFrame.heightRate = (helpFrame.height + mouseY) / mainWindow.height + datasetFrame.heightRate = (datasetFrame.height + mouseY) / mainWindow.height + releaseSection.heightRate = (releaseSection.height - mouseY) / mainWindow.height if(releaseSection.height < 100) { - releaseSection.height = 100 - helpFrame.height = mainWindow.height - recentProjectsFrame.height - 100 - 4*mainWindow.spacing - datasetFrame.height = mainWindow.height - recentProjectsFrame.height - 100 - 4*mainWindow.spacing + releaseSection.heightRate = 100 / mainWindow.height + helpFrame.heightRate = (mainWindow.height - recentProjectsFrame.height - 100 - 4*mainWindow.spacing) / mainWindow.height + datasetFrame.heightRate = (mainWindow.height - recentProjectsFrame.height - 100 - 4*mainWindow.spacing) / mainWindow.height } if(helpFrame.height < 100 || datasetFrame.height < 100) { - helpFrame.height = 100 - datasetFrame.height = 100 - releaseSection.height = mainWindow.height - recentProjectsFrame.height - 100 - 4*mainWindow.spacing + helpFrame.heightRate = 100 / mainWindow.height + datasetFrame.heightRate = 100 / mainWindow.height + releaseSection.heightRate = (mainWindow.height - recentProjectsFrame.height - 100 - 4*mainWindow.spacing) / mainWindow.height } } } } } Rectangle { height: 3 width : parent.width color: "gray" anchors.bottom: parent.top anchors.bottomMargin: 0 opacity: 0 MouseArea { anchors.fill: parent hoverEnabled: true onEntered: {parent.opacity = 1} onExited: { if(!drag.active && !pressed) parent.opacity = 0 } onPressed:parent.opacity = 1 onPressAndHold:parent.opacity = 1 onReleased: parent.opacity = 0 drag{ target: parent; axis: Drag.YAxis } onMouseYChanged: { if(drag.active){ - recentProjectsFrame.height = recentProjectsFrame.height + mouseY - exampleProjects.height = exampleProjects.height + mouseY - helpFrame.height = helpFrame.height - mouseY - datasetFrame.height = datasetFrame.height - mouseY + recentProjectsFrame.heightRate = (recentProjectsFrame.height + mouseY) / mainWindow.height + exampleProjects.heightRate =(exampleProjects.height + mouseY) / mainWindow.height + helpFrame.heightRate = (helpFrame.height - mouseY) / mainWindow.height + datasetFrame.heightRate = (datasetFrame.height - mouseY) / mainWindow.height if(recentProjectsFrame.height < 100 || exampleProjects.height < 100) { - recentProjectsFrame.height = 100 - exampleProjects.height = 100 - helpFrame.height = mainWindow.height - releaseSection.height - 100 - 4*mainWindow.spacing - datasetFrame.height = mainWindow.height - releaseSection.height - 100 - 4*mainWindow.spacing + recentProjectsFrame.heightRate = 100 / mainWindow.height + exampleProjects.heightRate = 100/ mainWindow.height + helpFrame.heightRate = (mainWindow.height - releaseSection.height - 100 - 4*mainWindow.spacing) / mainWindow.height + datasetFrame.heightRate = (mainWindow.height - releaseSection.height - 100 - 4*mainWindow.spacing) / mainWindow.height } if(helpFrame.height < 100 || datasetFrame.height < 100) { - helpFrame.height = 100 - datasetFrame.height = 100 - recentProjectsFrame.height = mainWindow.height - releaseSection.height - 100 - 4*mainWindow.spacing - exampleProjects.height = mainWindow.height - releaseSection.height - 100 - 4*mainWindow.spacing + helpFrame.heightRate = 100 / mainWindow.height + datasetFrame.heightRate = 100 / mainWindow.height + recentProjectsFrame.heightRate = (mainWindow.height - releaseSection.height - 100 - 4*mainWindow.spacing) / mainWindow.height + exampleProjects.heightRate = (mainWindow.height - releaseSection.height - 100 - 4*mainWindow.spacing) / mainWindow.height } } } } } ColumnLayout { anchors.fill: parent spacing: 20 clip: true RowLayout { Layout.fillWidth: true Layout.minimumHeight: Math.min((parent.height - parent.spacing) *0.2, 100) Layout.preferredHeight: Math.min((parent.height - parent.spacing) *0.2, 100) Image { Layout.preferredHeight: helpFrame.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) Layout.minimumHeight: helpFrame.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) Layout.preferredWidth: helpFrame.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) Layout.minimumWidth: helpFrame.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) Layout.alignment: Qt.AlignVCenter source: helpFrame.fullScreen ? helper.getMinIcon() : helper.getMaxIcon() sourceSize.width: helpFrame.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) sourceSize.height: helpFrame.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) MouseArea { anchors.fill: parent onClicked: { if(!helpFrame.fullScreen) { hideTiles() - helpFrame.prevWidth = helpFrame.width - helpFrame.prevHeight = helpFrame.height + helpFrame.prevWidth = helpFrame.widthRate + helpFrame.prevHeight = helpFrame.heightRate helpFrame.visible = true helpFrame.z = 1 helpFrame.anchors.fill = undefined helpFrame.anchors.right = undefined helpFrame.anchors.bottom = undefined helpFrame.anchors.centerIn = undefined helpFrame.anchors.top = undefined helpFrame.anchors.left = undefined + helpFrame.heightRate = 1 + helpFrame.widthRate = 1 helpFrame.anchors.fill = mainWindow } else { helpFrame.anchors.fill = undefined helpFrame.anchors.right = undefined helpFrame.anchors.bottom = undefined helpFrame.anchors.centerIn = undefined helpFrame.anchors.top = undefined helpFrame.anchors.left = undefined helpFrame.anchors.top = recentProjectsFrame.bottom helpFrame.anchors.topMargin = mainWindow.spacing helpFrame.anchors.left = mainWindow.left helpFrame.anchors.leftMargin = mainWindow.spacing helpFrame.anchors.bottom = releaseSection.top helpFrame.anchors.bottomMargin = mainWindow.spacing - helpFrame.width = helpFrame.prevWidth - helpFrame.height = helpFrame.prevHeight + helpFrame.widthRate = helpFrame.prevWidth + helpFrame.heightRate = helpFrame.prevHeight showTiles(); } helpFrame.fullScreen = !helpFrame.fullScreen } } } Label { text: qsTr("Help") verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter font.pointSize: helpFrame.fullScreen ? 60 : 24 minimumPointSize: 10 fontSizeMode: Text.Fit Layout.fillWidth: true Layout.fillHeight: true wrapMode: Text.WordWrap } } HelpList { id: helpList width: parent.width Layout.minimumHeight: Math.max((parent.height - parent.spacing) *0.8, parent.height - parent.spacing - 100) Layout.fillHeight: true Layout.fillWidth: true clip: true } } } Frame { id: datasetFrame property string sectionName: "datasetFrame" - width: helper.getWidthScale(sectionName) === -1 ? 3 * mainWindow.width / 5 - 4*mainWindow.spacing : helper.getWidthScale(sectionName) - height: helper.getHeightScale(sectionName) === -1 ? mainWindow.height / 4 - 4*mainWindow.spacing : helper.getHeightScale(sectionName) + property double widthRate : helper.getWidthScale(sectionName) === -1 ? (3 * mainWindow.width / 5 - 4*mainWindow.spacing) / mainWindow.width : helper.getWidthScale(sectionName) + property double heightRate : helper.getHeightScale(sectionName) === -1 ? (mainWindow.height / 4 - 4*mainWindow.spacing) / mainWindow.height : helper.getHeightScale(sectionName) + width: mainWindow.width * widthRate + height: mainWindow.height * heightRate + anchors.top: exampleProjects.bottom anchors.topMargin: mainWindow.spacing anchors.left: helpFrame.right anchors.leftMargin: mainWindow.spacing anchors.right: newsSection.left anchors.rightMargin: mainWindow.spacing anchors.bottom: releaseSection.top anchors.bottomMargin: mainWindow.spacing visible: true opacity: 1 padding: 5 clip: true property bool fullScreen: false property double prevWidth: 0 property double prevHeight: 0 Component.onCompleted: { if(helper.getWidthScale(sectionName) === -1 || helper.getHeightScale(sectionName) === -1) mainWindow.restoreOriginalLayout() } Rectangle { width: 3 height: parent.height color: "gray" anchors.left: parent.right anchors.rightMargin: 0 opacity: 0 MouseArea { anchors.fill: parent hoverEnabled: true onEntered: {parent.opacity = 1} onExited: { if(!drag.active && !pressed) parent.opacity = 0 } onPressed:parent.opacity = 1 onPressAndHold:parent.opacity = 1 onReleased: parent.opacity = 0 drag{ target: parent; axis: Drag.XAxis } onMouseXChanged: { if(drag.active){ - newsSection.width = newsSection.width - mouseX - exampleProjects.width = exampleProjects.width + mouseX - datasetFrame.width = datasetFrame.width + mouseX - releaseSection.width = releaseSection.width + mouseX + newsSection.widthRate = (newsSection.width - mouseX) / mainWindow.width + exampleProjects.widthRate = (exampleProjects.width + mouseX) / mainWindow.width + datasetFrame.widthRate = (datasetFrame.width + mouseX) / mainWindow.width + releaseSection.widthRate = (releaseSection.width + mouseX) / mainWindow.width if(exampleProjects.width < 300 || datasetFrame.width < 300){ - newsSection.width = mainWindow.width - Math.max(recentProjectsFrame.width, helpFrame.width) - 300 - 4*mainWindow.spacing - exampleProjects.width = mainWindow.width - newsSection.width - recentProjectsFrame.width - 4*mainWindow.spacing - datasetFrame.width = mainWindow.width - newsSection.width - helpFrame.width - 4*mainWindow.spacing - releaseSection.width = Math.max(recentProjectsFrame.width, helpFrame.width) + mainWindow.spacing + 300 + newsSection.widthRate = (mainWindow.width - Math.max(recentProjectsFrame.width, helpFrame.width) - 300 - 4*mainWindow.spacing) / mainWindow.width + exampleProjects.widthRate = (mainWindow.width - newsSection.width - recentProjectsFrame.width - 4*mainWindow.spacing)/ mainWindow.width + datasetFrame.widthRate = (mainWindow.width - newsSection.width - helpFrame.width - 4*mainWindow.spacing) / mainWindow.width + releaseSection.widthRate = (Math.max(recentProjectsFrame.width, helpFrame.width) + mainWindow.spacing + 300) / mainWindow.width } if(newsSection.width < 150) { - newsSection.width = 150 - exampleProjects.width = mainWindow.width - recentProjectsFrame.width - 150 - 4*mainWindow.spacing - datasetFrame.width = mainWindow.width - helpFrame.width - 150 - 4*mainWindow.spacing - releaseSection.width = mainWindow.width - newsSection.width - 3*mainWindow.spacing + newsSection.widthRate = 150 / mainWindow.width + exampleProjects.widthRate = (mainWindow.width - recentProjectsFrame.width - 150 - 4*mainWindow.spacing)/ mainWindow.width + datasetFrame.widthRate = (mainWindow.width - helpFrame.width - 150 - 4*mainWindow.spacing) / mainWindow.width + releaseSection.widthRate = (mainWindow.width - newsSection.width - 3*mainWindow.spacing) / mainWindow.width } } } } } Rectangle { height: 3 width : parent.width color: "gray" anchors.top: parent.bottom anchors.bottomMargin: 0 opacity: 0 MouseArea { anchors.fill: parent hoverEnabled: true onEntered: {parent.opacity = 1} onExited: { if(!drag.active && !pressed) parent.opacity = 0 } onPressed:parent.opacity = 1 onPressAndHold:parent.opacity = 1 onReleased: parent.opacity = 0 drag{ target: parent; axis: Drag.YAxis } onMouseYChanged: { if(drag.active){ - helpFrame.height = helpFrame.height + mouseY - datasetFrame.height = datasetFrame.height + mouseY - releaseSection.height = releaseSection.height - mouseY + helpFrame.heightRate = (helpFrame.height + mouseY) / mainWindow.height + datasetFrame.heightRate = (datasetFrame.height + mouseY) / mainWindow.height + releaseSection.heightRate = (releaseSection.height - mouseY) / mainWindow.height if(releaseSection.height < 100) { - releaseSection.height = 100 - helpFrame.height = mainWindow.height - recentProjectsFrame.height - 100 - 4*mainWindow.spacing - datasetFrame.height = mainWindow.height - recentProjectsFrame.height - 100 - 4*mainWindow.spacing + releaseSection.heightRate = 100 / mainWindow.height + helpFrame.heightRate = (mainWindow.height - recentProjectsFrame.height - 100 - 4*mainWindow.spacing) / mainWindow.height + datasetFrame.heightRate = (mainWindow.height - recentProjectsFrame.height - 100 - 4*mainWindow.spacing) / mainWindow.height } if(helpFrame.height < 100 || datasetFrame.height < 100) { - helpFrame.height = 100 - datasetFrame.height = 100 - releaseSection.height = mainWindow.height - recentProjectsFrame.height - 100 - 4*mainWindow.spacing + helpFrame.heightRate = 100 / mainWindow.height + datasetFrame.heightRate = 100 / mainWindow.height + releaseSection.heightRate = (mainWindow.height - recentProjectsFrame.height - 100 - 4*mainWindow.spacing) / mainWindow.height } } } } } Rectangle { height: 3 width : parent.width color: "gray" anchors.bottom: parent.top anchors.bottomMargin: 0 opacity: 0 MouseArea { anchors.fill: parent hoverEnabled: true onEntered: {parent.opacity = 1} onExited: { if(!drag.active && !pressed) parent.opacity = 0 } onPressed:parent.opacity = 1 onPressAndHold:parent.opacity = 1 onReleased: parent.opacity = 0 drag{ target: parent; axis: Drag.YAxis } onMouseYChanged: { if(drag.active){ - recentProjectsFrame.height = recentProjectsFrame.height + mouseY - exampleProjects.height = exampleProjects.height + mouseY - helpFrame.height = helpFrame.height - mouseY - datasetFrame.height = datasetFrame.height - mouseY + recentProjectsFrame.heightRate = (recentProjectsFrame.height + mouseY) / mainWindow.height + exampleProjects.heightRate = (exampleProjects.height + mouseY) / mainWindow.height + helpFrame.heightRate = (helpFrame.height - mouseY) / mainWindow.height + datasetFrame.heightRate = (datasetFrame.height - mouseY) / mainWindow.height if(recentProjectsFrame.height < 100 || exampleProjects.height < 100) { - recentProjectsFrame.height = 100 - exampleProjects.height = 100 - helpFrame.height = mainWindow.height - releaseSection.height - 100 - 4*mainWindow.spacing - datasetFrame.height = mainWindow.height - releaseSection.height - 100 - 4*mainWindow.spacing + recentProjectsFrame.heightRate = 100 / mainWindow.height + exampleProjects.heightRate = 100 / mainWindow.height + helpFrame.heightRate = (mainWindow.height - releaseSection.height - 100 - 4*mainWindow.spacing) / mainWindow.height + datasetFrame.heightRate = (mainWindow.height - releaseSection.height - 100 - 4*mainWindow.spacing) / mainWindow.height } if(helpFrame.height < 100 || datasetFrame.height < 100) { - helpFrame.height = 100 - datasetFrame.height = 100 - recentProjectsFrame.height = mainWindow.height - releaseSection.height - 100 - 4*mainWindow.spacing - exampleProjects.height = mainWindow.height - releaseSection.height - 100 - 4*mainWindow.spacing + helpFrame.heightRate = 100 / mainWindow.height + datasetFrame.heightRate = 100 / mainWindow.height + recentProjectsFrame.heightRate = (mainWindow.height - releaseSection.height - 100 - 4*mainWindow.spacing) / mainWindow.height + exampleProjects.heightRate = (mainWindow.height - releaseSection.height - 100 - 4*mainWindow.spacing) / mainWindow.height } } } } } Rectangle { width: 3 height: parent.height color: "gray" anchors.right: parent.left anchors.leftMargin: 0 opacity: 0 MouseArea { anchors.fill: parent hoverEnabled: true onEntered: {parent.opacity = 1} onExited: { if(!drag.active && !pressed) parent.opacity = 0 } onPressed:parent.opacity = 1 onPressAndHold:parent.opacity = 1 onReleased: parent.opacity = 0 drag{ target: parent; axis: Drag.XAxis } onMouseXChanged: { if(drag.active){ - helpFrame.width = helpFrame.width + mouseX - datasetFrame.width = datasetFrame.width - mouseX + helpFrame.widthRate = (helpFrame.width + mouseX) / mainWindow.width + datasetFrame.widthRate = (datasetFrame.width - mouseX) / mainWindow.width if(helpFrame.width < 150){ - helpFrame.width = 150 - datasetFrame.width = mainWindow.width - newsSection.width - 150 - 4*mainWindow.spacing + helpFrame.widthRate = 150 / mainWindow.width + datasetFrame.widthRate = (mainWindow.width - newsSection.width - 150 - 4*mainWindow.spacing) / mainWindow.width } if(datasetFrame.width < 150) { - datasetFrame.width = 150 - helpFrame.width = mainWindow.width - newsSection.width - 150 - 4*mainWindow.spacing + datasetFrame.widthRate = 150 / mainWindow.width + helpFrame.widthRate = (mainWindow.width - newsSection.width - 150 - 4*mainWindow.spacing) / mainWindow.width } } } } } ColumnLayout { anchors.fill: parent clip: true spacing: 20 RowLayout { Layout.fillWidth: true Layout.minimumHeight: Math.min((parent.height - parent.spacing) *0.2, 100) Layout.preferredHeight: Math.min((parent.height - parent.spacing) *0.2, 100) Image { Layout.preferredHeight: datasetFrame.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) Layout.minimumHeight: datasetFrame.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) Layout.preferredWidth: datasetFrame.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) Layout.minimumWidth: datasetFrame.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) Layout.alignment: Qt.AlignVCenter source: datasetFrame.fullScreen ? helper.getMinIcon() : helper.getMaxIcon() sourceSize.width: datasetFrame.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) sourceSize.height: datasetFrame.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) MouseArea { anchors.fill: parent onClicked: { if(!datasetFrame.fullScreen) { hideTiles() - datasetFrame.prevWidth = datasetFrame.width - datasetFrame.prevHeight = datasetFrame.height + datasetFrame.prevWidth = datasetFrame.widthRate + datasetFrame.prevHeight = datasetFrame.heightRate datasetFrame.visible = true datasetFrame.z = 1 datasetFrame.anchors.fill = undefined datasetFrame.anchors.right = undefined datasetFrame.anchors.bottom = undefined datasetFrame.anchors.centerIn = undefined datasetFrame.anchors.top = undefined datasetFrame.anchors.left = undefined + datasetFrame.widthRate = 1 + datasetFrame.heightRate = 1 datasetFrame.anchors.fill = mainWindow } else { datasetFrame.anchors.fill = undefined datasetFrame.anchors.right = undefined datasetFrame.anchors.bottom = undefined datasetFrame.anchors.centerIn = undefined datasetFrame.anchors.top = undefined datasetFrame.anchors.left = undefined datasetFrame.anchors.top = exampleProjects.bottom datasetFrame.anchors.topMargin = mainWindow.spacing datasetFrame.anchors.left = helpFrame.right datasetFrame.anchors.leftMargin = mainWindow.spacing datasetFrame.anchors.bottom = releaseSection.top datasetFrame.anchors.bottomMargin = mainWindow.spacing datasetFrame.anchors.right = newsSection.left datasetFrame.anchors.rightMargin = mainWindow.spacing - datasetFrame.width = datasetFrame.prevWidth - datasetFrame.height = datasetFrame.prevHeight + datasetFrame.widthRate = datasetFrame.prevWidth + datasetFrame.heightRate = datasetFrame.prevHeight showTiles(); } datasetFrame.fullScreen = !datasetFrame.fullScreen } } } Label { text: qsTr("Start exploring data") verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter font.pointSize: datasetFrame.fullScreen ? 60 : 24 minimumPointSize: 10 fontSizeMode: Text.Fit Layout.fillWidth: true Layout.fillHeight: true wrapMode: Text.WordWrap } } RowLayout { id: datasetSectionRow Layout.fillHeight: true Layout.minimumHeight: Math.max((parent.height - parent.spacing) *0.8, parent.height - parent.spacing - 100) Layout.fillWidth: true Layout.preferredWidth: parent.width Layout.minimumWidth: parent.width spacing: 10 clip: true property int separatorWidth: 5 ListView { id: categoryList Layout.preferredWidth: (parent.width - 3 * datasetSectionRow.separatorWidth - 6*datasetSectionRow.spacing) * 0.15 Layout.minimumWidth: (parent.width - 3 * datasetSectionRow.separatorWidth - 6*datasetSectionRow.spacing) * 0.15 //width: textWidth // implicitWidth: textWidth spacing: 10 Layout.fillHeight: true property int textWidth: 100 clip: true ScrollBar.vertical: ScrollBar { } Component.onCompleted: console.log("Model: " + datasetModel.allCategories()) model: datasetModel.allCategories() delegate:Rectangle { width: parent.width height: 25 id: categoryDelegate property string categoryName : modelData property bool selected: ListView.isCurrentItem RowLayout { id: categoryRow spacing: 10 //Layout.fillHeight: true //Layout.fillWidth: true anchors.fill: parent Rectangle { id: categoryBullet anchors.verticalCenter: parent.verticalCenter width: 5 height: 5 color: "#7a7d82" } Label { height: parent.height width: parent.width - 5 - categoryRow.spacing Layout.minimumWidth: parent.width - 5 - categoryRow.spacing Layout.preferredWidth: parent.width - 5 - categoryRow.spacing Layout.fillHeight: true id: categoryLabel verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter text: categoryDelegate.categoryName font.bold: true wrapMode: Text.WordWrap font.pixelSize: 18 minimumPixelSize: 1 fontSizeMode: Text.Fit color: selected ? "#d69f00" : "#000000" scale: selected ? 1.15 : 1.0 Behavior on color { ColorAnimation { duration: 150 } } Behavior on scale { PropertyAnimation { duration: 300 } } Component.onCompleted: { console.log("Category name: " + categoryDelegate.categoryName) if(index == 0) { mainWindow.currentCategory = categoryDelegate.categoryName categoryDelegate.ListView.view.currentIndex = index console.log("Set current categ: " + mainWindow.currentCategory) } console.log("Category size: " + (paintedWidth + categoryBullet.width + categoryRow.spacing)) if(categoryList.textWidth < paintedWidth + categoryBullet.width + categoryRow.spacing) categoryList.textWidth = paintedWidth + categoryBullet.width + categoryRow.spacing } } } MouseArea { anchors.fill: parent onClicked: { console.log("Category name: " + categoryDelegate.categoryName + "Clicked") categoryDelegate.ListView.view.currentIndex = index if (mainWindow.currentCategory != categoryName) mainWindow.currentCategory = categoryName console.log("Category size: " + (categoryLabel.paintedWidth + categoryBullet.width + categoryRow.spacing)) if(categoryList.textWidth < categoryLabel.paintedWidth + categoryBullet.width + categoryRow.spacing) categoryList.textWidth = categoryLabel.paintedWidth + categoryBullet.width + categoryRow.spacing } } } } Rectangle { Layout.fillHeight: true width: 5 color: "grey" } ListView { id: subcategoryList spacing: 10 Layout.fillHeight: true Layout.preferredWidth: (parent.width - 3 * datasetSectionRow.separatorWidth - 6*datasetSectionRow.spacing) * 0.15 Layout.minimumWidth: (parent.width - 3 * datasetSectionRow.separatorWidth - 6*datasetSectionRow.spacing) * 0.15 ScrollBar.vertical: ScrollBar{} clip: true property int textWidth: 100 model: datasetModel.allSubcategories(mainWindow.currentCategory) delegate: Rectangle { width: parent.width height: 25 id: subcategoryDelegate property string subcategoryName : modelData property bool selected: ListView.isCurrentItem RowLayout { id: subcategoryRow spacing: 10 anchors.fill: parent Rectangle { id: subcategoryBullet anchors.verticalCenter: parent.verticalCenter width: 5 height: 5 color: "#7a7d82" } Label { height: parent.height width: parent.width - 5 - subcategoryRow.spacing Layout.minimumWidth: parent.width - 5 - subcategoryRow.spacing Layout.preferredWidth: parent.width - 5 - subcategoryRow.spacing Layout.fillHeight: true verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter text: subcategoryDelegate.subcategoryName wrapMode: Text.WordWrap font.bold: true font.pixelSize: 18 minimumPixelSize: 1 fontSizeMode: Text.Fit color: selected ? "#d69f00" : "#000000" scale: selected ? 1.15 : 1.0 Behavior on color { ColorAnimation { duration: 150 } } Behavior on scale { PropertyAnimation { duration: 300 } } Component.onCompleted: { console.log("Subcategory name: " + subcategoryDelegate.subcategoryName) if(index == 0) { mainWindow.currentSubcategory = subcategoryDelegate.subcategoryName subcategoryDelegate.ListView.view.currentIndex = index } console.log("Subcategory size: " + (paintedWidth + subcategoryBullet.width + subcategoryRow.spacing)) if(subcategoryList.textWidth < paintedWidth + subcategoryBullet.width + subcategoryRow.spacing) { subcategoryList.textWidth = paintedWidth + subcategoryBullet.width + subcategoryRow.spacing console.log("Subcategory size bigger, new value: " + subcategoryList.textWidth + " " + subcategoryList.width) } } } } MouseArea { anchors.fill: parent onClicked: { console.log("Subcategory name: " + subcategoryDelegate.subcategoryName + "Clicked") subcategoryDelegate.ListView.view.currentIndex = index if (mainWindow.currentSubcategory != subcategoryName) mainWindow.currentSubcategory = subcategoryName } } } } Rectangle { Layout.fillHeight: true width: 5 color: "grey" } GridView { id: datasetGrid Layout.fillHeight: true Layout.preferredWidth: (parent.width - 3 * datasetSectionRow.separatorWidth - 6*datasetSectionRow.spacing) * 0.4 Layout.minimumWidth: (parent.width - 3 * datasetSectionRow.separatorWidth - 6*datasetSectionRow.spacing) * 0.4 cellWidth: width/4 cellHeight: 40 clip: true model: datasetModel.allDatasets(mainWindow.currentCategory, mainWindow.currentSubcategory) delegate: Rectangle { id: datasetDelegate property string datasetName : modelData property bool selected: (index == datasetGrid.currentIndex) width: (datasetGrid.width - 30) / 4 height: textHeight property int textWidth: 200 property int textHeight: 40 RowLayout { id: datasetRow spacing: 10 anchors.fill: parent Rectangle { id: datasetBullet width: 5 height: 5 color: "#7a7d82" } Label { id: datasetText height: parent.height width: parent.width - 5 - datasetRow.spacing Layout.minimumWidth: parent.width - 5 - datasetRow.spacing Layout.preferredWidth: parent.width - 5 - datasetRow.spacing Layout.fillHeight: true verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter text: datasetDelegate.datasetName wrapMode: Text.WordWrap font.bold: true font.pixelSize: 18 minimumPixelSize: 1 fontSizeMode: Text.Fit color: selected ? "#d69f00" : "#000000" scale: selected ? 1.15 : 1.0 Behavior on color { ColorAnimation { duration: 150 } } Behavior on scale { PropertyAnimation { duration: 300 } } Component.onCompleted: { console.log("Dataset name: " + datasetDelegate.datasetName) datasetDelegate.textHeight = paintedHeight datasetDelegate.textWidth = paintedWidth + datasetBullet.width + datasetRow.spacing if(index == 0) { console.log("index 0 " + mainWindow.currentCategory +" subcat: " + mainWindow.currentSubcategory + " dtaset " +datasetDelegate.datasetName); datasetGrid.currentIndex = index mainWindow.currentDataset = datasetDelegate.datasetName mainWindow.datasetClicked(mainWindow.currentCategory, mainWindow.currentSubcategory, datasetDelegate.datasetName) } } } } MouseArea { anchors.fill: parent onClicked: { datasetGrid.currentIndex = index console.log("Dataset name: " + datasetDelegate.datasetName + "Clicked") mainWindow.currentDataset = datasetDelegate.datasetName mainWindow.datasetClicked(mainWindow.currentCategory, mainWindow.currentSubcategory, datasetDelegate.datasetName) } } Component.onCompleted: { console.log("current index: " + datasetGrid.currentIndex + " index " + index) } } Component.onCompleted: { console.log("GridView completed") } } Rectangle { Layout.fillHeight: true width: 5 color: "grey" } ScrollView { id: scrollView Layout.fillHeight: true Layout.preferredWidth: (parent.width - 3 * datasetSectionRow.separatorWidth - 6*datasetSectionRow.spacing) * 0.3 Layout.minimumWidth: (parent.width - 3 * datasetSectionRow.separatorWidth - 6*datasetSectionRow.spacing) * 0.3 contentHeight: datasetDescriptionColumn.height clip: true ColumnLayout { id: datasetDescriptionColumn //anchors.fill: parent Layout.minimumWidth: scrollView.width // datasetFrame.width *0.3 Layout.preferredWidth: scrollView.width //datasetFrame.width *0.3 width: scrollView.width //datasetFrame.width *0.3 spacing: 10 Row { width: datasetDescriptionColumn.width spacing: 5 Text { id: datasetTitleLabel text: "Full name: " font.pixelSize: 14 font.bold: true wrapMode: Text.WordWrap } Text { id: datasetTitle //Layout.fillWidth: true width: datasetDescriptionColumn.width - datasetTitleLabel.paintedWidth text: "-" wrapMode: Text.WordWrap font.pixelSize: 14 } } Row { width: parent.width height: Math.max(datasetDescription.textHeight, datasetDescriptionLabel.paintedHeight) spacing: 5 Text { id: datasetDescriptionLabel text: "Description: " font.pixelSize: 14 font.bold: true wrapMode: Text.WordWrap } Text { id: datasetDescription property double textHeight: paintedHeight text: "" width: datasetDescriptionColumn.width - datasetDescriptionLabel.paintedWidth Layout.preferredWidth: datasetDescriptionColumn.width - datasetDescriptionLabel.paintedWidth Layout.minimumWidth: datasetDescriptionColumn.width - datasetDescriptionLabel.paintedWidth wrapMode: Text.WordWrap font.pixelSize: 12 Component.onCompleted:{ console.log("First Description width: " + width + " " + (datasetDescriptionColumn.width - datasetDescriptionLabel.paintedWidth)) console.log("First Description height: " + paintedHeight + " " + parent.height) } } } Row { width: parent.width spacing: 5 Text { id: datasetColumnsLabel text: "Columns: " font.pixelSize: 14 font.bold: true wrapMode: Text.WordWrap } Text { id: datasetColumns text: "-" width: datasetDescriptionColumn.width - datasetColumnsLabel.paintedWidth wrapMode: Text.WordWrap font.pixelSize: 14 } } Row { width: parent.width spacing: 5 Text { id: datasetRowsLabel text: "Rows: " font.pixelSize: 14 font.bold: true wrapMode: Text.WordWrap } Text { id: datasetRows text: "-" width: datasetDescriptionColumn.width - datasetRowsLabel.paintedWidth wrapMode: Text.WordWrap font.pixelSize: 14 } } Rectangle { width: datasetButtonText.paintedWidth + 10 height: datasetButtonText.paintedHeight + 10 Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom color:'#dfe3ee' Text { id: datasetButtonText text: "Open dataset" horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter font.pixelSize: 14 font.bold: true wrapMode: Text.WordWrap } MouseArea { anchors.fill: parent onClicked: { mainWindow.openDataset() } } } Component.onCompleted: console.log("Width of last column: " + datasetFrame.width / 5 + " " + width) } } } } } Frame { id: releaseSection property string sectionName: "releaseSection" - width: helper.getWidthScale(sectionName) === -1? 4 * mainWindow.width / 5 - 4*mainWindow.spacing : helper.getWidthScale(sectionName) - height: helper.getHeightScale(sectionName) === -1 ? 2*mainWindow.height / 4 - 4*mainWindow.spacing : helper.getHeightScale(sectionName) + property double widthRate : helper.getWidthScale(sectionName) === -1 ? (4 * mainWindow.width / 5 - 4*mainWindow.spacing) / mainWindow.width : helper.getWidthScale(sectionName) + property double heightRate : helper.getHeightScale(sectionName) === -1 ? (2*mainWindow.height / 4 - 4*mainWindow.spacing) / mainWindow.height : helper.getHeightScale(sectionName) + width: mainWindow.width * widthRate + height: mainWindow.height * heightRate + anchors.left: parent.left anchors.leftMargin: mainWindow.spacing anchors.right: newsSection.left anchors.rightMargin: mainWindow.spacing anchors.bottom: parent.bottom anchors.bottomMargin: mainWindow.spacing visible: true opacity: 1 padding: 5 clip: true property bool fullScreen: false property double prevWidth: 0 property double prevHeight: 0 Component.onCompleted: { if(helper.getWidthScale(sectionName) === -1 || helper.getHeightScale(sectionName) === -1) mainWindow.restoreOriginalLayout() } Rectangle { width: 3 height: parent.height color: "gray" anchors.left: parent.right anchors.rightMargin: 0 opacity: 0 MouseArea { anchors.fill: parent hoverEnabled: true onEntered: {parent.opacity = 1} onExited: { if(!drag.active && !pressed) parent.opacity = 0 } onPressed:parent.opacity = 1 onPressAndHold:parent.opacity = 1 onReleased: parent.opacity = 0 drag{ target: parent; axis: Drag.XAxis } onMouseXChanged: { if(drag.active){ - newsSection.width = newsSection.width - mouseX - exampleProjects.width = exampleProjects.width + mouseX - datasetFrame.width = datasetFrame.width + mouseX - releaseSection.width = releaseSection.width + mouseX + newsSection.widthRate = (newsSection.width - mouseX) / mainWindow.width + exampleProjects.widthRate = (exampleProjects.width + mouseX) / mainWindow.width + datasetFrame.widthRate = (datasetFrame.width + mouseX) / mainWindow.width + releaseSection.widthRate = (releaseSection.width + mouseX) / mainWindow.width if(exampleProjects.width < 300 || datasetFrame.width < 300){ - newsSection.width = mainWindow.width - Math.max(recentProjectsFrame.width, helpFrame.width) - 300 - 4*mainWindow.spacing - exampleProjects.width = mainWindow.width - newsSection.width - recentProjectsFrame.width - 4*mainWindow.spacing - datasetFrame.width = mainWindow.width - newsSection.width - helpFrame.width - 4*mainWindow.spacing - releaseSection.width = Math.max(recentProjectsFrame.width, helpFrame.width) + mainWindow.spacing + 300 + newsSection.widthRate = (mainWindow.width - Math.max(recentProjectsFrame.width, helpFrame.width) - 300 - 4*mainWindow.spacing) / mainWindow.width + exampleProjects.widthRate = (mainWindow.width - newsSection.width - recentProjectsFrame.width - 4*mainWindow.spacing) / mainWindow.width + datasetFrame.widthRate = (mainWindow.width - newsSection.width - helpFrame.width - 4*mainWindow.spacing) / mainWindow.width + releaseSection.widthRate = (Math.max(recentProjectsFrame.width, helpFrame.width) + mainWindow.spacing + 300) / mainWindow.width } if(newsSection.width < 150) { - newsSection.width = 150 - exampleProjects.width = mainWindow.width - recentProjectsFrame.width - 150 - 4*mainWindow.spacing - datasetFrame.width = mainWindow.width - helpFrame.width - 150 - 4*mainWindow.spacing - releaseSection.width = mainWindow.width - newsSection.width - 3*mainWindow.spacing + newsSection.width = 150 / mainWindow.width + exampleProjects.widthRate = (mainWindow.width - recentProjectsFrame.width - 150 - 4*mainWindow.spacing) / mainWindow.width + datasetFrame.widthRate = (mainWindow.width - helpFrame.width - 150 - 4*mainWindow.spacing) / mainWindow.width + releaseSection.widthRate = (mainWindow.width - newsSection.width - 3*mainWindow.spacing) / mainWindow.width } } } } } Rectangle { height: 3 width : parent.width color: "gray" anchors.bottom: parent.top anchors.bottomMargin: 0 opacity: 0 MouseArea { anchors.fill: parent hoverEnabled: true onEntered: {parent.opacity = 1} onExited: { if(!drag.active && !pressed) parent.opacity = 0 } onPressed:parent.opacity = 1 onPressAndHold:parent.opacity = 1 onReleased: parent.opacity = 0 drag{ target: parent; axis: Drag.YAxis } onMouseYChanged: { if(drag.active){ - helpFrame.height = helpFrame.height + mouseY - datasetFrame.height = datasetFrame.height + mouseY - releaseSection.height = releaseSection.height - mouseY + helpFrame.heightRate = (helpFrame.height + mouseY) / mainWindow.height + datasetFrame.heightRate = (datasetFrame.height + mouseY) / mainWindow.height + releaseSection.heightRate = (releaseSection.height - mouseY) / mainWindow.height if(releaseSection.height < 100) { - releaseSection.height = 100 - helpFrame.height = mainWindow.height - recentProjectsFrame.height - 100 - 4*mainWindow.spacing - datasetFrame.height = mainWindow.height - recentProjectsFrame.height - 100 - 4*mainWindow.spacing + releaseSection.heightRate = 100 / mainWindow.height + helpFrame.heightRate = (mainWindow.height - recentProjectsFrame.height - 100 - 4*mainWindow.spacing) / mainWindow.height + datasetFrame.heightRate = (mainWindow.height - recentProjectsFrame.height - 100 - 4*mainWindow.spacing) / mainWindow.height } if(helpFrame.height < 100 || datasetFrame.height < 100) { - helpFrame.height = 100 - datasetFrame.height = 100 - releaseSection.height = mainWindow.height - recentProjectsFrame.height - 100 - 4*mainWindow.spacing + helpFrame.heightRate = 100 / mainWindow.height + datasetFrame.heightRate = 100 / mainWindow.height + releaseSection.heightRate = (mainWindow.height - recentProjectsFrame.height - 100 - 4*mainWindow.spacing) / mainWindow.height } } } } } ColumnLayout { anchors.fill: parent clip: true spacing: 10 RowLayout { Layout.fillWidth: true Layout.minimumHeight: Math.min((parent.height - parent.spacing) *0.2, 100) Layout.preferredHeight: Math.min((parent.height - parent.spacing) *0.2, 100) Image { Layout.preferredHeight: releaseSection.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) Layout.minimumHeight: releaseSection.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) Layout.preferredWidth: releaseSection.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) Layout.minimumWidth: releaseSection.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) Layout.alignment: Qt.AlignVCenter source: releaseSection.fullScreen ? helper.getMinIcon() : helper.getMaxIcon() sourceSize.width: releaseSection.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) sourceSize.height: releaseSection.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) MouseArea { anchors.fill: parent onClicked: { if(!releaseSection.fullScreen) { hideTiles() - releaseSection.prevWidth = releaseSection.width - releaseSection.prevHeight = releaseSection.height + releaseSection.prevWidth = releaseSection.widthRate + releaseSection.prevHeight = releaseSection.heightRate releaseSection.visible = true releaseSection.z = 1 releaseSection.anchors.fill = undefined releaseSection.anchors.right = undefined releaseSection.anchors.bottom = undefined releaseSection.anchors.centerIn = undefined releaseSection.anchors.top = undefined releaseSection.anchors.left = undefined + releaseSection.widthRate = 1 + releaseSection.heightRate = 1 releaseSection.anchors.fill = mainWindow } else { releaseSection.anchors.fill = undefined releaseSection.anchors.right = undefined releaseSection.anchors.bottom = undefined releaseSection.anchors.centerIn = undefined releaseSection.anchors.top = undefined releaseSection.anchors.left = undefined releaseSection.anchors.left = mainWindow.left releaseSection.anchors.leftMargin = mainWindow.spacing releaseSection.anchors.bottom = mainWindow.bottom releaseSection.anchors.bottomMargin = mainWindow.spacing releaseSection.anchors.right = newsSection.left releaseSection.anchors.rightMargin = mainWindow.spacing - releaseSection.width = releaseSection.prevWidth - releaseSection.height = releaseSection.prevHeight + releaseSection.widthRate = releaseSection.prevWidth + releaseSection.heightRate = releaseSection.prevHeight showTiles(); } releaseSection.fullScreen = !releaseSection.fullScreen } } } Image { Layout.preferredHeight: releaseSection.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) Layout.minimumHeight: releaseSection.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) Layout.preferredWidth: releaseSection.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) Layout.minimumWidth: releaseSection.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) Layout.alignment: Qt.AlignVCenter source: helper.getBackIcon() sourceSize.width: releaseSection.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) sourceSize.height: releaseSection.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) MouseArea { anchors.fill: parent onClicked: { releaseWebView.goBack() } } } Image { Layout.preferredHeight: releaseSection.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) Layout.minimumHeight: releaseSection.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) Layout.preferredWidth: releaseSection.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) Layout.minimumWidth: releaseSection.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) Layout.alignment: Qt.AlignVCenter source: helper.getForwardIcon() sourceSize.width: releaseSection.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) sourceSize.height: releaseSection.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 35) : Math.min(Math.min(parent.height, parent.width) * 0.5, 25) MouseArea { anchors.fill: parent onClicked: { releaseWebView.goForward() } } } Label { text: qsTr("What's new in this release") verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter font.pointSize:releaseSection.fullScreen ? 60 : 24 minimumPointSize: 10 fontSizeMode: Text.Fit Layout.fillWidth: true Layout.fillHeight: true wrapMode: Text.WordWrap } } WebView { id: releaseWebView Layout.fillHeight: true Layout.minimumHeight: Math.max((parent.height - parent.spacing) *0.8, parent.height - parent.spacing - 100) Layout.preferredHeight: Math.max((parent.height - parent.spacing) *0.8, parent.height - parent.spacing - 100) Layout.fillWidth: true url: initialUrl } } } }