diff --git a/src/kdefrontend/MainWin.cpp b/src/kdefrontend/MainWin.cpp index 968fd0e4d..0f80ebfd2 100644 --- a/src/kdefrontend/MainWin.cpp +++ b/src/kdefrontend/MainWin.cpp @@ -1,2306 +1,2294 @@ /*************************************************************************** File : MainWin.cc Project : LabPlot Description : Main window of the application -------------------------------------------------------------------- Copyright : (C) 2008-2018 Stefan Gerlach (stefan.gerlach@uni.kn) Copyright : (C) 2009-2018 Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "MainWin.h" #include "backend/core/Project.h" #include "backend/core/Folder.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/Workbook.h" #include "backend/spreadsheet/Spreadsheet.h" #include "backend/matrix/Matrix.h" #include "backend/worksheet/Worksheet.h" #include "backend/datasources/LiveDataSource.h" #include "backend/datasources/DatasetHandler.h" #ifdef HAVE_LIBORIGIN #include "backend/datasources/projects/OriginProjectParser.h" #endif #ifdef HAVE_CANTOR_LIBS #include "backend/cantorWorksheet/CantorWorksheet.h" #endif #include "backend/datapicker/Datapicker.h" #include "backend/note/Note.h" #include "backend/lib/macros.h" #include "backend/worksheet/plots/cartesian/CartesianPlot.h" #ifdef HAVE_MQTT #include "backend/datasources/MQTTClient.h" #endif #include "commonfrontend/core/PartMdiView.h" #include "commonfrontend/ProjectExplorer.h" #include "commonfrontend/matrix/MatrixView.h" #include "commonfrontend/spreadsheet/SpreadsheetView.h" #include "commonfrontend/worksheet/WorksheetView.h" #ifdef HAVE_CANTOR_LIBS #include "commonfrontend/cantorWorksheet/CantorWorksheetView.h" #endif #include "commonfrontend/datapicker/DatapickerView.h" #include "commonfrontend/datapicker/DatapickerImageView.h" #include "commonfrontend/note/NoteView.h" #include "commonfrontend/widgets/MemoryWidget.h" #include "kdefrontend/datasources/ImportFileDialog.h" #include "kdefrontend/datasources/ImportDatasetDialog.h" #include "kdefrontend/datasources/ImportDatasetWidget.h" #include "kdefrontend/datasources/ImportProjectDialog.h" #include "kdefrontend/datasources/ImportSQLDatabaseDialog.h" #include #include "kdefrontend/dockwidgets/ProjectDock.h" #include "kdefrontend/HistoryDialog.h" #include "kdefrontend/SettingsDialog.h" #include "kdefrontend/GuiObserver.h" #include "kdefrontend/widgets/FITSHeaderEditDialog.h" #include "DatasetModel.h" #include "WelcomeScreenHelper.h" #ifdef Q_OS_MAC #include "3rdparty/kdmactouchbar/src/kdmactouchbar.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_CANTOR_LIBS #include #include #include #include #endif /*! \class MainWin \brief Main application window. \ingroup kdefrontend */ MainWin::MainWin(QWidget *parent, const QString& filename) : KXmlGuiWindow(parent) { initGUI(filename); setAcceptDrops(true); //restore the geometry KConfigGroup group = KSharedConfig::openConfig()->group("MainWin"); restoreGeometry(group.readEntry("geometry", QByteArray())); } MainWin::~MainWin() { //save the recent opened files m_recentProjectsAction->saveEntries( KSharedConfig::openConfig()->group("Recent Files") ); KConfigGroup group = KSharedConfig::openConfig()->group("MainWin"); group.writeEntry("geometry", saveGeometry()); KSharedConfig::openConfig()->sync(); qDebug() << "Mainwin Destructor "; if(dynamic_cast(centralWidget()) != nullptr) { qDebug() << "Destructor save welcome screen"; QMetaObject::invokeMethod(m_welcomeWidget->rootObject(), "saveWidgetDimensions"); } if (m_project != nullptr) { if(dynamic_cast(centralWidget()) == nullptr) m_mdiArea->closeAllSubWindows(); disconnect(m_project, nullptr, this, nullptr); delete m_project; } if (m_aspectTreeModel) delete m_aspectTreeModel; if (m_guiObserver) delete m_guiObserver; if(m_welcomeScreenHelper) delete m_welcomeScreenHelper; } void MainWin::showPresenter() { const Worksheet* w = dynamic_cast(m_currentAspect); if (w) { auto* view = dynamic_cast(w->view()); view->presenterMode(); } else { //currently active object is not a worksheet but we're asked to start in the presenter mode //determine the first available worksheet and show it in the presenter mode QVector worksheets = m_project->children(); if (worksheets.size() > 0) { auto* view = qobject_cast(worksheets.first()->view()); view->presenterMode(); } else { QMessageBox::information(this, i18n("Presenter Mode"), i18n("No worksheets are available in the project. The presenter mode will not be started.")); } } } AspectTreeModel* MainWin::model() const { return m_aspectTreeModel; } Project* MainWin::project() const { return m_project; } void MainWin::initGUI(const QString& fileName) { m_mdiArea = new QMdiArea; setCentralWidget(m_mdiArea); connect(m_mdiArea, &QMdiArea::subWindowActivated, this, &MainWin::handleCurrentSubWindowChanged); statusBar()->showMessage(i18nc("%1 is the LabPlot version", "Welcome to LabPlot %1", QLatin1String(LVERSION))); initActions(); #ifdef Q_OS_MAC setupGUI(Default, QLatin1String("/Applications/labplot2.app/Contents/Resources/labplot2ui.rc")); m_touchBar = new KDMacTouchBar(this); //m_touchBar->setTouchButtonStyle(KDMacTouchBar::IconOnly); #else setupGUI(Default, KXMLGUIClient::xmlFile()); // should be "labplot2ui.rc" #endif DEBUG("Component name: " << KXMLGUIClient::componentName().toStdString()); DEBUG("XML file: " << KXMLGUIClient::xmlFile().toStdString() << " (should be \"labplot2ui.rc\")"); //all toolbars created via the KXMLGUI framework are locked on default: // * on the very first program start, unlock all toolbars // * on later program starts, set stored lock status //Furthermore, we want to show icons only after the first program start. KConfigGroup groupMain = KSharedConfig::openConfig()->group("MainWindow"); if (groupMain.exists()) { //KXMLGUI framework automatically stores "Disabled" for the key "ToolBarsMovable" //in case the toolbars are locked -> load this value const QString& str = groupMain.readEntry(QLatin1String("ToolBarsMovable"), ""); bool locked = (str == QLatin1String("Disabled")); KToolBar::setToolBarsLocked(locked); } else { //first start KToolBar::setToolBarsLocked(false); //show icons only for (auto* container : factory()->containers(QLatin1String("ToolBar"))) { auto* toolbar = dynamic_cast(container); if (toolbar) toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly); } } initMenus(); auto* mainToolBar = qobject_cast(factory()->container("main_toolbar", this)); if (!mainToolBar) { QMessageBox::critical(this, i18n("GUI configuration file not found"), i18n("%1 file was not found. Please check your installation.", KXMLGUIClient::xmlFile())); //TODO: the application is not really usable if the rc file was not found. We should quit the application. The following line crashes //the application because of the splash screen. We need to find another solution. // QMetaObject::invokeMethod(this, "close", Qt::QueuedConnection); //call close as soon as we enter the eventloop return; } auto* tbImport = new QToolButton(mainToolBar); tbImport->setPopupMode(QToolButton::MenuButtonPopup); tbImport->setMenu(m_importMenu); tbImport->setDefaultAction(m_importFileAction); mainToolBar->addWidget(tbImport); qobject_cast(factory()->container("import", this))->setIcon(QIcon::fromTheme("document-import")); setWindowIcon(QIcon::fromTheme("LabPlot2", QGuiApplication::windowIcon())); setAttribute( Qt::WA_DeleteOnClose ); //make the status bar of a fixed size in order to avoid height changes when placing a ProgressBar there. QFont font; font.setFamily(font.defaultFamily()); QFontMetrics fm(font); statusBar()->setFixedHeight(fm.height() + 5); //load recently used projects m_recentProjectsAction->loadEntries( KSharedConfig::openConfig()->group("Recent Files") ); //set the view mode of the mdi area KConfigGroup group = KSharedConfig::openConfig()->group( "Settings_General" ); int viewMode = group.readEntry("ViewMode", 0); if (viewMode == 1) { m_mdiArea->setViewMode(QMdiArea::TabbedView); int tabPosition = group.readEntry("TabPosition", 0); m_mdiArea->setTabPosition(QTabWidget::TabPosition(tabPosition)); m_mdiArea->setTabsClosable(true); m_mdiArea->setTabsMovable(true); m_tileWindows->setVisible(false); m_cascadeWindows->setVisible(false); } //auto-save m_autoSaveActive = group.readEntry("AutoSave", false); int interval = group.readEntry("AutoSaveInterval", 1); interval = interval*60*1000; m_autoSaveTimer.setInterval(interval); connect(&m_autoSaveTimer, &QTimer::timeout, this, &MainWin::autoSaveProject); if (!fileName.isEmpty()) { #ifdef HAVE_LIBORIGIN if (Project::isLabPlotProject(fileName) || OriginProjectParser::isOriginProject(fileName)) { #else if (Project::isLabPlotProject(fileName)) { #endif QTimer::singleShot(0, this, [=] () { openProject(fileName); }); } else { newProject(); QTimer::singleShot(0, this, [=] () { importFileDialog(fileName); }); } } else { //There is no file to open. Depending on the settings do nothing, //create a new project or open the last used project. int load = group.readEntry("LoadOnStart", 0); if (load == 1) //create new project newProject(); else if (load == 2) { //create new project with a worksheet newProject(); newWorksheet(); } else if (load == 3) { //open last used project if (!m_recentProjectsAction->urls().isEmpty()) { QDEBUG("TO OPEN m_recentProjectsAction->urls() =" << m_recentProjectsAction->urls().constFirst()); openRecentProject( m_recentProjectsAction->urls().constFirst() ); } } else if (load == 4) { //welcome screen m_showWelcomeScreen = true; m_welcomeWidget = createWelcomeScreen(); setCentralWidget(m_welcomeWidget); } } //show memory info const bool showMemoryInfo = group.readEntry(QLatin1String("ShowMemoryInfo"), true); if (showMemoryInfo) { m_memoryInfoWidget = new MemoryWidget(statusBar()); statusBar()->addPermanentWidget(m_memoryInfoWidget); } updateGUIOnProjectChanges(); } /** * @brief Creates a new welcome screen to be set as central widget. */ QQuickWidget* MainWin::createWelcomeScreen() { QSize maxSize = qApp->primaryScreen()->availableSize(); resize(maxSize); setMinimumSize(700, 400); showMaximized(); KToolBar* toolbar = toolBar(); - if(toolbar != nullptr) { + if(toolbar) toolbar->setVisible(false); - } else { - qDebug() << "There is no toolbar to hide"; - } QList recentList; for (QUrl url : m_recentProjectsAction->urls()) recentList.append(QVariant(url)); - //Set the source qml + //Set context property QQuickWidget* quickWidget = new QQuickWidget(this); - QUrl source("qrc:///main.qml"); - - //Set ocntext property QQmlContext *ctxt = quickWidget->rootContext(); QVariant variant(recentList); ctxt->setContextProperty("recentProjects", variant); //Create helper object if(m_welcomeScreenHelper != nullptr) delete m_welcomeScreenHelper; m_welcomeScreenHelper = new WelcomeScreenHelper(); connect(m_welcomeScreenHelper, SIGNAL(openExampleProject(QString)), this, SLOT(openProject(const QString& ))); ctxt->setContextProperty("datasetModel", m_welcomeScreenHelper->getDatasetModel()); ctxt->setContextProperty("helper", m_welcomeScreenHelper); - quickWidget->setSource(source); + quickWidget->setSource(QUrl(QLatin1String("qrc:///main.qml"))); quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); QObject *item = quickWidget->rootObject(); //connect qml's signals QObject::connect(item, SIGNAL(recentProjectClicked(QUrl)), this, SLOT(openRecentProject(QUrl))); QObject::connect(item, SIGNAL(datasetClicked(QString, QString, QString)), m_welcomeScreenHelper, SLOT(datasetClicked(const QString&, const QString&, const QString&))); QObject::connect(item, SIGNAL(openDataset()), this, SLOT(openDatasetExample())); QObject::connect(item, SIGNAL(openExampleProject(QString)), m_welcomeScreenHelper, SLOT(exampleProjectClicked(const QString&))); m_welcomeScreenHelper->showFirstDataset(); return quickWidget; } /** * @brief Initiates resetting the layout of the welcome screen */ void MainWin::resetWelcomeScreen() { - if(dynamic_cast(centralWidget()) != nullptr) { - QMetaObject::invokeMethod(m_welcomeWidget->rootObject(), "restoreOriginalLayout"); - } + if(dynamic_cast(centralWidget())) + QMetaObject::invokeMethod(m_welcomeWidget->rootObject(), "restoreOriginalLayout"); } /** * @brief Creates a new MDI area, to replace the Welcome Screen as central widget */ void MainWin::createMdiArea() { setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); setMinimumSize(0,0); KToolBar* toolbar = toolBar(); - if(toolbar != nullptr) { + if(toolbar) toolbar->setVisible(true); - } else { - qDebug() << "There is no toolbar to display"; - } //Save welcome screen's dimensions. - if(m_showWelcomeScreen) { - qDebug() << "Call saving welcome screen widget dimensions"; + if(m_showWelcomeScreen) 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 m_newProjectAction = KStandardAction::openNew(this, SLOT(newProject()),actionCollection()); m_openProjectAction = KStandardAction::open(this, SLOT(openProject()),actionCollection()); m_recentProjectsAction = KStandardAction::openRecent(this, SLOT(openRecentProject(QUrl)),actionCollection()); m_closeAction = KStandardAction::close(this, SLOT(closeProject()),actionCollection()); actionCollection()->setDefaultShortcut(m_closeAction, QKeySequence()); //remove the shortcut, QKeySequence::Close will be used for closing sub-windows m_saveAction = KStandardAction::save(this, SLOT(saveProject()),actionCollection()); m_saveAsAction = KStandardAction::saveAs(this, SLOT(saveProjectAs()),actionCollection()); m_printAction = KStandardAction::print(this, SLOT(print()),actionCollection()); m_printPreviewAction = KStandardAction::printPreview(this, SLOT(printPreview()),actionCollection()); //TODO: on Mac OS when going full-screen we get a crash because of an stack-overflow #ifndef Q_OS_MAC KStandardAction::fullScreen(this, SLOT(toggleFullScreen()), this, actionCollection()); #endif //New Folder/Workbook/Spreadsheet/Matrix/Worksheet/Datasources m_newWorkbookAction = new QAction(QIcon::fromTheme("labplot-workbook-new"),i18n("Workbook"),this); actionCollection()->addAction("new_workbook", m_newWorkbookAction); m_newWorkbookAction->setWhatsThis(i18n("Creates a new workbook for collection spreadsheets, matrices and plots")); connect(m_newWorkbookAction, &QAction::triggered, this, &MainWin::newWorkbook); m_newDatapickerAction = new QAction(QIcon::fromTheme("color-picker-black"), i18n("Datapicker"), this); m_newDatapickerAction->setWhatsThis(i18n("Creates a data picker for getting data from a picture")); actionCollection()->addAction("new_datapicker", m_newDatapickerAction); connect(m_newDatapickerAction, &QAction::triggered, this, &MainWin::newDatapicker); m_newSpreadsheetAction = new QAction(QIcon::fromTheme("labplot-spreadsheet-new"),i18n("Spreadsheet"),this); // m_newSpreadsheetAction->setShortcut(Qt::CTRL+Qt::Key_Equal); m_newSpreadsheetAction->setWhatsThis(i18n("Creates a new spreadsheet for data editing")); actionCollection()->addAction("new_spreadsheet", m_newSpreadsheetAction); connect(m_newSpreadsheetAction, &QAction::triggered, this, &MainWin::newSpreadsheet); m_newMatrixAction = new QAction(QIcon::fromTheme("labplot-matrix-new"),i18n("Matrix"),this); // m_newMatrixAction->setShortcut(Qt::CTRL+Qt::Key_Equal); m_newMatrixAction->setWhatsThis(i18n("Creates a new matrix for data editing")); actionCollection()->addAction("new_matrix", m_newMatrixAction); connect(m_newMatrixAction, &QAction::triggered, this, &MainWin::newMatrix); m_newWorksheetAction = new QAction(QIcon::fromTheme("labplot-worksheet-new"),i18n("Worksheet"),this); // m_newWorksheetAction->setShortcut(Qt::ALT+Qt::Key_X); m_newWorksheetAction->setWhatsThis(i18n("Creates a new worksheet for data plotting")); actionCollection()->addAction("new_worksheet", m_newWorksheetAction); connect(m_newWorksheetAction, &QAction::triggered, this, &MainWin::newWorksheet); m_newNotesAction = new QAction(QIcon::fromTheme("document-new"),i18n("Note"),this); m_newNotesAction->setWhatsThis(i18n("Creates a new note for arbitrary text")); actionCollection()->addAction("new_notes", m_newNotesAction); connect(m_newNotesAction, &QAction::triggered, this, &MainWin::newNotes); // m_newScriptAction = new QAction(QIcon::fromTheme("insert-text"),i18n("Note/Script"),this); // actionCollection()->addAction("new_script", m_newScriptAction); // connect(m_newScriptAction, &QAction::triggered,SLOT(newScript())); m_newFolderAction = new QAction(QIcon::fromTheme("folder-new"),i18n("Folder"),this); m_newFolderAction->setWhatsThis(i18n("Creates a new folder to collect sheets and other elements")); actionCollection()->addAction("new_folder", m_newFolderAction); connect(m_newFolderAction, &QAction::triggered, this, &MainWin::newFolder); //"New file datasources" m_newLiveDataSourceAction = new QAction(QIcon::fromTheme("application-octet-stream"),i18n("Live Data Source"),this); m_newLiveDataSourceAction->setWhatsThis(i18n("Creates a live data source to read data from a real time device")); actionCollection()->addAction("new_live_datasource", m_newLiveDataSourceAction); connect(m_newLiveDataSourceAction, &QAction::triggered, this, &MainWin::newLiveDataSourceActionTriggered); //Import/Export m_importFileAction = new QAction(QIcon::fromTheme("document-import"), i18n("Import from File"), this); actionCollection()->setDefaultShortcut(m_importFileAction, Qt::CTRL+Qt::SHIFT+Qt::Key_I); m_importFileAction->setWhatsThis(i18n("Import data from a regular file")); actionCollection()->addAction("import_file", m_importFileAction); connect(m_importFileAction, &QAction::triggered, this, [=]() {importFileDialog();}); m_importSqlAction = new QAction(QIcon::fromTheme("network-server-database"), i18n("From SQL Database"), this); m_importSqlAction->setWhatsThis(i18n("Import data from a SQL database")); actionCollection()->addAction("import_sql", m_importSqlAction); connect(m_importSqlAction, &QAction::triggered, this, &MainWin::importSqlDialog); m_importDatasetAction = new QAction(QIcon::fromTheme(QLatin1String("database-index")), i18n("From Dataset Collection"), this); m_importDatasetAction->setWhatsThis(i18n("Imports data from an online dataset")); actionCollection()->addAction("import_dataset_datasource", m_importDatasetAction); connect(m_importDatasetAction, &QAction::triggered, this, &MainWin::importDatasetDialog); m_importLabPlotAction = new QAction(QIcon::fromTheme("project-open"), i18n("LabPlot Project"), this); m_importLabPlotAction->setWhatsThis(i18n("Import a project from a LabPlot project file (.lml)")); actionCollection()->addAction("import_labplot", m_importLabPlotAction); connect(m_importLabPlotAction, &QAction::triggered, this, &MainWin::importProjectDialog); #ifdef HAVE_LIBORIGIN m_importOpjAction = new QAction(QIcon::fromTheme("project-open"), i18n("Origin Project (OPJ)"), this); m_importOpjAction->setWhatsThis(i18n("Import a project from an OriginLab Origin project file (.opj)")); actionCollection()->addAction("import_opj", m_importOpjAction); connect(m_importOpjAction, &QAction::triggered, this, &MainWin::importProjectDialog); #endif m_exportAction = new QAction(QIcon::fromTheme("document-export"), i18n("Export"), this); m_exportAction->setWhatsThis(i18n("Export selected element")); actionCollection()->setDefaultShortcut(m_exportAction, Qt::CTRL+Qt::SHIFT+Qt::Key_E); actionCollection()->addAction("export", m_exportAction); connect(m_exportAction, &QAction::triggered, this, &MainWin::exportDialog); m_editFitsFileAction = new QAction(QIcon::fromTheme("editor"), i18n("FITS Metadata Editor"), this); m_editFitsFileAction->setWhatsThis(i18n("Open editor to edit FITS meta data")); actionCollection()->addAction("edit_fits", m_editFitsFileAction); connect(m_editFitsFileAction, &QAction::triggered, this, &MainWin::editFitsFileDialog); // Edit //Undo/Redo-stuff m_undoAction = KStandardAction::undo(this, SLOT(undo()), actionCollection()); m_redoAction = KStandardAction::redo(this, SLOT(redo()), actionCollection()); m_historyAction = new QAction(QIcon::fromTheme("view-history"), i18n("Undo/Redo History"),this); actionCollection()->addAction("history", m_historyAction); connect(m_historyAction, &QAction::triggered, this, &MainWin::historyDialog); // TODO: more menus // Appearance // Analysis: see WorksheetView.cpp // Drawing // Script //Windows QAction* action = new QAction(i18n("&Close"), this); actionCollection()->setDefaultShortcut(action, QKeySequence::Close); action->setStatusTip(i18n("Close the active window")); actionCollection()->addAction("close window", action); connect(action, &QAction::triggered, m_mdiArea, &QMdiArea::closeActiveSubWindow); action = new QAction(i18n("Close &All"), this); action->setStatusTip(i18n("Close all the windows")); actionCollection()->addAction("close all windows", action); connect(action, &QAction::triggered, m_mdiArea, &QMdiArea::closeAllSubWindows); m_tileWindows = new QAction(i18n("&Tile"), this); m_tileWindows->setStatusTip(i18n("Tile the windows")); actionCollection()->addAction("tile windows", m_tileWindows); connect(m_tileWindows, &QAction::triggered, m_mdiArea, &QMdiArea::tileSubWindows); m_cascadeWindows = new QAction(i18n("&Cascade"), this); m_cascadeWindows->setStatusTip(i18n("Cascade the windows")); actionCollection()->addAction("cascade windows", m_cascadeWindows); connect(m_cascadeWindows, &QAction::triggered, m_mdiArea, &QMdiArea::cascadeSubWindows); action = new QAction(QIcon::fromTheme("go-next-view"), i18n("Ne&xt"), this); actionCollection()->setDefaultShortcut(action, QKeySequence::NextChild); action->setStatusTip(i18n("Move the focus to the next window")); actionCollection()->addAction("next window", action); connect(action, &QAction::triggered, m_mdiArea, &QMdiArea::activateNextSubWindow); action = new QAction(QIcon::fromTheme("go-previous-view"), i18n("Pre&vious"), this); actionCollection()->setDefaultShortcut(action, QKeySequence::PreviousChild); action->setStatusTip(i18n("Move the focus to the previous window")); actionCollection()->addAction("previous window", action); connect(action, &QAction::triggered, m_mdiArea, &QMdiArea::activatePreviousSubWindow); //"Standard actions" KStandardAction::preferences(this, SLOT(settingsDialog()), actionCollection()); KStandardAction::quit(this, SLOT(close()), actionCollection()); //Actions for window visibility auto* windowVisibilityActions = new QActionGroup(this); windowVisibilityActions->setExclusive(true); m_visibilityFolderAction = new QAction(QIcon::fromTheme("folder"), i18n("Current &Folder Only"), windowVisibilityActions); m_visibilityFolderAction->setCheckable(true); m_visibilityFolderAction->setData(Project::folderOnly); m_visibilitySubfolderAction = new QAction(QIcon::fromTheme("folder-documents"), i18n("Current Folder and &Subfolders"), windowVisibilityActions); m_visibilitySubfolderAction->setCheckable(true); m_visibilitySubfolderAction->setData(Project::folderAndSubfolders); m_visibilityAllAction = new QAction(i18n("&All"), windowVisibilityActions); m_visibilityAllAction->setCheckable(true); m_visibilityAllAction->setData(Project::allMdiWindows); connect(windowVisibilityActions, &QActionGroup::triggered, this, &MainWin::setMdiWindowVisibility); //Actions for hiding/showing the dock widgets auto* docksActions = new QActionGroup(this); docksActions->setExclusive(false); m_toggleProjectExplorerDockAction = new QAction(QIcon::fromTheme("view-list-tree"), i18n("Project Explorer"), docksActions); m_toggleProjectExplorerDockAction->setCheckable(true); m_toggleProjectExplorerDockAction->setChecked(true); actionCollection()->addAction("toggle_project_explorer_dock", m_toggleProjectExplorerDockAction); m_togglePropertiesDockAction = new QAction(QIcon::fromTheme("view-list-details"), i18n("Properties Explorer"), docksActions); m_togglePropertiesDockAction->setCheckable(true); m_togglePropertiesDockAction->setChecked(true); actionCollection()->addAction("toggle_properties_explorer_dock", m_togglePropertiesDockAction); connect(docksActions, &QActionGroup::triggered, this, &MainWin::toggleDockWidget); //global search QAction* searchAction = new QAction(actionCollection()); searchAction->setShortcut(QKeySequence::Find); connect(searchAction, &QAction::triggered, this, [=]() { if (m_project) { if (!m_projectExplorerDock->isVisible()) { m_toggleProjectExplorerDockAction->setChecked(true); toggleDockWidget(m_toggleProjectExplorerDockAction); } m_projectExplorer->search(); } }); this->addAction(searchAction); } void MainWin::initMenus() { //menu in the main toolbar for adding new aspects auto* menu = dynamic_cast(factory()->container("new", this)); menu->setIcon(QIcon::fromTheme("window-new")); //menu in the project explorer and in the toolbar for adding new aspects m_newMenu = new QMenu(i18n("Add New"), this); m_newMenu->setIcon(QIcon::fromTheme("window-new")); m_newMenu->addAction(m_newFolderAction); m_newMenu->addAction(m_newWorkbookAction); m_newMenu->addAction(m_newSpreadsheetAction); m_newMenu->addAction(m_newMatrixAction); m_newMenu->addAction(m_newWorksheetAction); m_newMenu->addAction(m_newNotesAction); m_newMenu->addAction(m_newDatapickerAction); m_newMenu->addSeparator(); m_newMenu->addAction(m_newLiveDataSourceAction); //import menu m_importMenu = new QMenu(this); m_importMenu->setIcon(QIcon::fromTheme("document-import")); m_importMenu ->addAction(m_importFileAction); m_importMenu ->addAction(m_importSqlAction); m_importMenu->addAction(m_importDatasetAction); m_importMenu->addSeparator(); m_importMenu->addAction(m_importLabPlotAction); #ifdef HAVE_LIBORIGIN m_importMenu ->addAction(m_importOpjAction); #endif #ifdef HAVE_CANTOR_LIBS m_newMenu->addSeparator(); m_newCantorWorksheetMenu = new QMenu(i18n("CAS Worksheet"), this); m_newCantorWorksheetMenu->setIcon(QIcon::fromTheme("archive-insert")); //"Adding Cantor backends to menu and context menu" QStringList m_availableBackend = Cantor::Backend::listAvailableBackends(); if (m_availableBackend.count() > 0) { unplugActionList(QLatin1String("backends_list")); QList newBackendActions; for (Cantor::Backend* backend : Cantor::Backend::availableBackends()) { if (!backend->isEnabled()) continue; QAction* action = new QAction(QIcon::fromTheme(backend->icon()), backend->name(),this); action->setData(backend->name()); newBackendActions << action; m_newCantorWorksheetMenu->addAction(action); } connect(m_newCantorWorksheetMenu, &QMenu::triggered, this, &MainWin::newCantorWorksheet); plugActionList(QLatin1String("backends_list"), newBackendActions); } m_newMenu->addMenu(m_newCantorWorksheetMenu); #else delete this->guiFactory()->container("cas_worksheet", this); delete this->guiFactory()->container("new_cas_worksheet", this); delete this->guiFactory()->container("cas_worksheet_toolbar", this); #endif //menu subwindow visibility policy m_visibilityMenu = new QMenu(i18n("Window Visibility Policy"), this); m_visibilityMenu->setIcon(QIcon::fromTheme("window-duplicate")); m_visibilityMenu ->addAction(m_visibilityFolderAction); m_visibilityMenu ->addAction(m_visibilitySubfolderAction); m_visibilityMenu ->addAction(m_visibilityAllAction); //menu for editing files m_editMenu = new QMenu(i18n("Edit"), this); m_editMenu->addAction(m_editFitsFileAction); KColorSchemeManager schemeManager; KActionMenu* schemesMenu = schemeManager.createSchemeSelectionMenu(i18n("Color Scheme"), QString(), this); 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()); #ifdef Q_OS_MAC m_touchBar->clear(); if (b){ m_touchBar->addAction(m_newProjectAction); m_touchBar->addAction(m_openProjectAction); } else { m_touchBar->addAction(m_importFileAction); m_touchBar->addAction(m_newWorksheetAction); m_touchBar->addAction(m_newSpreadsheetAction); } #endif // 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; } //reset the touchbar #ifdef Q_OS_MAC m_touchBar->clear(); m_touchBar->addAction(m_undoAction); m_touchBar->addAction(m_redoAction); m_touchBar->addSeparator(); #endif 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; } #ifdef Q_OS_MAC if (dynamic_cast(m_currentAspect)) { //if (m_currentAspect->type() == AspectType::Folder) { m_touchBar->addAction(m_newWorksheetAction); m_touchBar->addAction(m_newSpreadsheetAction); m_touchBar->addAction(m_newMatrixAction); } #endif //Handle the Worksheet-object const Worksheet* w = dynamic_cast(m_currentAspect); if (!w) w = dynamic_cast(m_currentAspect->parent(AspectType::Worksheet)); if (w) { //populate worksheet menu auto* view = qobject_cast(w->view()); auto* menu = qobject_cast(factory->container("worksheet", this)); menu->clear(); view->createContextMenu(menu); menu->setEnabled(true); //populate analysis menu menu = qobject_cast(factory->container("analysis", this)); menu->clear(); view->createAnalysisMenu(menu); menu->setEnabled(true); //populate worksheet-toolbar auto* toolbar = qobject_cast(factory->container("worksheet_toolbar", this)); toolbar->clear(); view->fillToolBar(toolbar); toolbar->setVisible(true); toolbar->setEnabled(true); //populate the toolbar for cartesian plots toolbar = qobject_cast(factory->container("cartesian_plot_toolbar", this)); toolbar->clear(); view->fillCartesianPlotToolBar(toolbar); toolbar->setVisible(true); toolbar->setEnabled(true); //populate the touchbar on Mac #ifdef Q_OS_MAC view->fillTouchBar(m_touchBar); #endif //hide the spreadsheet toolbar factory->container("spreadsheet_toolbar", this)->setVisible(false); } else { factory->container("worksheet", this)->setEnabled(false); factory->container("worksheet_toolbar", this)->setVisible(false); factory->container("analysis", this)->setEnabled(false); // factory->container("drawing", this)->setEnabled(false); factory->container("worksheet_toolbar", this)->setEnabled(false); factory->container("cartesian_plot_toolbar", this)->setEnabled(false); } //Handle the Spreadsheet-object const auto* spreadsheet = this->activeSpreadsheet(); if (!spreadsheet) spreadsheet = dynamic_cast(m_currentAspect->parent(AspectType::Spreadsheet)); if (spreadsheet) { //populate spreadsheet-menu auto* view = qobject_cast(spreadsheet->view()); auto* menu = qobject_cast(factory->container("spreadsheet", this)); menu->clear(); view->createContextMenu(menu); menu->setEnabled(true); //populate spreadsheet-toolbar auto* toolbar = qobject_cast(factory->container("spreadsheet_toolbar", this)); toolbar->clear(); view->fillToolBar(toolbar); toolbar->setVisible(true); toolbar->setEnabled(true); //populate the touchbar on Mac #ifdef Q_OS_MAC m_touchBar->addAction(m_importFileAction); view->fillTouchBar(m_touchBar); #endif } else { factory->container("spreadsheet", this)->setEnabled(false); factory->container("spreadsheet_toolbar", this)->setVisible(false); } //Handle the Matrix-object const Matrix* matrix = dynamic_cast(m_currentAspect); if (!matrix) matrix = dynamic_cast(m_currentAspect->parent(AspectType::Matrix)); if (matrix) { //populate matrix-menu auto* view = qobject_cast(matrix->view()); auto* menu = qobject_cast(factory->container("matrix", this)); menu->clear(); view->createContextMenu(menu); menu->setEnabled(true); //populate the touchbar on Mac #ifdef Q_OS_MAC m_touchBar->addAction(m_importFileAction); //view->fillTouchBar(m_touchBar); #endif } else factory->container("matrix", this)->setEnabled(false); #ifdef HAVE_CANTOR_LIBS const CantorWorksheet* cantorworksheet = dynamic_cast(m_currentAspect); if (!cantorworksheet) cantorworksheet = dynamic_cast(m_currentAspect->parent(AspectType::CantorWorksheet)); if (cantorworksheet) { auto* view = qobject_cast(cantorworksheet->view()); auto* menu = qobject_cast(factory->container("cas_worksheet", this)); menu->clear(); view->createContextMenu(menu); menu->setEnabled(true); auto* toolbar = qobject_cast(factory->container("cas_worksheet_toolbar", this)); toolbar->setVisible(true); toolbar->clear(); view->fillToolBar(toolbar); } else { //no Cantor worksheet selected -> deactivate Cantor worksheet related menu and toolbar factory->container("cas_worksheet", this)->setEnabled(false); factory->container("cas_worksheet_toolbar", this)->setVisible(false); } #endif const Datapicker* datapicker = dynamic_cast(m_currentAspect); if (!datapicker) datapicker = dynamic_cast(m_currentAspect->parent(AspectType::Datapicker)); if (!datapicker) { if (m_currentAspect->type() == AspectType::DatapickerCurve) datapicker = dynamic_cast(m_currentAspect->parentAspect()); } if (datapicker) { //populate datapicker-menu auto* view = qobject_cast(datapicker->view()); auto* menu = qobject_cast(factory->container("datapicker", this)); menu->clear(); view->createContextMenu(menu); menu->setEnabled(true); //populate spreadsheet-toolbar auto* toolbar = qobject_cast(factory->container("datapicker_toolbar", this)); toolbar->clear(); view->fillToolBar(toolbar); toolbar->setVisible(true); } else { factory->container("datapicker", this)->setEnabled(false); factory->container("datapicker_toolbar", this)->setVisible(false); } } /*! creates a new empty project. Returns \c true, if a new project was created. */ bool MainWin::newProject() { //close the current project, if available if (!closeProject()) return false; - if(dynamic_cast(centralWidget()) != nullptr) { + if(dynamic_cast(centralWidget())) { 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) { + if(dynamic_cast(centralWidget())) { createMdiArea(); setCentralWidget(m_mdiArea); } if (!newProject()) return; WAIT_CURSOR; QElapsedTimer timer; timer.start(); bool rc = false; if (Project::isLabPlotProject(filename)) { m_project->setFileName(filename); rc = m_project->load(filename); #ifdef HAVE_LIBORIGIN } else if (OriginProjectParser::isOriginProject(filename)) { OriginProjectParser parser; parser.setProjectFileName(filename); parser.importTo(m_project, QStringList()); //TODO: add return code rc = true; } #endif if (!rc) { closeProject(); RESET_CURSOR; return; } m_currentFileName = filename; m_project->undoStack()->clear(); m_undoViewEmptyLabel = i18n("%1: opened", m_project->name()); m_recentProjectsAction->addUrl( QUrl(filename) ); setCaption(m_project->name()); updateGUIOnProjectChanges(); updateGUI(); //there are most probably worksheets or spreadsheets in the open project -> update the GUI m_saveAction->setEnabled(false); statusBar()->showMessage( i18n("Project successfully opened (in %1 seconds).", (float)timer.elapsed()/1000) ); if (m_autoSaveActive) m_autoSaveTimer.start(); RESET_CURSOR; } void MainWin::openRecentProject(const QUrl& url) { - if(dynamic_cast(centralWidget()) != nullptr) { + if(dynamic_cast(centralWidget())) { createMdiArea(); setCentralWidget(m_mdiArea); } if (url.isLocalFile()) // fix for Windows this->openProject(url.toLocalFile()); else this->openProject(url.path()); } /*! Closes the current project, if available. Return \c true, if the project was closed. */ bool MainWin::closeProject() { if (m_project == nullptr) return true; //nothing to close if (warnModified()) return false; if(!m_closing) { if(dynamic_cast(centralWidget()) == nullptr && m_showWelcomeScreen) { m_welcomeWidget = createWelcomeScreen(); setCentralWidget(m_welcomeWidget); } } m_projectClosing = true; statusBar()->clearMessage(); delete m_aspectTreeModel; m_aspectTreeModel = nullptr; delete m_project; m_project = nullptr; m_currentFileName.clear(); m_projectClosing = false; //update the UI if we're just closing a project //and not closing(quitting) the application if (!m_closing) { m_projectExplorerDock->hide(); m_propertiesDock->hide(); m_currentAspect = nullptr; m_currentFolder = nullptr; updateGUIOnProjectChanges(); if (m_autoSaveActive) m_autoSaveTimer.stop(); } removeDockWidget(cursorDock); delete cursorDock; cursorDock = nullptr; cursorWidget = nullptr; // is deleted, because it's the cild of cursorDock return true; } bool MainWin::saveProject() { const QString& fileName = m_project->fileName(); if (fileName.isEmpty()) return saveProjectAs(); else return save(fileName); } bool MainWin::saveProjectAs() { KConfigGroup conf(KSharedConfig::openConfig(), "MainWin"); const QString& dir = conf.readEntry("LastOpenDir", ""); QString path = QFileDialog::getSaveFileName(this, i18n("Save Project As"), dir, i18n("LabPlot Projects (*.lml *.lml.gz *.lml.bz2 *.lml.xz *.LML *.LML.GZ *.LML.BZ2 *.LML.XZ)")); if (path.isEmpty())// "Cancel" was clicked return false; if (path.contains(QLatin1String(".lml"), Qt::CaseInsensitive) == false) path.append(QLatin1String(".lml")); //save new "last open directory" int pos = path.lastIndexOf(QDir::separator()); if (pos != -1) { const QString& newDir = path.left(pos); if (newDir != dir) conf.writeEntry("LastOpenDir", newDir); } return save(path); } /*! * auxiliary function that does the actual saving of the project */ bool MainWin::save(const QString& fileName) { QTemporaryFile tempFile(QDir::tempPath() + "/" + QLatin1String("labplot_save_XXXXXX")); if (!tempFile.open()) { KMessageBox::error(this, i18n("Couldn't open the temporary file for writing.")); return false; } WAIT_CURSOR; const QString& tempFileName = tempFile.fileName(); DEBUG("Using temporary file " << tempFileName.toStdString()) tempFile.close(); // use file ending to find out how to compress file QIODevice* file; // if ending is .lml, do gzip compression anyway if (fileName.endsWith(QLatin1String(".lml"))) file = new KCompressionDevice(tempFileName, KCompressionDevice::GZip); else file = new KFilterDev(tempFileName); if (file == nullptr) file = new QFile(tempFileName); bool ok; if (file->open(QIODevice::WriteOnly)) { m_project->setFileName(fileName); QPixmap thumbnail = centralWidget()->grab(); QXmlStreamWriter writer(file); m_project->setFileName(fileName); m_project->save(thumbnail, &writer); m_project->undoStack()->clear(); m_project->setChanged(false); file->close(); // target file must not exist if (QFile::exists(fileName)) QFile::remove(fileName); // do not rename temp file. Qt still holds a handle (which fails renaming on Windows) and deletes it bool rc = QFile::copy(tempFileName, fileName); if (rc) { setCaption(m_project->name()); statusBar()->showMessage(i18n("Project saved")); m_saveAction->setEnabled(false); m_recentProjectsAction->addUrl( QUrl(fileName) ); ok = true; //if the project dock is visible, refresh the shown content //(version and modification time might have been changed) if (stackedWidget->currentWidget() == projectDock) projectDock->setProject(m_project); //we have a file name now // -> auto save can be activated now if not happened yet if (m_autoSaveActive && !m_autoSaveTimer.isActive()) m_autoSaveTimer.start(); } else { RESET_CURSOR; KMessageBox::error(this, i18n("Couldn't save the file '%1'.", fileName)); ok = false; } } else { RESET_CURSOR; KMessageBox::error(this, i18n("Couldn't open the file '%1' for writing.", fileName)); ok = false; } delete file; RESET_CURSOR; return ok; } /*! * automatically saves the project in the specified time interval. */ void MainWin::autoSaveProject() { //don't auto save when there are no changes or the file name //was not provided yet (the project was never explicitly saved yet). if ( !m_project->hasChanged() || m_project->fileName().isEmpty()) return; this->saveProject(); } /*! prints the current sheet (worksheet, spreadsheet or matrix) */ void MainWin::print() { QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return; AbstractPart* part = dynamic_cast(win)->part(); statusBar()->showMessage(i18n("Preparing printing of %1", part->name())); if (part->printView()) statusBar()->showMessage(i18n("%1 printed", part->name())); else statusBar()->clearMessage(); } void MainWin::printPreview() { QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return; AbstractPart* part = dynamic_cast(win)->part(); statusBar()->showMessage(i18n("Preparing printing of %1", part->name())); if (part->printPreview()) statusBar()->showMessage(i18n("%1 printed", part->name())); else statusBar()->clearMessage(); } /**************************************************************************************/ /*! adds a new Folder to the project. */ void MainWin::newFolder() { Folder* folder = new Folder(i18n("Folder")); this->addAspectToProject(folder); } /*! adds a new Workbook to the project. */ void MainWin::newWorkbook() { Workbook* workbook = new Workbook(i18n("Workbook")); this->addAspectToProject(workbook); } /*! adds a new Datapicker to the project. */ void MainWin::newDatapicker() { Datapicker* datapicker = new Datapicker(i18n("Datapicker")); this->addAspectToProject(datapicker); } /*! adds a new Spreadsheet to the project. */ void MainWin::newSpreadsheet() { Spreadsheet* spreadsheet = new Spreadsheet(i18n("Spreadsheet")); //if the current active window is a workbook and no folder/project is selected in the project explorer, //add the new spreadsheet to the workbook Workbook* workbook = dynamic_cast(m_currentAspect); if (workbook) { QModelIndex index = m_projectExplorer->currentIndex(); const auto* aspect = static_cast(index.internalPointer()); if (!aspect->inherits(AspectType::Folder)) { workbook->addChild(spreadsheet); return; } } this->addAspectToProject(spreadsheet); } /*! adds a new Matrix to the project. */ void MainWin::newMatrix() { Matrix* matrix = new Matrix(i18n("Matrix")); //if the current active window is a workbook and no folder/project is selected in the project explorer, //add the new matrix to the workbook Workbook* workbook = dynamic_cast(m_currentAspect); if (workbook) { QModelIndex index = m_projectExplorer->currentIndex(); const auto* aspect = static_cast(index.internalPointer()); if (!aspect->inherits(AspectType::Folder)) { workbook->addChild(matrix); return; } } this->addAspectToProject(matrix); } /*! adds a new Worksheet to the project. */ void MainWin::newWorksheet() { Worksheet* worksheet = new Worksheet(i18n("Worksheet")); this->addAspectToProject(worksheet); } /*! adds a new Note to the project. */ void MainWin::newNotes() { Note* notes = new Note(i18n("Note")); this->addAspectToProject(notes); } /*! returns a pointer to a \c Spreadsheet object, if the currently active Mdi-Subwindow or if the currently selected tab in a \c WorkbookView is a \c SpreadsheetView Otherwise returns \c 0. */ Spreadsheet* MainWin::activeSpreadsheet() const { if(dynamic_cast(centralWidget()) != nullptr) { return nullptr; } if (!m_currentAspect) return nullptr; Spreadsheet* spreadsheet = nullptr; if (m_currentAspect->type() == AspectType::Spreadsheet) spreadsheet = dynamic_cast(m_currentAspect); else { //check whether one of spreadsheet columns is selected and determine the spreadsheet auto* parent = m_currentAspect->parentAspect(); if (parent && parent->type() == AspectType::Spreadsheet) spreadsheet = dynamic_cast(parent); } return spreadsheet; } #ifdef HAVE_CANTOR_LIBS /* adds a new Cantor Spreadsheet to the project. */ void MainWin::newCantorWorksheet(QAction* action) { CantorWorksheet* cantorworksheet = new CantorWorksheet(action->data().toString()); this->addAspectToProject(cantorworksheet); } /********************************************************************************/ #endif /*! called if there were changes in the project. Adds "changed" to the window caption and activates the save-Action. */ void MainWin::projectChanged() { setCaption(i18n("%1 [Changed]", m_project->name())); m_saveAction->setEnabled(true); m_undoAction->setEnabled(true); return; } void MainWin::handleCurrentSubWindowChanged(QMdiSubWindow* win) { if (!win) return; auto* view = qobject_cast(win); if (!view) { updateGUI(); return; } if (view == m_currentSubWindow) { //do nothing, if the current sub-window gets selected again. //This event happens, when labplot loses the focus (modal window is opened or the user switches to another application) //and gets it back (modal window is closed or the user switches back to labplot). return; } else m_currentSubWindow = view; updateGUI(); if (!m_suppressCurrentSubWindowChangedEvent) m_projectExplorer->setCurrentAspect(view->part()); } void MainWin::handleAspectAdded(const AbstractAspect* aspect) { const auto* part = dynamic_cast(aspect); if (part) { // connect(part, &AbstractPart::importFromFileRequested, this, &MainWin::importFileDialog); connect(part, &AbstractPart::importFromFileRequested, this, [=]() {importFileDialog();}); connect(part, &AbstractPart::importFromSQLDatabaseRequested, this, &MainWin::importSqlDialog); //TODO: export, print and print preview should be handled in the views and not in MainWin. connect(part, &AbstractPart::exportRequested, this, &MainWin::exportDialog); connect(part, &AbstractPart::printRequested, this, &MainWin::print); connect(part, &AbstractPart::printPreviewRequested, this, &MainWin::printPreview); connect(part, &AbstractPart::showRequested, this, &MainWin::handleShowSubWindowRequested); const auto* worksheet = dynamic_cast(aspect); if (worksheet) connect(worksheet, &Worksheet::cartesianPlotMouseModeChanged, this, &MainWin::cartesianPlotMouseModeChanged); } } // void MainWin::cartesianPlotMouseModeChanged(CartesianPlot::MouseMode void MainWin::handleAspectRemoved(const AbstractAspect* parent,const AbstractAspect* before,const AbstractAspect* aspect) { Q_UNUSED(before); Q_UNUSED(aspect); //no need to react on AbstractSimpleFilter if (!dynamic_cast(aspect)) m_projectExplorer->setCurrentAspect(parent); } void MainWin::handleAspectAboutToBeRemoved(const AbstractAspect *aspect) { const auto* part = qobject_cast(aspect); if (!part) return; const auto* workbook = dynamic_cast(aspect->parentAspect()); auto* datapicker = dynamic_cast(aspect->parentAspect()); if (!datapicker) datapicker = dynamic_cast(aspect->parentAspect()->parentAspect()); if (!workbook && !datapicker) { PartMdiView* win = part->mdiSubWindow(); if (win) m_mdiArea->removeSubWindow(win); } } /*! called when the current aspect in the tree of the project explorer was changed. Selects the new aspect. */ void MainWin::handleCurrentAspectChanged(AbstractAspect *aspect) { if (!aspect) aspect = m_project; // should never happen, just in case m_suppressCurrentSubWindowChangedEvent = true; if (aspect->folder() != m_currentFolder) { m_currentFolder = aspect->folder(); updateMdiWindowVisibility(); } m_currentAspect = aspect; //activate the corresponding MDI sub window for the current aspect activateSubWindowForAspect(aspect); m_suppressCurrentSubWindowChangedEvent = false; updateGUI(); } void MainWin::activateSubWindowForAspect(const AbstractAspect* aspect) const { const auto* part = dynamic_cast(aspect); if (part) { //for LiveDataSource we currently don't show any view /*if (dynamic_cast(part)) return;*/ PartMdiView* win; //for aspects being children of a Workbook, we show workbook's window, otherwise the window of the selected part const auto* workbook = dynamic_cast(aspect->parentAspect()); auto* datapicker = dynamic_cast(aspect->parentAspect()); if (!datapicker) datapicker = dynamic_cast(aspect->parentAspect()->parentAspect()); if (workbook) win = workbook->mdiSubWindow(); else if (datapicker) win = datapicker->mdiSubWindow(); else win = part->mdiSubWindow(); if (m_mdiArea->subWindowList().indexOf(win) == -1) { if (dynamic_cast(part)) m_mdiArea->addSubWindow(win, Qt::Tool); else m_mdiArea->addSubWindow(win); win->show(); //Qt provides its own "system menu" for every sub-window. The shortcut for the close-action //in this menu collides with our global m_closeAction. //remove the shortcuts in the system menu to avoid this collision. QMenu* menu = win->systemMenu(); if (menu) { for (QAction* action : menu->actions()) action->setShortcut(QKeySequence()); } } m_mdiArea->setActiveSubWindow(win); } else { //activate the mdiView of the parent, if a child was selected const AbstractAspect* parent = aspect->parentAspect(); if (parent) { activateSubWindowForAspect(parent); //if the parent's parent is a Workbook (a column of a spreadsheet in workbook was selected), //we need to select the corresponding tab in WorkbookView too if (parent->parentAspect()) { auto* workbook = dynamic_cast(parent->parentAspect()); auto* datapicker = dynamic_cast(parent->parentAspect()); if (!datapicker) datapicker = dynamic_cast(parent->parentAspect()->parentAspect()); if (workbook) workbook->childSelected(parent); else if (datapicker) datapicker->childSelected(parent); } } } return; } void MainWin::setMdiWindowVisibility(QAction* action) { m_project->setMdiWindowVisibility((Project::MdiWindowVisibility)(action->data().toInt())); } /*! shows the sub window of a worksheet, matrix or a spreadsheet. Used if the window was closed before and the user asks to show the window again via the context menu in the project explorer. */ void MainWin::handleShowSubWindowRequested() { activateSubWindowForAspect(m_currentAspect); } /*! this is called on a right click on the root folder in the project explorer */ void MainWin::createContextMenu(QMenu* menu) const { QAction* firstAction = nullptr; // if we're populating the context menu for the project explorer, then //there're already actions available there. Skip the first title-action //and insert the action at the beginning of the menu. if (menu->actions().size()>1) firstAction = menu->actions().at(1); menu->insertMenu(firstAction, m_newMenu); //The tabbed view collides with the visibility policy for the subwindows. //Hide the menus for the visibility policy if the tabbed view is used. if (m_mdiArea->viewMode() != QMdiArea::TabbedView) { menu->insertSeparator(firstAction); menu->insertMenu(firstAction, m_visibilityMenu); menu->insertSeparator(firstAction); } } /*! this is called on a right click on a non-root folder in the project explorer */ void MainWin::createFolderContextMenu(const Folder* folder, QMenu* menu) const { Q_UNUSED(folder); //Folder provides it's own context menu. Add a separator before adding additional actions. menu->addSeparator(); this->createContextMenu(menu); } void MainWin::undo() { WAIT_CURSOR; m_project->undoStack()->undo(); if (m_project->undoStack()->index() == 0) { setCaption(m_project->name()); m_saveAction->setEnabled(false); m_undoAction->setEnabled(false); m_project->setChanged(false); } m_redoAction->setEnabled(true); RESET_CURSOR; } void MainWin::redo() { WAIT_CURSOR; m_project->undoStack()->redo(); projectChanged(); if (m_project->undoStack()->index() == m_project->undoStack()->count()) m_redoAction->setEnabled(false); RESET_CURSOR; } /*! Shows/hides mdi sub-windows depending on the current visibility policy. */ void MainWin::updateMdiWindowVisibility() const { QList windows = m_mdiArea->subWindowList(); PartMdiView* part_view; switch (m_project->mdiWindowVisibility()) { case Project::allMdiWindows: for (auto* window : windows) window->show(); break; case Project::folderOnly: for (auto* window : windows) { part_view = qobject_cast(window); Q_ASSERT(part_view); if (part_view->part()->folder() == m_currentFolder) part_view->show(); else part_view->hide(); } break; case Project::folderAndSubfolders: for (auto* window : windows) { part_view = qobject_cast(window); if (part_view->part()->isDescendantOf(m_currentFolder)) part_view->show(); else part_view->hide(); } break; } } void MainWin::toggleDockWidget(QAction* action) { if (action->objectName() == "toggle_project_explorer_dock") { if (m_projectExplorerDock->isVisible()) m_projectExplorerDock->hide(); // toggleHideWidget(m_projectExplorerDock, true); else m_projectExplorerDock->show(); // toggleShowWidget(m_projectExplorerDock, true); } else if (action->objectName() == "toggle_properties_explorer_dock") { if (m_propertiesDock->isVisible()) m_propertiesDock->hide(); // toggleHideWidget(m_propertiesDock, false); else m_propertiesDock->show(); // toggleShowWidget(m_propertiesDock, false); } } /* void MainWin::toggleHideWidget(QWidget* widget, bool hideToLeft) { auto* timeline = new QTimeLine(800, this); timeline->setEasingCurve(QEasingCurve::InOutQuad); connect(timeline, &QTimeLine::valueChanged, [=] { const qreal value = timeline->currentValue(); const int widgetWidth = widget->width(); const int widgetPosY = widget->pos().y(); int moveX = 0; if (hideToLeft) { moveX = static_cast(value * widgetWidth) - widgetWidth; } else { const int frameRight = this->frameGeometry().right(); moveX = frameRight - static_cast(value * widgetWidth); } widget->move(moveX, widgetPosY); }); timeline->setDirection(QTimeLine::Backward); timeline->start(); connect(timeline, &QTimeLine::finished, [widget] {widget->hide();}); connect(timeline, &QTimeLine::finished, timeline, &QTimeLine::deleteLater); } void MainWin::toggleShowWidget(QWidget* widget, bool showToRight) { auto* timeline = new QTimeLine(800, this); timeline->setEasingCurve(QEasingCurve::InOutQuad); connect(timeline, &QTimeLine::valueChanged, [=]() { if (widget->isHidden()) { widget->show(); } const qreal value = timeline->currentValue(); const int widgetWidth = widget->width(); const int widgetPosY = widget->pos().y(); int moveX = 0; if (showToRight) { moveX = static_cast(value * widgetWidth) - widgetWidth; } else { const int frameRight = this->frameGeometry().right(); moveX = frameRight - static_cast(value * widgetWidth); } widget->move(moveX, widgetPosY); }); timeline->setDirection(QTimeLine::Forward); timeline->start(); connect(timeline, &QTimeLine::finished, timeline, &QTimeLine::deleteLater); } */ void MainWin::projectExplorerDockVisibilityChanged(bool visible) { m_toggleProjectExplorerDockAction->setChecked(visible); } void MainWin::propertiesDockVisibilityChanged(bool visible) { m_togglePropertiesDockAction->setChecked(visible); } void MainWin::cursorDockVisibilityChanged(bool visible) { //if the cursor dock was closed, switch to the "Select and Edit" mouse mode if (!visible) { // auto* worksheet = activeWorksheet(); //TODO: } } void MainWin::cartesianPlotMouseModeChanged(CartesianPlot::MouseMode mode) { if (mode != CartesianPlot::Cursor) { if (cursorDock) cursorDock->hide(); } else { if (!cursorDock) { cursorDock = new QDockWidget(i18n("Cursor"), this); cursorWidget = new CursorDock(cursorDock); cursorDock->setWidget(cursorWidget); connect(cursorDock, &QDockWidget::visibilityChanged, this, &MainWin::cursorDockVisibilityChanged); // cursorDock->setFloating(true); // does not work. Don't understand why // if (m_propertiesDock) // tabifyDockWidget(cursorDock, m_propertiesDock); // else addDockWidget(Qt::DockWidgetArea::RightDockWidgetArea, cursorDock); } auto* worksheet = static_cast(QObject::sender()); cursorWidget->setWorksheet(worksheet); cursorDock->show(); } } void MainWin::toggleFullScreen() { if (this->windowState() == Qt::WindowFullScreen) this->setWindowState(m_lastWindowState); else { m_lastWindowState = this->windowState(); this->showFullScreen(); } } void MainWin::closeEvent(QCloseEvent* event) { m_closing = true; if (!this->closeProject()) { m_closing = false; event->ignore(); } } void MainWin::dragEnterEvent(QDragEnterEvent* event) { event->accept(); } void MainWin::dropEvent(QDropEvent* event) { if (event->mimeData() && !event->mimeData()->urls().isEmpty()) { QUrl url = event->mimeData()->urls().at(0); const QString& f = url.toLocalFile(); #ifdef HAVE_LIBORIGIN if (Project::isLabPlotProject(f) || OriginProjectParser::isOriginProject(f)) #else if (Project::isLabPlotProject(f)) #endif openProject(f); else { if (!m_project) newProject(); importFileDialog(f); } event->accept(); } else event->ignore(); } void MainWin::handleSettingsChanges() { const KConfigGroup group = KSharedConfig::openConfig()->group( "Settings_General" ); if(dynamic_cast(centralWidget()) == nullptr) { QMdiArea::ViewMode viewMode = QMdiArea::ViewMode(group.readEntry("ViewMode", 0)); if (m_mdiArea->viewMode() != viewMode) { m_mdiArea->setViewMode(viewMode); if (viewMode == QMdiArea::SubWindowView) this->updateMdiWindowVisibility(); } if (m_mdiArea->viewMode() == QMdiArea::TabbedView) { m_tileWindows->setVisible(false); m_cascadeWindows->setVisible(false); QTabWidget::TabPosition tabPosition = QTabWidget::TabPosition(group.readEntry("TabPosition", 0)); if (m_mdiArea->tabPosition() != tabPosition) m_mdiArea->setTabPosition(tabPosition); } else { m_tileWindows->setVisible(true); m_cascadeWindows->setVisible(true); } } //autosave bool autoSave = group.readEntry("AutoSave", 0); if (m_autoSaveActive != autoSave) { m_autoSaveActive = autoSave; if (autoSave) m_autoSaveTimer.start(); else m_autoSaveTimer.stop(); } int interval = group.readEntry("AutoSaveInterval", 1); interval *= 60*1000; if (interval != m_autoSaveTimer.interval()) m_autoSaveTimer.setInterval(interval); //show memory info bool showMemoryInfo = group.readEntry(QLatin1String("ShowMemoryInfo"), true); if (m_showMemoryInfo != showMemoryInfo) { m_showMemoryInfo = showMemoryInfo; if (showMemoryInfo) { m_memoryInfoWidget = new MemoryWidget(statusBar()); statusBar()->addPermanentWidget(m_memoryInfoWidget); } else { if (m_memoryInfoWidget) { statusBar()->removeWidget(m_memoryInfoWidget); delete m_memoryInfoWidget; m_memoryInfoWidget = nullptr; } } } bool showWelcomeScreen = group.readEntry(QLatin1String("ShowWelcomeScreen"), true); if(m_showWelcomeScreen != showWelcomeScreen) { m_showWelcomeScreen = showWelcomeScreen; } } void MainWin::openDatasetExample() { newProject(); addAspectToProject(m_welcomeScreenHelper->releaseConfiguredSpreadsheet()); } /***************************************************************************************/ /************************************** dialogs ***************************************/ /***************************************************************************************/ /*! shows the dialog with the Undo-history. */ void MainWin::historyDialog() { if (!m_project->undoStack()) return; auto* dialog = new HistoryDialog(this, m_project->undoStack(), m_undoViewEmptyLabel); int index = m_project->undoStack()->index(); if (dialog->exec() != QDialog::Accepted) { if (m_project->undoStack()->count() != 0) m_project->undoStack()->setIndex(index); } //disable undo/redo-actions if the history was cleared //(in both cases, when accepted or rejected in the dialog) if (m_project->undoStack()->count() == 0) { m_undoAction->setEnabled(false); m_redoAction->setEnabled(false); } } /*! Opens the dialog to import data to the selected workbook, spreadsheet or matrix */ void MainWin::importFileDialog(const QString& fileName) { DEBUG("MainWin::importFileDialog()"); auto* dlg = new ImportFileDialog(this, false, fileName); // select existing container if (m_currentAspect->type() == AspectType::Spreadsheet || m_currentAspect->type() == AspectType::Matrix || m_currentAspect->type() == AspectType::Workbook) dlg->setCurrentIndex(m_projectExplorer->currentIndex()); else if (m_currentAspect->type() == AspectType::Column && m_currentAspect->parentAspect()->type() == AspectType::Spreadsheet) dlg->setCurrentIndex(m_aspectTreeModel->modelIndexOfAspect(m_currentAspect->parentAspect())); if (dlg->exec() == QDialog::Accepted) { dlg->importTo(statusBar()); m_project->setChanged(true); } delete dlg; DEBUG("MainWin::importFileDialog() DONE"); } void MainWin::importSqlDialog() { DEBUG("MainWin::importSqlDialog()"); auto* dlg = new ImportSQLDatabaseDialog(this); // select existing container if (m_currentAspect->type() == AspectType::Spreadsheet || m_currentAspect->type() == AspectType::Matrix || m_currentAspect->type() == AspectType::Workbook) dlg->setCurrentIndex(m_projectExplorer->currentIndex()); else if (m_currentAspect->type() == AspectType::Column && m_currentAspect->parentAspect()->type() == AspectType::Spreadsheet) dlg->setCurrentIndex(m_aspectTreeModel->modelIndexOfAspect(m_currentAspect->parentAspect())); if (dlg->exec() == QDialog::Accepted) { dlg->importTo(statusBar()); m_project->setChanged(true); } delete dlg; DEBUG("MainWin::importSqlDialog() DONE"); } void MainWin::importProjectDialog() { DEBUG("MainWin::importProjectDialog()"); ImportProjectDialog::ProjectType type; if (QObject::sender() == m_importOpjAction) type = ImportProjectDialog::ProjectOrigin; else type = ImportProjectDialog::ProjectLabPlot; auto* dlg = new ImportProjectDialog(this, type); // set current folder dlg->setCurrentFolder(m_currentFolder); if (dlg->exec() == QDialog::Accepted) { dlg->importTo(statusBar()); m_project->setChanged(true); } delete dlg; DEBUG("MainWin::importProjectDialog() DONE"); } /*! * \brief opens a dialog to import datasets */ void MainWin::importDatasetDialog() { ImportDatasetDialog* dlg = new ImportDatasetDialog(this); if (dlg->exec() == QDialog::Accepted) { Spreadsheet* spreadsheet = new Spreadsheet(i18n("Dataset%1", 1)); DatasetHandler* dataset = new DatasetHandler(spreadsheet); dlg->importToDataset(dataset, statusBar()); QTimer timer; timer.setSingleShot(true); QEventLoop loop; connect(dataset, &DatasetHandler::downloadCompleted, &loop, &QEventLoop::quit); connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); timer.start(1500); loop.exec(); if(timer.isActive()){ timer.stop(); addAspectToProject(spreadsheet); delete dataset; } else delete dataset; } delete dlg; } /*! opens the dialog for the export of the currently active worksheet, spreadsheet or matrix. */ void MainWin::exportDialog() { QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return; AbstractPart* part = dynamic_cast(win)->part(); if (part->exportView()) statusBar()->showMessage(i18n("%1 exported", part->name())); } void MainWin::editFitsFileDialog() { auto* editDialog = new FITSHeaderEditDialog(this); if (editDialog->exec() == QDialog::Accepted) { if (editDialog->saved()) statusBar()->showMessage(i18n("FITS files saved")); } } /*! adds a new file data source to the current project. */ void MainWin::newLiveDataSourceActionTriggered() { ImportFileDialog* dlg = new ImportFileDialog(this, true); if (dlg->exec() == QDialog::Accepted) { if (static_cast(dlg->sourceType()) == LiveDataSource::MQTT) { #ifdef HAVE_MQTT MQTTClient* mqttClient = new MQTTClient(i18n("MQTT Client%1", 1)); dlg->importToMQTT(mqttClient); mqttClient->setName(mqttClient->clientHostName()); QVector existingClients = m_project->children(AbstractAspect::Recursive); //doesn't make sense to have more MQTTClients connected to the same broker bool found = false; for (const auto* client : existingClients) { if (client->clientHostName() == mqttClient->clientHostName() && client->clientPort() == mqttClient->clientPort()) { found = true; break; } } if (!found) addAspectToProject(mqttClient); else { delete mqttClient; QMessageBox::warning(this, "Warning", "There already is a MQTTClient with this host!"); } #endif } else { LiveDataSource* dataSource = new LiveDataSource(i18n("Live data source%1", 1), false); dlg->importToLiveDataSource(dataSource, statusBar()); addAspectToProject(dataSource); } } delete dlg; } 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 8f04924f8..0b1154ff9 100644 --- a/src/kdefrontend/WelcomeScreenHelper.cpp +++ b/src/kdefrontend/WelcomeScreenHelper.cpp @@ -1,452 +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 +#include +#include + #include -#include -#include +#include +#include +#include /*! \class WelcomeScreenHelper \brief Helper class for the welcome screen \ingroup kdefrontend */ WelcomeScreenHelper::WelcomeScreenHelper() { m_datasetWidget = new ImportDatasetWidget(0); m_datasetWidget->hide(); + QIcon icon = QIcon::fromTheme("labplot-maximize"); m_maxIcon = icon.pixmap(icon.availableSizes().first()); icon = QIcon::fromTheme("labplot-minimize"); m_minIcon = icon.pixmap(icon.availableSizes().first()); m_datasetModel = new DatasetModel(m_datasetWidget->getDatasetsMap()); loadConfig(); processExampleProjects(); } WelcomeScreenHelper::~WelcomeScreenHelper() { //save width&height ratio values KConfigGroup conf(KSharedConfig::openConfig(), "WelcomeScreenHelper"); int widthCount = m_widthScale.size(); conf.writeEntry("width_count", widthCount); int currentWidthIndex = 0; for(auto item = m_widthScale.begin(); item != m_widthScale.end() && currentWidthIndex < widthCount; item++) { conf.writeEntry("widthName_" + QString::number(currentWidthIndex), item.key()); conf.writeEntry("widthValue_" + QString::number(currentWidthIndex), QString::number(item.value())); currentWidthIndex++; } int heightCount = m_heightScale.size(); conf.writeEntry("height_count", widthCount); int currentHeightIndex = 0; for(auto item = m_heightScale.begin(); item != m_heightScale.end() && currentHeightIndex < heightCount; item++) { conf.writeEntry("heightName_" + QString::number(currentHeightIndex), item.key()); conf.writeEntry("heightValue_" + QString::number(currentHeightIndex), QString::number(item.value())); currentHeightIndex++; } } /** * @brief Loads the saved configuration */ void WelcomeScreenHelper::loadConfig() { KConfigGroup conf(KSharedConfig::openConfig(), "WelcomeScreenHelper"); int widthCount = conf.readEntry("width_count", -1); for(int i = 0; i < widthCount; ++i) { QString id = conf.readEntry("widthName_" + QString::number(i), ""); double value = QString(conf.readEntry("widthValue_" + QString::number(i), "-1")).toDouble(); if(!id.isEmpty() && value != -1) m_widthScale[id] = value; } int heightCount = conf.readEntry("height_count", -1); for(int i = 0; i < heightCount; ++i) { QString id = conf.readEntry("heightName_" + QString::number(i), ""); double value = QString(conf.readEntry("heightValue_" + QString::number(i), "-1")).toDouble(); if(!id.isEmpty() && value != -1) m_heightScale[id] = value; } } /** * @brief Handles a dataset being clicked in the dataset section of the WelcomeScreen. * Initiates listing information about the dataset in the previously mentioned section. * * @param category the category the dataset belongs to * @param subcategory the subcategory the dataset belongs to * @param datasetName the name of the dataset */ void WelcomeScreenHelper::datasetClicked(const QString& category, const QString& subcategory, const QString& datasetName) { m_datasetWidget->setCollection("All"); m_datasetWidget->setCategory(category); m_datasetWidget->setSubcategory(subcategory); m_datasetWidget->setDataset(datasetName); m_spreadsheet.reset(new Spreadsheet(i18n("Dataset%1", 1))); - if(m_datasetHandler != nullptr) + if(m_datasetHandler) delete m_datasetHandler; m_datasetHandler = new DatasetHandler(m_spreadsheet.get()); m_datasetWidget->import(m_datasetHandler); QTimer timer; timer.setSingleShot(true); QEventLoop loop; connect(m_datasetHandler, &DatasetHandler::downloadCompleted, &loop, &QEventLoop::quit); connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); timer.start(1500); loop.exec(); if(timer.isActive()){ timer.stop(); emit datasetFound(); - } - else + } else emit datasetNotFound(); } /** * @brief Returns the dataset's full name. */ QVariant WelcomeScreenHelper::datasetName() { return QVariant(m_spreadsheet->name()); } /** * @brief Returns the dataset's descripton. */ QVariant WelcomeScreenHelper::datasetDescription() { return QVariant(m_spreadsheet->comment()); } /** * @brief Returns the number of the dataset's columns. */ QVariant WelcomeScreenHelper::datasetColumns() { return QVariant(m_spreadsheet->columnCount()); } /** * @brief Returns the number of the dataset's rows. */ QVariant WelcomeScreenHelper::datasetRows() { return QVariant(m_spreadsheet->rowCount()); } /** * @brief Returns a pointer to the spreadsheet in which the data of the dataset was loaded. */ Spreadsheet* WelcomeScreenHelper::releaseConfiguredSpreadsheet() { return m_spreadsheet.release(); } /** * @brief Returns the thumbnail image saved with the project. * @param url the path to the saved project file. */ QVariant WelcomeScreenHelper::getProjectThumbnail(const QUrl& url) { QString filename; if (url.isLocalFile()) // fix for Windows filename = url.toLocalFile(); else filename = url.path(); QIODevice* file; // first try gzip compression, because projects can be gzipped and end with .lml if (filename.endsWith(QLatin1String(".lml"), Qt::CaseInsensitive)) file = new KCompressionDevice(filename,KFilterDev::compressionTypeForMimeType("application/x-gzip")); else // opens filename using file ending file = new KFilterDev(filename); if (!file) file = new QFile(filename); if (!file->open(QIODevice::ReadOnly)) { qDebug() << "Could not open file for reading."; return QVariant(); } char c; bool rc = file->getChar(&c); if (!rc) { qDebug() << "The project file is empty."; file->close(); delete file; return false; } file->seek(0); //parse XML XmlStreamReader reader(file); while (!(reader.isStartDocument() || reader.atEnd())) reader.readNext(); if (!(reader.atEnd())) { if (!reader.skipToNextTag()) return false; if (reader.name() == "project") { QString thumbnail = reader.attributes().value("thumbnail").toString(); thumbnail.prepend("data:image/jpg;base64,"); return QVariant(thumbnail); } } return QVariant(); } /** * @brief Returns a pointer to the datasetModel of the class. */ DatasetModel* WelcomeScreenHelper::getDatasetModel() { return m_datasetModel; } /** * @brief Processes metadata file containing example projects. */ void WelcomeScreenHelper::processExampleProjects() { const QString filePath = QStandardPaths::locate(QStandardPaths::AppDataLocation, "example_projects/example_projects.json"); QFile file(filePath); if (file.open(QIODevice::ReadOnly)) { QJsonDocument document = QJsonDocument::fromJson(file.readAll()); QJsonArray exampleArray = document.array(); //processing examples for(int i = 0 ; i < exampleArray.size(); ++i) { const QJsonObject currentExample = exampleArray[i].toObject(); const QString exampleName = currentExample.value("name").toString(); if(m_projectNameList.contains(exampleName)) { qDebug() << "There is already an example file with this name"; } else { m_projectNameList.append(exampleName); const QString exampleFile = currentExample.value("fileName").toString(); m_pathMap[exampleName] = exampleFile; //processing tags const QJsonArray tags = currentExample.value("tags").toArray(); for(int j = 0; j < tags.size(); ++j) { QString tagName = tags[j].toString(); m_tagMap[tagName].append(exampleName); m_datasetTag[exampleName].append(tagName); } } } file.close(); } else { qDebug("Couldn't open dataset category file"); } } /** * @brief Returns in string format the thumbnail for the given example file */ QVariant WelcomeScreenHelper::getExampleProjectThumbnail(const QString& exampleName) { const QString filePath = QStandardPaths::locate(QStandardPaths::AppDataLocation, "example_projects/" + m_pathMap[exampleName]); return getProjectThumbnail(filePath); } /** * @brief Returns the list of example projects. */ QVariant WelcomeScreenHelper::getExampleProjects() { return QVariant(m_projectNameList); } /** * @brief Returns the tags of the given example project. */ QVariant WelcomeScreenHelper::getExampleProjectTags(const QString& exampleName) { QString tags; const QStringList& tagList = m_datasetTag[exampleName]; for(int i = 0; i < tagList.size(); i++) { tags.append(tagList[i]); if(i < tagList.size() - 1) tags.append(", "); } return QVariant(tags); } /** * @brief Handles an example project being clicked in the welcome screen. * @param exampleName the name of the clicked example project */ void WelcomeScreenHelper::exampleProjectClicked(const QString& exampleName) { QString path = QStandardPaths::locate(QStandardPaths::AppDataLocation, "example_projects/" + m_pathMap[exampleName]); emit openExampleProject(path); } /** * @brief Searches among the example projects based on the text introduce in the search bar of the welcome screen. * @param searchtext - the text based on which we'll have to search * @return Returns the results */ QVariant WelcomeScreenHelper::searchExampleProjects(const QString& searchtext) { QStringList results; //search based on tags for(auto tag = m_tagMap.begin(); tag != m_tagMap.end(); tag++) { if (tag.key().contains(searchtext)) { for(QString example : tag.value()) { if(!results.contains(example)) results.append(example); } } } //search based on name for(QString example : m_projectNameList) { if(example.contains(searchtext) && !results.contains(example)) results.append(example); } return QVariant(results); } /** * @brief Sets the width scale for the given section, which will be saved. */ void WelcomeScreenHelper::setWidthScale(QString sectionID, double scale) { m_widthScale[sectionID] = scale; } /** * @brief Sets the height scale for the given section, which will be saved. */ void WelcomeScreenHelper::setHeightScale(QString sectionID, double scale) { m_heightScale[sectionID] = scale; } /** * @brief Returns the width scale for the given section. */ QVariant WelcomeScreenHelper::getWidthScale(QString sectionID) { - if(m_widthScale.keys().contains(sectionID)) { + if(m_widthScale.keys().contains(sectionID)) return QVariant(m_widthScale[sectionID]); - } return QVariant(-1); } /** * @brief Returns the height scale for the given section. */ QVariant WelcomeScreenHelper::getHeightScale(QString sectionID) { - if(m_heightScale.keys().contains(sectionID)) { + if(m_heightScale.keys().contains(sectionID)) return QVariant(m_heightScale[sectionID]); - } return QVariant(-1);; } /** * @brief Returns the maximize icon. */ QVariant WelcomeScreenHelper::getMaxIcon() { QByteArray bArray; QBuffer buffer(&bArray); buffer.open(QIODevice::WriteOnly); m_maxIcon.save(&buffer, "PNG"); QString image = QString::fromLatin1(bArray.toBase64().data()); image.prepend("data:image/png;base64,"); return QVariant(image); } /** * @brief Returns the minimize icon. */ QVariant WelcomeScreenHelper::getMinIcon() { QByteArray bArray; QBuffer buffer(&bArray); buffer.open(QIODevice::WriteOnly); m_minIcon.save(&buffer, "PNG"); QString image = QString::fromLatin1(bArray.toBase64().data()); image.prepend("data:image/png;base64,"); return QVariant(image); } /** * @brief Returns the go-back icon. */ QVariant WelcomeScreenHelper::getBackIcon() { QIcon icon = QIcon::fromTheme("labplot-back"); QPixmap pixmap = icon.pixmap(icon.availableSizes().first()); QByteArray bArray; QBuffer buffer(&bArray); buffer.open(QIODevice::WriteOnly); pixmap.save(&buffer, "PNG"); QString image = QString::fromLatin1(bArray.toBase64().data()); image.prepend("data:image/png;base64,"); return QVariant(image); } /** * @brief Returns the go-forward icon. */ QVariant WelcomeScreenHelper::getForwardIcon() { QIcon icon = QIcon::fromTheme("labplot-forward"); QPixmap pixmap = icon.pixmap(icon.availableSizes().first()); QByteArray bArray; QBuffer buffer(&bArray); buffer.open(QIODevice::WriteOnly); pixmap.save(&buffer, "PNG"); QString image = QString::fromLatin1(bArray.toBase64().data()); image.prepend("data:image/png;base64,"); return QVariant(image); } diff --git a/src/kdefrontend/WelcomeScreenHelper.h b/src/kdefrontend/WelcomeScreenHelper.h index 54b841c59..5caf73bd6 100644 --- a/src/kdefrontend/WelcomeScreenHelper.h +++ b/src/kdefrontend/WelcomeScreenHelper.h @@ -1,99 +1,98 @@ /*************************************************************************** File : WelcomeScreenHelper.h 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 * * * ***************************************************************************/ #ifndef WELCOMESCREENHELPER_H #define WELCOMESCREENHELPER_H #include #include #include #include #include class ImportDatasetWidget; class DatasetHandler; class Spreadsheet; class DatasetModel; class WelcomeScreenHelper: public QObject { Q_OBJECT public: WelcomeScreenHelper(); ~WelcomeScreenHelper(); Q_INVOKABLE QVariant datasetName(); Q_INVOKABLE QVariant datasetDescription(); Q_INVOKABLE QVariant datasetColumns(); Q_INVOKABLE QVariant datasetRows(); - Q_INVOKABLE QVariant getProjectThumbnail(const QUrl& url); + Q_INVOKABLE QVariant getProjectThumbnail(const QUrl&); Q_INVOKABLE QVariant getExampleProjectThumbnail(const QString&); Q_INVOKABLE QVariant getExampleProjects(); Q_INVOKABLE QVariant searchExampleProjects(const QString& searchtext); Q_INVOKABLE QVariant getExampleProjectTags(const QString&); Q_INVOKABLE void setWidthScale(QString sectionID, double scale); Q_INVOKABLE void setHeightScale(QString sectionID, double scale); Q_INVOKABLE QVariant getWidthScale(QString sectionID); Q_INVOKABLE QVariant getHeightScale(QString sectionID); Q_INVOKABLE QVariant getMaxIcon(); Q_INVOKABLE QVariant getMinIcon(); Q_INVOKABLE QVariant getBackIcon(); Q_INVOKABLE QVariant getForwardIcon(); - Spreadsheet* releaseConfiguredSpreadsheet(); DatasetModel* getDatasetModel(); public slots: void datasetClicked(const QString& category, const QString& subcategory, const QString& datasetName); void exampleProjectClicked(const QString&); private: DatasetModel* m_datasetModel{nullptr}; ImportDatasetWidget* m_datasetWidget{nullptr}; DatasetHandler* m_datasetHandler{nullptr}; mutable std::unique_ptr m_spreadsheet{nullptr}; QStringList m_projectNameList; QMap m_tagMap; QMap m_datasetTag; QMap m_pathMap; QMap m_widthScale; QMap m_heightScale; QPixmap m_maxIcon; QPixmap m_minIcon; void processExampleProjects(); void loadConfig(); signals: void datasetFound(); void datasetNotFound(); void showFirstDataset(); void openExampleProject(QString); }; #endif //WELCOMESCREENHELPER_H diff --git a/src/kdefrontend/main.qml b/src/kdefrontend/main.qml index 9dfe7e2d2..073e09b61 100644 --- a/src/kdefrontend/main.qml +++ b/src/kdefrontend/main.qml @@ -1,1992 +1,1978 @@ import QtQuick.Window 2.12 import QtQuick 2.6 import QtQuick.Controls 2.4 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.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") if(recentProjectsFrame.fullScreen) recentProjectsFrame.minimize() if(exampleProjects.fullScreen) exampleProjects.minimize() if(newsSection.fullScreen) newsSection.minimize() if(helpFrame.fullScreen) helpFrame.minimize() if(datasetFrame.fullScreen) datasetFrame.minimize() if(releaseSection.fullScreen) releaseSection.minimize() 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() datasetDescription.text = helper.datasetDescription() 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" 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: { if(helper.getWidthScale(sectionName) === -1 || helper.getHeightScale(sectionName) === -1) mainWindow.restoreOriginalLayout() } function maximize() { hideTiles() 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.widthRate = 1 recentProjectsFrame.heightRate = 1 recentProjectsFrame.anchors.fill = mainWindow } function minimize() { 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.widthRate = recentProjectsFrame.prevWidth recentProjectsFrame.heightRate = recentProjectsFrame.prevHeight showTiles(); } - 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.widthRate = (recentProjectsFrame.width + mouseX) / mainWindow.width exampleProjects.widthRate = (exampleProjects.width - mouseX) / mainWindow.width if(recentProjectsFrame.width < 150){ recentProjectsFrame.widthRate = 150 / mainWindow.width exampleProjects.widthRate = (mainWindow.width - newsSection.width - 150 - 4*mainWindow.spacing) / mainWindow.width } if(exampleProjects.width < 300) { 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.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.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.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, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) Layout.minimumHeight: recentProjectsFrame.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) Layout.preferredWidth: recentProjectsFrame.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) Layout.minimumWidth: recentProjectsFrame.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) 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, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) sourceSize.height: recentProjectsFrame.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) MouseArea { anchors.fill: parent onClicked: { - if(!recentProjectsFrame.fullScreen) { + if(!recentProjectsFrame.fullScreen) recentProjectsFrame.maximize() - } else { + else recentProjectsFrame.minimize() - } 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" 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() } function maximize() { hideTiles() 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 } function minimize() { 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.widthRate = exampleProjects.prevWidth exampleProjects.heightRate = exampleProjects.prevHeight showTiles(); } 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.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.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.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.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.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.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.widthRate = (exampleProjects.width - mouseX)/ mainWindow.width recentProjectsFrame.widthRate = (recentProjectsFrame.width + mouseX) / mainWindow.width if(recentProjectsFrame.width < 150){ recentProjectsFrame.widthRate = 150 / mainWindow.width exampleProjects.widthRate = (mainWindow.width - newsSection.width - 150 - 4*mainWindow.spacing) / mainWindow.width } if(exampleProjects.width < 300) { 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, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) Layout.minimumHeight: exampleProjects.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) Layout.preferredWidth: exampleProjects.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) Layout.minimumWidth: exampleProjects.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) 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, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) sourceSize.height: exampleProjects.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) MouseArea { anchors.fill: parent onClicked: { if(!exampleProjects.fullScreen) { exampleProjects.maximize() } else { exampleProjects.minimize() } 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" 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() } function maximize() { hideTiles() 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 } function minimize() { 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.widthRate = newsSection.prevWidth newsSection.heightRate = newsSection.prevHeight showTiles(); } 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.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.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.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, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) Layout.minimumHeight: newsSection.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) Layout.preferredWidth: newsSection.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) Layout.minimumWidth:newsSection.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) 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, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) sourceSize.height: newsSection.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) MouseArea { anchors.fill: parent onClicked: { - if(!newsSection.fullScreen) { + if(!newsSection.fullScreen) newsSection.maximize() - } else { + else newsSection.minimize() - } 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" 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() } function maximize() { hideTiles() 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 } function minimize(){ 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.widthRate = helpFrame.prevWidth helpFrame.heightRate = helpFrame.prevHeight showTiles(); } 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.widthRate = (helpFrame.width + mouseX) / mainWindow.width datasetFrame.widthRate = (datasetFrame.width - mouseX) / mainWindow.width if(helpFrame.width < 150){ helpFrame.widthRate = 150 / mainWindow.width datasetFrame.widthRate = (mainWindow.width - newsSection.width - 150 - 4*mainWindow.spacing) / mainWindow.width } if(datasetFrame.width < 150) { 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.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.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.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.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.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.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, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) Layout.minimumHeight: helpFrame.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) Layout.preferredWidth: helpFrame.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) Layout.minimumWidth: helpFrame.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) 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, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) sourceSize.height: helpFrame.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) MouseArea { anchors.fill: parent onClicked: { - if(!helpFrame.fullScreen) { + if(!helpFrame.fullScreen) helpFrame.maximize() - } else { + else helpFrame.minimize() - } 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" 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 function maximize() { hideTiles() 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 } function minimize() { 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.widthRate = datasetFrame.prevWidth datasetFrame.heightRate = datasetFrame.prevHeight showTiles(); } 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.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.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.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.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.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.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.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.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.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.widthRate = (helpFrame.width + mouseX) / mainWindow.width datasetFrame.widthRate = (datasetFrame.width - mouseX) / mainWindow.width if(helpFrame.width < 150){ helpFrame.widthRate = 150 / mainWindow.width datasetFrame.widthRate = (mainWindow.width - newsSection.width - 150 - 4*mainWindow.spacing) / mainWindow.width } if(datasetFrame.width < 150) { 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, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) Layout.minimumHeight: datasetFrame.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) Layout.preferredWidth: datasetFrame.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) Layout.minimumWidth: datasetFrame.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) 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, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) sourceSize.height: datasetFrame.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) MouseArea { anchors.fill: parent onClicked: { - if(!datasetFrame.fullScreen) { + if(!datasetFrame.fullScreen) datasetFrame.maximize() - } else { + else datasetFrame.minimize() - } 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 spacing: 10 Layout.fillHeight: true property int textWidth: 100 clip: true ScrollBar.vertical: ScrollBar { } 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 anchors.fill: parent Rectangle { id: categoryBullet Layout.alignment: Qt.AlignVCenter 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: { if(index == 0) { mainWindow.currentCategory = categoryDelegate.categoryName categoryDelegate.ListView.view.currentIndex = index } if(categoryList.textWidth < paintedWidth + categoryBullet.width + categoryRow.spacing) categoryList.textWidth = paintedWidth + categoryBullet.width + categoryRow.spacing } } } MouseArea { anchors.fill: parent onClicked: { categoryDelegate.ListView.view.currentIndex = index if (mainWindow.currentCategory != categoryName) mainWindow.currentCategory = categoryName 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 Layout.alignment: Qt.AlignVCenter 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: { if(index == 0) { mainWindow.currentSubcategory = subcategoryDelegate.subcategoryName subcategoryDelegate.ListView.view.currentIndex = index } if(subcategoryList.textWidth < paintedWidth + subcategoryBullet.width + subcategoryRow.spacing) { subcategoryList.textWidth = paintedWidth + subcategoryBullet.width + subcategoryRow.spacing } } } } 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: { datasetDelegate.textHeight = paintedHeight datasetDelegate.textWidth = paintedWidth + datasetBullet.width + datasetRow.spacing if(index == 0) { datasetGrid.currentIndex = index mainWindow.currentDataset = datasetDelegate.datasetName mainWindow.datasetClicked(mainWindow.currentCategory, mainWindow.currentSubcategory, datasetDelegate.datasetName) } } } } MouseArea { anchors.fill: parent onClicked: { if(datasetFrame.fullScreen) datasetFrame.minimize() datasetGrid.currentIndex = index console.log("Dataset name: " + datasetDelegate.datasetName + "Clicked") mainWindow.currentDataset = datasetDelegate.datasetName mainWindow.datasetClicked(mainWindow.currentCategory, mainWindow.currentSubcategory, datasetDelegate.datasetName) } } } } 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 Layout.minimumWidth: scrollView.width Layout.preferredWidth: scrollView.width width: scrollView.width 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 } } 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() } } } } } } } } Frame { id: releaseSection property string sectionName: "releaseSection" 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 function maximize() { hideTiles() 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 } function minimize() { 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.widthRate = releaseSection.prevWidth releaseSection.heightRate = releaseSection.prevHeight showTiles(); } function updateIcons() { if(releaseWebView.canGoBack) backIcon.opacity = 1 else backIcon.opacity = 0.5 if(releaseWebView.canGoForward) forwardIcon.opacity = 1 else forwardIcon.opacity = 0.5 } 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.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.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 / 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.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.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.heightRate = 100 / mainWindow.height datasetFrame.heightRate = 100 / mainWindow.height releaseSection.heightRate = (mainWindow.height - recentProjectsFrame.height - 100 - 4*mainWindow.spacing) / mainWindow.height } } } } } + // "What's new in this release" section 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, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) Layout.minimumHeight: releaseSection.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) Layout.preferredWidth: releaseSection.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) Layout.minimumWidth: releaseSection.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) 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, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) sourceSize.height: releaseSection.fullScreen ?Math.min(Math.min(parent.height, parent.width) * 0.5, 25) : Math.min(Math.min(parent.height, parent.width) * 0.5, 15) MouseArea { anchors.fill: parent onClicked: { - if(!releaseSection.fullScreen) { + if(!releaseSection.fullScreen) releaseSection.maximize() - } else { + else releaseSection.minimize() - } releaseSection.fullScreen = !releaseSection.fullScreen } } } Image { id: backIcon 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: { + onClicked: releaseWebView.goBack() - } } } Image { id: forwardIcon 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: { + 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.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 - Component.onCompleted: { + Component.onCompleted: releaseSection.updateIcons() - } - onCanGoBackChanged: { + onCanGoBackChanged: releaseSection.updateIcons() - } - onCanGoForwardChanged: { + onCanGoForwardChanged: releaseSection.updateIcons() - } } } } }