labplot  2.8.2
About: LabPlot is an application for plotting and analysis of 2D and 3D functions and data. It is a complete rewrite of LabPlot1 and lacks in the first release a lot of features available in the predecessor. On the other hand, the GUI and the usability is more superior.
  Fossies Dox: labplot-2.8.2.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

MainWin.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  File : MainWin.cc
3  Project : LabPlot
4  Description : Main window of the application
5  --------------------------------------------------------------------
6  Copyright : (C) 2008-2018 Stefan Gerlach (stefan.gerlach@uni.kn)
7  Copyright : (C) 2009-2020 Alexander Semke (alexander.semke@web.de)
8 
9  ***************************************************************************/
10 
11 /***************************************************************************
12  * *
13  * This program is free software; you can redistribute it and/or modify *
14  * it under the terms of the GNU General Public License as published by *
15  * the Free Software Foundation; either version 2 of the License, or *
16  * (at your option) any later version. *
17  * *
18  * This program is distributed in the hope that it will be useful, *
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
21  * GNU General Public License for more details. *
22  * *
23  * You should have received a copy of the GNU General Public License *
24  * along with this program; if not, write to the Free Software *
25  * Foundation, Inc., 51 Franklin Street, Fifth Floor, *
26  * Boston, MA 02110-1301 USA *
27  * *
28  ***************************************************************************/
29 
30 #include "MainWin.h"
31 
32 #include "backend/core/Project.h"
33 #include "backend/core/Folder.h"
35 #include "backend/core/Workbook.h"
37 #include "backend/matrix/Matrix.h"
41 #ifdef HAVE_LIBORIGIN
43 #endif
44 #ifdef HAVE_CANTOR_LIBS
46 #endif
48 #include "backend/note/Note.h"
49 #include "backend/lib/macros.h"
51 
52 #ifdef HAVE_MQTT
54 #endif
55 
61 #ifdef HAVE_CANTOR_LIBS
63 #endif
68 
81 #include "DatasetModel.h"
82 // #include "welcomescreen/WelcomeScreenHelper.h"
83 
84 #ifdef HAVE_KUSERFEEDBACK
85 #include <KUserFeedback/ApplicationVersionSource>
86 #include <KUserFeedback/PlatformInfoSource>
87 #include <KUserFeedback/QtVersionSource>
88 #include <KUserFeedback/ScreenInfoSource>
89 #include <KUserFeedback/StartCountSource>
90 #include <KUserFeedback/UsageTimeSource>
91 #endif
92 
93 #ifdef Q_OS_MAC
94 #include "3rdparty/kdmactouchbar/src/kdmactouchbar.h"
95 #endif
96 
97 #include <QMdiArea>
98 #include <QMenu>
99 #include <QDockWidget>
100 #include <QStackedWidget>
101 #include <QUndoStack>
102 #include <QCloseEvent>
103 #include <QFileDialog>
104 #include <QMimeData>
105 #include <QElapsedTimer>
106 #include <QHash>
107 #include <QStatusBar>
108 #include <QTemporaryFile>
109 #include <QTimeLine>
110 // #include <QtWidgets>
111 // #include <QtQuickWidgets/QQuickWidget>
112 // #include <QQuickItem>
113 // #include <QQuickView>
114 // #include <QQmlApplicationEngine>
115 // #include <QQmlContext>
116 
117 #include <KActionCollection>
118 #include <KConfigGroup>
119 #include <KStandardAction>
120 #include <kxmlguifactory.h>
121 #include <KMessageBox>
122 #include <KToolBar>
123 #include <KLocalizedString>
124 #include <KFilterDev>
125 #include <KRecentFilesAction>
126 #include <KActionMenu>
127 #include <KColorScheme>
128 #include <KColorSchemeManager>
129 #include <KToggleFullScreenAction>
130 #include <kconfigwidgets_version.h>
131 
132 #ifdef HAVE_CANTOR_LIBS
133 #include <cantor/backend.h>
134 #include <KConfigDialog>
135 #include <KCoreConfigSkeleton>
136 #include <KConfigSkeleton>
137 
138 //required to parse Cantor and Jupyter files
139 #include <QBuffer>
140 #include <KZip>
141 #include <QJsonDocument>
142 #include <QJsonParseError>
143 #endif
144 
145 /*!
146 \class MainWin
147 \brief Main application window.
148 
149 \ingroup kdefrontend
150 */
151 MainWin::MainWin(QWidget *parent, const QString& filename)
152  : KXmlGuiWindow(parent),
153  m_schemeManager(new KColorSchemeManager(this)) {
154 
155  initGUI(filename);
156  setAcceptDrops(true);
157 
158 #ifdef HAVE_KUSERFEEDBACK
159  m_userFeedbackProvider.setProductIdentifier(QStringLiteral("org.kde.labplot"));
160  m_userFeedbackProvider.setFeedbackServer(QUrl(QStringLiteral("https://telemetry.kde.org/")));
161  m_userFeedbackProvider.setSubmissionInterval(7);
162  m_userFeedbackProvider.setApplicationStartsUntilEncouragement(5);
163  m_userFeedbackProvider.setEncouragementDelay(30);
164 
165  // software version info
166  m_userFeedbackProvider.addDataSource(new KUserFeedback::ApplicationVersionSource);
167  m_userFeedbackProvider.addDataSource(new KUserFeedback::QtVersionSource);
168 
169  // info about the machine
170  m_userFeedbackProvider.addDataSource(new KUserFeedback::PlatformInfoSource);
171  m_userFeedbackProvider.addDataSource(new KUserFeedback::ScreenInfoSource);
172 
173  // usage info
174  m_userFeedbackProvider.addDataSource(new KUserFeedback::StartCountSource);
175  m_userFeedbackProvider.addDataSource(new KUserFeedback::UsageTimeSource);
176 #endif
177 }
178 
180  //save the current settings in MainWin
181  m_recentProjectsAction->saveEntries( KSharedConfig::openConfig()->group("Recent Files") );
182 
183  KConfigGroup group = KSharedConfig::openConfig()->group("MainWin");
184  group.writeEntry(QLatin1String("geometry"), saveGeometry());
185  group.writeEntry(QLatin1String("WindowState"), saveState());
186  group.writeEntry(QLatin1String("lastOpenFileFilter"), m_lastOpenFileFilter);
187  group.writeEntry(QLatin1String("ShowMemoryInfo"), (m_memoryInfoWidget != nullptr));
188  KSharedConfig::openConfig()->sync();
189 
190  //if welcome screen is shown, save its settings prior to deleting it
191 // if (dynamic_cast<QQuickWidget*>(centralWidget()))
192 // QMetaObject::invokeMethod(m_welcomeWidget->rootObject(), "saveWidgetDimensions");
193 
194  if (m_project) {
195 // if (dynamic_cast<QQuickWidget*>(centralWidget()) == nullptr)
196 // m_mdiArea->closeAllSubWindows();
197 
198  disconnect(m_project, nullptr, this, nullptr);
199  delete m_project;
200  }
201 
202  if (m_aspectTreeModel)
203  delete m_aspectTreeModel;
204 
205  if (m_guiObserver)
206  delete m_guiObserver;
207 
208 // if (m_welcomeScreenHelper)
209 // delete m_welcomeScreenHelper;
210 }
211 
213  const Worksheet* w = dynamic_cast<Worksheet*>(m_currentAspect);
214  if (w) {
215  auto* view = static_cast<WorksheetView*>(w->view());
216  view->presenterMode();
217  } else {
218  //currently active object is not a worksheet but we're asked to start in the presenter mode
219  //determine the first available worksheet and show it in the presenter mode
220  auto worksheets = m_project->children<Worksheet>();
221  if (worksheets.size() > 0) {
222  auto* view = static_cast<WorksheetView*>(worksheets.constFirst()->view());
223  view->presenterMode();
224  } else {
225  QMessageBox::information(this, i18n("Presenter Mode"),
226  i18n("No worksheets are available in the project. The presenter mode will not be started."));
227  }
228  }
229 }
230 
232  return m_aspectTreeModel;
233 }
234 
236  return m_project;
237 }
238 
239 void MainWin::initGUI(const QString& fileName) {
240  statusBar()->showMessage(i18nc("%1 is the LabPlot version", "Welcome to LabPlot %1", QLatin1String(LVERSION)));
241 
242  initActions();
243 
244 #ifdef Q_OS_MAC
245  // setup touchbar before GUI (otherwise actions in the toolbar are not selectable)
246  m_touchBar = new KDMacTouchBar(this);
247  setupGUI(Default, QLatin1String("/Applications/labplot2.app/Contents/Resources/labplot2ui.rc"));
248  //m_touchBar->setTouchButtonStyle(KDMacTouchBar::IconOnly);
249 #else
250  setupGUI(Default, KXMLGUIClient::xmlFile()); // should be "labplot2ui.rc"
251 #endif
252 
253  DEBUG(Q_FUNC_INFO << ", Component name: " << STDSTRING(KXMLGUIClient::componentName()));
254  DEBUG(Q_FUNC_INFO << ", XML file: " << STDSTRING(KXMLGUIClient::xmlFile()) << " (should be \"labplot2ui.rc\")");
255 
256  //all toolbars created via the KXMLGUI framework are locked on default:
257  // * on the very first program start, unlock all toolbars
258  // * on later program starts, set stored lock status
259  //Furthermore, we want to show icons only after the first program start.
260  KConfigGroup groupMain = KSharedConfig::openConfig()->group("MainWindow");
261  if (groupMain.exists()) {
262  //KXMLGUI framework automatically stores "Disabled" for the key "ToolBarsMovable"
263  //in case the toolbars are locked -> load this value
264  const QString& str = groupMain.readEntry(QLatin1String("ToolBarsMovable"), "");
265  bool locked = (str == QLatin1String("Disabled"));
266  KToolBar::setToolBarsLocked(locked);
267  } else {
268  //first start
269  KToolBar::setToolBarsLocked(false);
270 
271  //show icons only
272  for (auto* container : factory()->containers(QLatin1String("ToolBar"))) {
273  auto* toolbar = dynamic_cast<QToolBar*>(container);
274  if (toolbar)
275  toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly);
276  }
277  }
278 
279  initMenus();
280 
281  auto* mainToolBar = qobject_cast<QToolBar*>(factory()->container("main_toolbar", this));
282  if (!mainToolBar) {
283  QMessageBox::critical(this, i18n("GUI configuration file not found"), i18n("%1 file was not found. Please check your installation.", KXMLGUIClient::xmlFile()));
284  //TODO: the application is not really usable if the rc file was not found. We should quit the application. The following line crashes
285  //the application because of the splash screen. We need to find another solution.
286 // QMetaObject::invokeMethod(this, "close", Qt::QueuedConnection); //call close as soon as we enter the eventloop
287  return;
288  }
289 
290  auto* tbImport = new QToolButton(mainToolBar);
291  tbImport->setPopupMode(QToolButton::MenuButtonPopup);
292  tbImport->setMenu(m_importMenu);
293  tbImport->setDefaultAction(m_importFileAction);
294  auto* lastAction = mainToolBar->actions().at(mainToolBar->actions().count() - 1);
295  mainToolBar->insertWidget(lastAction, tbImport);
296 
297  qobject_cast<QMenu*>(factory()->container("import", this))->setIcon(QIcon::fromTheme("document-import"));
298  setWindowIcon(QIcon::fromTheme("LabPlot2", QGuiApplication::windowIcon()));
299  setAttribute( Qt::WA_DeleteOnClose );
300 
301  //make the status bar of a fixed size in order to avoid height changes when placing a ProgressBar there.
302  QFont font;
303  font.setFamily(font.defaultFamily());
304  QFontMetrics fm(font);
305  statusBar()->setFixedHeight(fm.height() + 5);
306 
307  //load recently used projects
308  m_recentProjectsAction->loadEntries( KSharedConfig::openConfig()->group("Recent Files") );
309 
310  //General Settings
311  const KConfigGroup& group = KSharedConfig::openConfig()->group("Settings_General");
312 
313  //title bar
314  m_titleBarMode = static_cast<MainWin::TitleBarMode>(group.readEntry("TitleBar", 0));
315 
316  //auto-save
317  m_autoSaveActive = group.readEntry<bool>("AutoSave", false);
318  int interval = group.readEntry("AutoSaveInterval", 1);
319  interval = interval*60*1000;
320  m_autoSaveTimer.setInterval(interval);
321  connect(&m_autoSaveTimer, &QTimer::timeout, this, &MainWin::autoSaveProject);
322 
323  if (!fileName.isEmpty()) {
324  createMdiArea();
325  setCentralWidget(m_mdiArea);
326 #ifdef HAVE_LIBORIGIN
328 #else
329  if (Project::isLabPlotProject(fileName)) {
330 #endif
331  QTimer::singleShot(0, this, [=] () { openProject(fileName); });
332  } else {
333  newProject();
334  QTimer::singleShot(0, this, [=] () { importFileDialog(fileName); });
335  }
336 
337  } else {
338  //There is no file to open. Depending on the settings do nothing,
339  //create a new project or open the last used project.
340  LoadOnStart load = (LoadOnStart)group.readEntry("LoadOnStart", static_cast<int>(LoadOnStart::Nothing));
341  if (load != LoadOnStart::WelcomeScreen) {
342  createMdiArea();
343  setCentralWidget(m_mdiArea);
344 
345  if (load == LoadOnStart::NewProject) //create new project
346  newProject();
347  else if (load == LoadOnStart::NewProjectWorksheet) { //create new project with a worksheet
348  newProject();
349  newWorksheet();
350  } else if (load == LoadOnStart::LastProject) { //open last used project
351  const QString& path = KSharedConfig::openConfig()->group("MainWin").readEntry("LastOpenProject", "");
352  if (!path.isEmpty())
353  openProject(path);
354  }
355 
357  } else{ //welcome screen
358 // m_showWelcomeScreen = true;
359 // m_welcomeWidget = createWelcomeScreen();
360 // setCentralWidget(m_welcomeWidget);
361  }
362  }
363 
364  //read the settings of MainWin
365  const KConfigGroup& groupMainWin = KSharedConfig::openConfig()->group(QLatin1String("MainWin"));
366 
367  //status bar
368  bool visible = groupMainWin.readEntry(QLatin1String("ShowStatusBar"), true);
369  statusBar()->setVisible(visible);
370  m_toggleStatusBarAction->setChecked(visible);
371  m_toggleMemoryInfoAction->setEnabled(visible);
372 
373  //show memory info
374  visible = groupMainWin.readEntry(QLatin1String("ShowMemoryInfo"), true);
375  if (visible) {
376  m_memoryInfoWidget = new MemoryWidget(statusBar());
377  statusBar()->addPermanentWidget(m_memoryInfoWidget);
378  }
379  m_toggleMemoryInfoAction->setChecked(visible);
380 
381  //restore the geometry
382  restoreGeometry(groupMainWin.readEntry("geometry", QByteArray()));
383 
384  m_lastOpenFileFilter = groupMainWin.readEntry(QLatin1String("lastOpenFileFilter"), QString());
385 }
386 
387 /**
388  * @brief Creates a new welcome screen to be set as central widget.
389  */
390 /*
391 QQuickWidget* MainWin::createWelcomeScreen() {
392  QSize maxSize = qApp->primaryScreen()->availableSize();
393  resize(maxSize);
394  setMinimumSize(700, 400);
395  showMaximized();
396 
397  KToolBar* toolbar = toolBar();
398  if (toolbar)
399  toolbar->setVisible(false);
400 
401  QList<QVariant> recentList;
402  for (QUrl& url : m_recentProjectsAction->urls())
403  recentList.append(QVariant(url));
404 
405  //Set context property
406  QQuickWidget* quickWidget = new QQuickWidget(this);
407  QQmlContext* ctxt = quickWidget->rootContext();
408  QVariant variant(recentList);
409  ctxt->setContextProperty("recentProjects", variant);
410 
411  //Create helper object
412  if (m_welcomeScreenHelper)
413  delete m_welcomeScreenHelper;
414  m_welcomeScreenHelper = new WelcomeScreenHelper();
415  connect(m_welcomeScreenHelper, &WelcomeScreenHelper::openExampleProject,
416  this, QOverload<const QString&>::of(&MainWin::openProject));
417 
418  ctxt->setContextProperty("datasetModel", m_welcomeScreenHelper->getDatasetModel());
419  ctxt->setContextProperty("helper", m_welcomeScreenHelper);
420 
421  quickWidget->setSource(QUrl(QLatin1String("qrc:///main.qml")));
422  quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
423  QObject *item = quickWidget->rootObject();
424 
425  //connect qml's signals
426  QObject::connect(item, SIGNAL(recentProjectClicked(QUrl)), this, SLOT(openRecentProject(QUrl)));
427  QObject::connect(item, SIGNAL(datasetClicked(QString,QString,QString)), m_welcomeScreenHelper, SLOT(datasetClicked(QString,QString,QString)));
428  QObject::connect(item, SIGNAL(openDataset()), this, SLOT(openDatasetExample()));
429  QObject::connect(item, SIGNAL(openExampleProject(QString)), m_welcomeScreenHelper, SLOT(exampleProjectClicked(QString)));
430  emit m_welcomeScreenHelper->showFirstDataset();
431 
432  return quickWidget;
433 }
434 */
435 /**
436  * @brief Initiates resetting the layout of the welcome screen
437  */
438 /*
439 void MainWin::resetWelcomeScreen() {
440  if (dynamic_cast<QQuickWidget*>(centralWidget()))
441  QMetaObject::invokeMethod(m_welcomeWidget->rootObject(), "restoreOriginalLayout");
442 }
443 */
444 
445 /**
446  * @brief Creates a new MDI area, to replace the Welcome Screen as central widget
447  */
449  KToolBar* toolbar = toolBar();
450  if (toolbar)
451  toolbar->setVisible(true);
452 
453  //Save welcome screen's dimensions.
454 // if (m_showWelcomeScreen)
455 // QMetaObject::invokeMethod(m_welcomeWidget->rootObject(), "saveWidgetDimensions");
456 
457  m_mdiArea = new QMdiArea;
458  setCentralWidget(m_mdiArea);
459  connect(m_mdiArea, &QMdiArea::subWindowActivated, this, &MainWin::handleCurrentSubWindowChanged);
460 
461  //set the view mode of the mdi area
462  KConfigGroup group = KSharedConfig::openConfig()->group( "Settings_General" );
463  int viewMode = group.readEntry("ViewMode", 0);
464  if (viewMode == 1) {
465  m_mdiArea->setViewMode(QMdiArea::TabbedView);
466  int tabPosition = group.readEntry("TabPosition", 0);
467  m_mdiArea->setTabPosition(QTabWidget::TabPosition(tabPosition));
468  m_mdiArea->setTabsClosable(true);
469  m_mdiArea->setTabsMovable(true);
470  m_tileWindowsAction->setVisible(false);
471  m_cascadeWindowsAction->setVisible(false);
472  }
473 
474  connect(m_closeWindowAction, &QAction::triggered, m_mdiArea, &QMdiArea::closeActiveSubWindow);
475  connect(m_closeAllWindowsAction, &QAction::triggered, m_mdiArea, &QMdiArea::closeAllSubWindows);
476  connect(m_tileWindowsAction, &QAction::triggered, m_mdiArea, &QMdiArea::tileSubWindows);
477  connect(m_cascadeWindowsAction, &QAction::triggered, m_mdiArea, &QMdiArea::cascadeSubWindows);
478  connect(m_nextWindowAction, &QAction::triggered, m_mdiArea, &QMdiArea::activateNextSubWindow);
479  connect(m_prevWindowAction, &QAction::triggered, m_mdiArea, &QMdiArea::activatePreviousSubWindow);
480 }
481 
483  // ******************** File-menu *******************************
484  //add some standard actions
485  m_newProjectAction = KStandardAction::openNew(this, &MainWin::newProject, actionCollection());
486  m_openProjectAction = KStandardAction::open(this, static_cast<void (MainWin::*)()>(&MainWin::openProject), actionCollection());
487  m_recentProjectsAction = KStandardAction::openRecent(this, &MainWin::openRecentProject, actionCollection());
488  m_closeAction = KStandardAction::close(this, &MainWin::closeProject, actionCollection());
489  actionCollection()->setDefaultShortcut(m_closeAction, QKeySequence()); //remove the shortcut, QKeySequence::Close will be used for closing sub-windows
490  m_saveAction = KStandardAction::save(this, &MainWin::saveProject, actionCollection());
491  m_saveAsAction = KStandardAction::saveAs(this, &MainWin::saveProjectAs, actionCollection());
492  m_printAction = KStandardAction::print(this, &MainWin::print, actionCollection());
493  m_printPreviewAction = KStandardAction::printPreview(this, &MainWin::printPreview, actionCollection());
494 
495  m_toggleFullScreenAction = KStandardAction::fullScreen(this, &MainWin::toggleFullScreen, this, actionCollection());
496 
497  //QDEBUG(Q_FUNC_INFO << ", preferences action name:" << KStandardAction::name(KStandardAction::Preferences))
498  KStandardAction::preferences(this, &MainWin::settingsDialog, actionCollection());
499  // QAction* action = actionCollection()->action(KStandardAction::name(KStandardAction::Preferences)));
500  KStandardAction::quit(this, &MainWin::close, actionCollection());
501 
502  //New Folder/Workbook/Spreadsheet/Matrix/Worksheet/Datasources
503  m_newWorkbookAction = new QAction(QIcon::fromTheme("labplot-workbook-new"),i18n("Workbook"),this);
504  actionCollection()->addAction("new_workbook", m_newWorkbookAction);
505  m_newWorkbookAction->setWhatsThis(i18n("Creates a new workbook for collection spreadsheets, matrices and plots"));
506  connect(m_newWorkbookAction, &QAction::triggered, this, &MainWin::newWorkbook);
507 
508  m_newDatapickerAction = new QAction(QIcon::fromTheme("color-picker-black"), i18n("Datapicker"), this);
509  m_newDatapickerAction->setWhatsThis(i18n("Creates a data picker for getting data from a picture"));
510  actionCollection()->addAction("new_datapicker", m_newDatapickerAction);
511  connect(m_newDatapickerAction, &QAction::triggered, this, &MainWin::newDatapicker);
512 
513  m_newSpreadsheetAction = new QAction(QIcon::fromTheme("labplot-spreadsheet-new"),i18n("Spreadsheet"),this);
514 // m_newSpreadsheetAction->setShortcut(Qt::CTRL+Qt::Key_Equal);
515  m_newSpreadsheetAction->setWhatsThis(i18n("Creates a new spreadsheet for data editing"));
516  actionCollection()->addAction("new_spreadsheet", m_newSpreadsheetAction);
517  connect(m_newSpreadsheetAction, &QAction::triggered, this, &MainWin::newSpreadsheet);
518 
519  m_newMatrixAction = new QAction(QIcon::fromTheme("labplot-matrix-new"),i18n("Matrix"),this);
520 // m_newMatrixAction->setShortcut(Qt::CTRL+Qt::Key_Equal);
521  m_newMatrixAction->setWhatsThis(i18n("Creates a new matrix for data editing"));
522  actionCollection()->addAction("new_matrix", m_newMatrixAction);
523  connect(m_newMatrixAction, &QAction::triggered, this, &MainWin::newMatrix);
524 
525  m_newWorksheetAction = new QAction(QIcon::fromTheme("labplot-worksheet-new"),i18n("Worksheet"),this);
526 // m_newWorksheetAction->setShortcut(Qt::ALT+Qt::Key_X);
527  m_newWorksheetAction->setWhatsThis(i18n("Creates a new worksheet for data plotting"));
528  actionCollection()->addAction("new_worksheet", m_newWorksheetAction);
529  connect(m_newWorksheetAction, &QAction::triggered, this, &MainWin::newWorksheet);
530 
531  m_newNotesAction = new QAction(QIcon::fromTheme("document-new"),i18n("Note"),this);
532  m_newNotesAction->setWhatsThis(i18n("Creates a new note for arbitrary text"));
533  actionCollection()->addAction("new_notes", m_newNotesAction);
534  connect(m_newNotesAction, &QAction::triggered, this, &MainWin::newNotes);
535 
536 // m_newScriptAction = new QAction(QIcon::fromTheme("insert-text"),i18n("Note/Script"),this);
537 // actionCollection()->addAction("new_script", m_newScriptAction);
538 // connect(m_newScriptAction, &QAction::triggered,SLOT(newScript()));
539 
540  m_newFolderAction = new QAction(QIcon::fromTheme("folder-new"),i18n("Folder"),this);
541  m_newFolderAction->setWhatsThis(i18n("Creates a new folder to collect sheets and other elements"));
542  actionCollection()->addAction("new_folder", m_newFolderAction);
543  connect(m_newFolderAction, &QAction::triggered, this, &MainWin::newFolder);
544 
545  //"New file datasources"
546  m_newLiveDataSourceAction = new QAction(QIcon::fromTheme("application-octet-stream"),i18n("Live Data Source"),this);
547  m_newLiveDataSourceAction->setWhatsThis(i18n("Creates a live data source to read data from a real time device"));
548  actionCollection()->addAction("new_live_datasource", m_newLiveDataSourceAction);
549  connect(m_newLiveDataSourceAction, &QAction::triggered, this, &MainWin::newLiveDataSourceActionTriggered);
550 
551  //Import/Export
552  m_importFileAction = new QAction(QIcon::fromTheme("document-import"), i18n("Import from File"), this);
553  actionCollection()->setDefaultShortcut(m_importFileAction, Qt::CTRL+Qt::SHIFT+Qt::Key_I);
554  m_importFileAction->setWhatsThis(i18n("Import data from a regular file"));
555  actionCollection()->addAction("import_file", m_importFileAction);
556  connect(m_importFileAction, &QAction::triggered, this, [=]() {importFileDialog();});
557 
558  m_importSqlAction = new QAction(QIcon::fromTheme("network-server-database"), i18n("From SQL Database"), this);
559  m_importSqlAction->setWhatsThis(i18n("Import data from a SQL database"));
560  actionCollection()->addAction("import_sql", m_importSqlAction);
561  connect(m_importSqlAction, &QAction::triggered, this, &MainWin::importSqlDialog);
562 
563  m_importDatasetAction = new QAction(QIcon::fromTheme(QLatin1String("database-index")), i18n("From Dataset Collection"), this);
564  m_importDatasetAction->setWhatsThis(i18n("Imports data from an online dataset"));
565  actionCollection()->addAction("import_dataset_datasource", m_importDatasetAction);
566  connect(m_importDatasetAction, &QAction::triggered, this, &MainWin::importDatasetDialog);
567 
568  m_importLabPlotAction = new QAction(QIcon::fromTheme("project-open"), i18n("LabPlot Project"), this);
569  m_importLabPlotAction->setWhatsThis(i18n("Import a project from a LabPlot project file (.lml)"));
570  actionCollection()->addAction("import_labplot", m_importLabPlotAction);
571  connect(m_importLabPlotAction, &QAction::triggered, this, &MainWin::importProjectDialog);
572 
573 #ifdef HAVE_LIBORIGIN
574  m_importOpjAction = new QAction(QIcon::fromTheme("project-open"), i18n("Origin Project (OPJ)"), this);
575  m_importOpjAction->setWhatsThis(i18n("Import a project from an OriginLab Origin project file (.opj)"));
576  actionCollection()->addAction("import_opj", m_importOpjAction);
577  connect(m_importOpjAction, &QAction::triggered, this, &MainWin::importProjectDialog);
578 #endif
579 
580  m_exportAction = new QAction(QIcon::fromTheme("document-export"), i18n("Export"), this);
581  m_exportAction->setWhatsThis(i18n("Export selected element"));
582  actionCollection()->setDefaultShortcut(m_exportAction, Qt::CTRL+Qt::SHIFT+Qt::Key_E);
583  actionCollection()->addAction("export", m_exportAction);
584  connect(m_exportAction, &QAction::triggered, this, &MainWin::exportDialog);
585 
586  m_editFitsFileAction = new QAction(QIcon::fromTheme("editor"), i18n("FITS Metadata Editor"), this);
587  m_editFitsFileAction->setWhatsThis(i18n("Open editor to edit FITS meta data"));
588  actionCollection()->addAction("edit_fits", m_editFitsFileAction);
589  connect(m_editFitsFileAction, &QAction::triggered, this, &MainWin::editFitsFileDialog);
590 
591  // Edit
592  //Undo/Redo-stuff
593  m_undoAction = KStandardAction::undo(this, SLOT(undo()), actionCollection());
594  m_redoAction = KStandardAction::redo(this, SLOT(redo()), actionCollection());
595  m_historyAction = new QAction(QIcon::fromTheme("view-history"), i18n("Undo/Redo History"),this);
596  actionCollection()->addAction("history", m_historyAction);
597  connect(m_historyAction, &QAction::triggered, this, &MainWin::historyDialog);
598 
599 #ifdef Q_OS_MAC
600  m_undoIconOnlyAction = new QAction(m_undoAction->icon(), QString());
601  connect(m_undoIconOnlyAction, &QAction::triggered, this, &MainWin::undo);
602 
603  m_redoIconOnlyAction = new QAction(m_redoAction->icon(), QString());
604  connect(m_redoIconOnlyAction, &QAction::triggered, this, &MainWin::redo);
605 #endif
606  // TODO: more menus
607  // Appearance
608  // Analysis: see WorksheetView.cpp
609  // Drawing
610  // Script
611 
612  //Windows
613  m_closeWindowAction = new QAction(i18n("&Close"), this);
614  actionCollection()->setDefaultShortcut(m_closeWindowAction, QKeySequence::Close);
615  m_closeWindowAction->setStatusTip(i18n("Close the active window"));
616  actionCollection()->addAction("close window", m_closeWindowAction);
617 
618  m_closeAllWindowsAction = new QAction(i18n("Close &All"), this);
619  m_closeAllWindowsAction->setStatusTip(i18n("Close all the windows"));
620  actionCollection()->addAction("close all windows", m_closeAllWindowsAction);
621 
622  m_tileWindowsAction = new QAction(i18n("&Tile"), this);
623  m_tileWindowsAction->setStatusTip(i18n("Tile the windows"));
624  actionCollection()->addAction("tile windows", m_tileWindowsAction);
625 
626  m_cascadeWindowsAction = new QAction(i18n("&Cascade"), this);
627  m_cascadeWindowsAction->setStatusTip(i18n("Cascade the windows"));
628  actionCollection()->addAction("cascade windows", m_cascadeWindowsAction);
629 
630  m_nextWindowAction = new QAction(QIcon::fromTheme("go-next-view"), i18n("Ne&xt"), this);
631  actionCollection()->setDefaultShortcut(m_nextWindowAction, QKeySequence::NextChild);
632  m_nextWindowAction->setStatusTip(i18n("Move the focus to the next window"));
633  actionCollection()->addAction("next window", m_nextWindowAction);
634 
635  m_prevWindowAction = new QAction(QIcon::fromTheme("go-previous-view"), i18n("Pre&vious"), this);
636  actionCollection()->setDefaultShortcut(m_prevWindowAction, QKeySequence::PreviousChild);
637  m_prevWindowAction->setStatusTip(i18n("Move the focus to the previous window"));
638  actionCollection()->addAction("previous window", m_prevWindowAction);
639 
640  //Actions for window visibility
641  auto* windowVisibilityActions = new QActionGroup(this);
642  windowVisibilityActions->setExclusive(true);
643 
644  m_visibilityFolderAction = new QAction(QIcon::fromTheme("folder"), i18n("Current &Folder Only"), windowVisibilityActions);
645  m_visibilityFolderAction->setCheckable(true);
647 
648  m_visibilitySubfolderAction = new QAction(QIcon::fromTheme("folder-documents"), i18n("Current Folder and &Subfolders"), windowVisibilityActions);
649  m_visibilitySubfolderAction->setCheckable(true);
651 
652  m_visibilityAllAction = new QAction(i18n("&All"), windowVisibilityActions);
653  m_visibilityAllAction->setCheckable(true);
655 
656  connect(windowVisibilityActions, &QActionGroup::triggered, this, &MainWin::setMdiWindowVisibility);
657 
658  //show/hide the status bar
659  m_toggleStatusBarAction = new QAction(i18n("Show Status Bar"));
660  m_toggleStatusBarAction->setCheckable(true);
661  m_toggleStatusBarAction->setChecked(true);
662  connect(m_toggleStatusBarAction, &QAction::triggered, this, &MainWin::toggleStatusBar);
663 
664  //show/hide the status bar
665  m_toggleMemoryInfoAction = new QAction(i18n("Show Memory Usage"));
666  m_toggleMemoryInfoAction->setCheckable(true);
667  m_toggleMemoryInfoAction->setChecked(true);
668  connect(m_toggleMemoryInfoAction, &QAction::triggered, this, &MainWin::toggleMemoryInfo);
669 
670  //Actions for hiding/showing the dock widgets
671  auto* docksActions = new QActionGroup(this);
672  docksActions->setExclusive(false);
673 
674  m_toggleProjectExplorerDockAction = new QAction(QIcon::fromTheme("view-list-tree"), i18n("Project Explorer"), docksActions);
675  m_toggleProjectExplorerDockAction->setCheckable(true);
676  m_toggleProjectExplorerDockAction->setChecked(true);
677  actionCollection()->addAction("toggle_project_explorer_dock", m_toggleProjectExplorerDockAction);
678 
679  m_togglePropertiesDockAction = new QAction(QIcon::fromTheme("view-list-details"), i18n("Properties Explorer"), docksActions);
680  m_togglePropertiesDockAction->setCheckable(true);
681  m_togglePropertiesDockAction->setChecked(true);
682  actionCollection()->addAction("toggle_properties_explorer_dock", m_togglePropertiesDockAction);
683 
684  connect(docksActions, &QActionGroup::triggered, this, &MainWin::toggleDockWidget);
685 
686  //global search
687  QAction* searchAction = new QAction(actionCollection());
688  searchAction->setShortcut(QKeySequence::Find);
689  connect(searchAction, &QAction::triggered, this, [=]() {
690  if (m_project) {
691  if (!m_projectExplorerDock->isVisible()) {
692  m_toggleProjectExplorerDockAction->setChecked(true);
693  toggleDockWidget(m_toggleProjectExplorerDockAction);
694  }
696  }
697  });
698  this->addAction(searchAction);
699 }
700 
702  //add the actions to toggle the status bar and the project and properties explorer widgets to the "View" menu.
703  //this menu is created automatically when the default "full screen" action is created in initActions().
704  auto* menu = dynamic_cast<QMenu*>(factory()->container("view", this));
705 
706  if (menu) {
707  menu->addSeparator();
708  menu->addAction(m_toggleStatusBarAction);
709  menu->addSeparator();
710  menu->addAction(m_toggleMemoryInfoAction);
711  menu->addSeparator();
712  menu->addAction(m_toggleProjectExplorerDockAction);
713  menu->addAction(m_togglePropertiesDockAction);
714  }
715 
716  //menu in the main toolbar for adding new aspects
717  menu = dynamic_cast<QMenu*>(factory()->container("new", this));
718  menu->setIcon(QIcon::fromTheme("window-new"));
719 
720  //menu in the project explorer and in the toolbar for adding new aspects
721  m_newMenu = new QMenu(i18n("Add New"), this);
722  m_newMenu->setIcon(QIcon::fromTheme("window-new"));
723  m_newMenu->addAction(m_newFolderAction);
724  m_newMenu->addAction(m_newWorkbookAction);
725  m_newMenu->addAction(m_newSpreadsheetAction);
726  m_newMenu->addAction(m_newMatrixAction);
727  m_newMenu->addAction(m_newWorksheetAction);
728  m_newMenu->addAction(m_newNotesAction);
729  m_newMenu->addAction(m_newDatapickerAction);
730  m_newMenu->addSeparator();
732 
733  //import menu
734  m_importMenu = new QMenu(this);
735  m_importMenu->setIcon(QIcon::fromTheme("document-import"));
736  m_importMenu ->addAction(m_importFileAction);
737  m_importMenu ->addAction(m_importSqlAction);
739  m_importMenu->addSeparator();
741 #ifdef HAVE_LIBORIGIN
742  m_importMenu ->addAction(m_importOpjAction);
743 #endif
744 
745 #ifdef HAVE_CANTOR_LIBS
746  m_newMenu->addSeparator();
747  m_newCantorWorksheetMenu = new QMenu(i18n("CAS Worksheet"), this);
748  m_newCantorWorksheetMenu->setIcon(QIcon::fromTheme("archive-insert"));
749 
750  //"Adding Cantor backends to menu and context menu"
751  QStringList m_availableBackend = Cantor::Backend::listAvailableBackends();
752  if (m_availableBackend.count() > 0) {
753  unplugActionList(QLatin1String("backends_list"));
754  QList<QAction*> newBackendActions;
755  for (auto* backend : Cantor::Backend::availableBackends()) {
756  if (!backend->isEnabled()) continue;
757  QAction* action = new QAction(QIcon::fromTheme(backend->icon()), backend->name(), this);
758  action->setData(backend->name());
759  newBackendActions << action;
760  m_newCantorWorksheetMenu->addAction(action);
761  }
762 
763  connect(m_newCantorWorksheetMenu, &QMenu::triggered, this, &MainWin::newCantorWorksheet);
764  plugActionList(QLatin1String("backends_list"), newBackendActions);
765  }
766  m_newMenu->addMenu(m_newCantorWorksheetMenu);
767 #else
768  delete this->guiFactory()->container("cas_worksheet", this);
769  delete this->guiFactory()->container("new_cas_worksheet", this);
770  delete this->guiFactory()->container("cas_worksheet_toolbar", this);
771 #endif
772 
773  //menu subwindow visibility policy
774  m_visibilityMenu = new QMenu(i18n("Window Visibility Policy"), this);
775  m_visibilityMenu->setIcon(QIcon::fromTheme("window-duplicate"));
779 
780  //menu for editing files
781  m_editMenu = new QMenu(i18n("Edit"), this);
782  m_editMenu->addAction(m_editFitsFileAction);
783 
784  //set the action for the current color scheme checked
785  KConfigGroup group = KSharedConfig::openConfig()->group(QLatin1String("Settings_General"));
786 #if KCONFIGWIDGETS_VERSION >= QT_VERSION_CHECK(5, 67, 0) // KColorSchemeManager has a system default option
787  QString schemeName = group.readEntry("ColorScheme");
788 #else
789  KConfigGroup generalGlobalsGroup = KSharedConfig::openConfig(QLatin1String("kdeglobals"))->group("General");
790  QString defaultSchemeName = generalGlobalsGroup.readEntry("ColorScheme", QStringLiteral("Breeze"));
791  QString schemeName = group.readEntry("ColorScheme", defaultSchemeName);
792 #endif
793  KActionMenu* schemesMenu = m_schemeManager->createSchemeSelectionMenu(i18n("Color Scheme"), schemeName, this);
794  schemesMenu->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-color")));
795 
796  QMenu* settingsMenu = dynamic_cast<QMenu*>(factory()->container("settings", this));
797  if (settingsMenu)
798  settingsMenu->insertMenu(settingsMenu->actions().constFirst(), schemesMenu->menu());
799 
800  connect(schemesMenu->menu(), &QMenu::triggered, this, &MainWin::colorSchemeChanged);
801 
802 #ifdef HAVE_CANTOR_LIBS
803  QAction* action = new QAction(QIcon::fromTheme(QLatin1String("cantor")), i18n("Configure CAS"), this);
804  connect(action, &QAction::triggered, this, &MainWin::cantorSettingsDialog);
805  action->setMenuRole(QAction::NoRole); // prevent macOS Qt heuristics to select this action for preferences
806  if (settingsMenu)
807  settingsMenu->addAction(action);
808 #endif
809 }
810 
812  QString schemeName = KLocalizedString::removeAcceleratorMarker(action->text());
813 
814  //background of the mdi area is not updated on theme changes, do it here.
815  QModelIndex index = m_schemeManager->indexForScheme(schemeName);
816  const QPalette& palette = KColorScheme::createApplicationPalette( KSharedConfig::openConfig(index.data(Qt::UserRole).toString()) );
817  const QBrush& brush = palette.brush(QPalette::Dark);
818  m_mdiArea->setBackground(brush);
819 
820  //save the selected color scheme
821  KConfigGroup group = KSharedConfig::openConfig()->group(QLatin1String("Settings_General"));
822  group.writeEntry("ColorScheme", schemeName);
823  group.sync();
824 }
825 
826 /*!
827  Asks to save the project if it was modified.
828  \return \c true if the project still needs to be saved ("cancel" clicked), \c false otherwise.
829  */
831  if (m_project->hasChanged()) {
832  int want_save = KMessageBox::warningYesNoCancel( this,
833  i18n("The current project %1 has been modified. Do you want to save it?", m_project->name()),
834  i18n("Save Project"));
835  switch (want_save) {
836  case KMessageBox::Yes:
837  return !saveProject();
838  case KMessageBox::No:
839  break;
840  case KMessageBox::Cancel:
841  return true;
842  }
843  }
844 
845  return false;
846 }
847 
848 /*!
849  * updates the state of actions, menus and toolbars (enabled or disabled)
850  * on project changes (project closes and opens)
851  */
853  if (m_closing)
854  return;
855 
856  KXMLGUIFactory* factory = this->guiFactory();
857  if (factory->container("worksheet", this) == nullptr) {
858  //no worksheet menu found, most probably labplot2ui.rc
859  //was not properly installed -> return here in order not to crash
860  return;
861  }
862 
863  //disable all menus if there is no project
864  bool hasProject = (m_project != nullptr);
865  m_saveAction->setEnabled(hasProject);
866  m_saveAsAction->setEnabled(hasProject);
867  m_printAction->setEnabled(hasProject);
868  m_printPreviewAction->setEnabled(hasProject);
869  m_importFileAction->setEnabled(hasProject);
870  m_importSqlAction->setEnabled(hasProject);
871 #ifdef HAVE_LIBORIGIN
872  m_importOpjAction->setEnabled(hasProject);
873 #endif
874  m_exportAction->setEnabled(hasProject);
875  m_newWorkbookAction->setEnabled(hasProject);
876  m_newSpreadsheetAction->setEnabled(hasProject);
877  m_newMatrixAction->setEnabled(hasProject);
878  m_newWorksheetAction->setEnabled(hasProject);
879  m_newDatapickerAction->setEnabled(hasProject);
880  m_closeAction->setEnabled(hasProject);
881  m_toggleProjectExplorerDockAction->setEnabled(hasProject);
882  m_togglePropertiesDockAction->setEnabled(hasProject);
883 
884  if (!m_mdiArea || !m_mdiArea->currentSubWindow()) {
885  factory->container("spreadsheet", this)->setEnabled(false);
886  factory->container("matrix", this)->setEnabled(false);
887  factory->container("worksheet", this)->setEnabled(false);
888  factory->container("analysis", this)->setEnabled(false);
889  factory->container("datapicker", this)->setEnabled(false);
890  factory->container("spreadsheet_toolbar", this)->hide();
891  factory->container("worksheet_toolbar", this)->hide();
892  factory->container("cartesian_plot_toolbar", this)->hide();
893 // factory->container("histogram_toolbar",this)->hide();
894 // factory->container("barchart_toolbar",this)->hide();
895  factory->container("datapicker_toolbar", this)->hide();
896 #ifdef HAVE_CANTOR_LIBS
897  factory->container("cas_worksheet", this)->setEnabled(false);
898  factory->container("cas_worksheet_toolbar", this)->hide();
899 #endif
900  }
901 
902  factory->container("new", this)->setEnabled(hasProject);
903  factory->container("edit", this)->setEnabled(hasProject);
904  factory->container("import", this)->setEnabled(hasProject);
905 
906  updateTitleBar();
907 
908 #ifdef Q_OS_MAC
909  m_touchBar->clear();
910 
911  if (!hasProject) {
912  m_touchBar->addAction(m_newProjectAction);
913  m_touchBar->addAction(m_openProjectAction);
914  } else {
915  m_touchBar->addAction(m_importFileAction);
916  m_touchBar->addAction(m_newWorksheetAction);
917  m_touchBar->addAction(m_newSpreadsheetAction);
918  m_touchBar->addAction(m_newMatrixAction);
919  }
920 #endif
921  // undo/redo actions are disabled in both cases - when the project is closed or opened
922  m_undoAction->setEnabled(false);
923  m_redoAction->setEnabled(false);
924 }
925 
926 /*
927  * updates the state of actions, menus and toolbars (enabled or disabled)
928  * depending on the currently active window (worksheet or spreadsheet).
929  */
931  if (m_project == nullptr || m_project->isLoading())
932  return;
933 
935  return;
936 
937  KXMLGUIFactory* factory = this->guiFactory();
938  if (factory->container("worksheet", this) == nullptr) {
939  //no worksheet menu found, most probably labplot2ui.rc
940  //was not properly installed -> return here in order not to crash
941  return;
942  }
943 
944  //reset the touchbar
945 #ifdef Q_OS_MAC
946  m_touchBar->clear();
947 
948  m_touchBar->addAction(m_undoIconOnlyAction);
949  m_touchBar->addAction(m_redoIconOnlyAction);
950  m_touchBar->addSeparator();
951 #endif
952 
953  if (!m_mdiArea || !m_mdiArea->currentSubWindow()) {
954  factory->container("spreadsheet", this)->setEnabled(false);
955  factory->container("matrix", this)->setEnabled(false);
956  factory->container("worksheet", this)->setEnabled(false);
957  factory->container("analysis", this)->setEnabled(false);
958  factory->container("datapicker", this)->setEnabled(false);
959  factory->container("spreadsheet_toolbar", this)->hide();
960  factory->container("worksheet_toolbar", this)->hide();
961 // factory->container("histogram_toolbar",this)->hide();
962 // factory->container("barchart_toolbar",this)->hide();
963  factory->container("cartesian_plot_toolbar", this)->hide();
964  factory->container("datapicker_toolbar", this)->hide();
965 #ifdef HAVE_CANTOR_LIBS
966  factory->container("cas_worksheet", this)->setEnabled(false);
967  factory->container("cas_worksheet_toolbar", this)->hide();
968 #endif
969  return;
970  }
971 
972 
973 #ifdef Q_OS_MAC
974  if (dynamic_cast<Folder*>(m_currentAspect)) {
975  m_touchBar->addAction(m_newWorksheetAction);
976  m_touchBar->addAction(m_newSpreadsheetAction);
977  m_touchBar->addAction(m_newMatrixAction);
978  }
979 #endif
980 
981  Q_ASSERT(m_currentAspect);
982 
983  //Handle the Worksheet-object
984  const Worksheet* w = dynamic_cast<Worksheet*>(m_currentAspect);
985  if (!w)
987  if (w) {
988  //populate worksheet menu
989  auto* view = qobject_cast<WorksheetView*>(w->view());
990  auto* menu = qobject_cast<QMenu*>(factory->container("worksheet", this));
991  menu->clear();
992  view->createContextMenu(menu);
993  menu->setEnabled(true);
994 
995  //populate analysis menu
996  menu = qobject_cast<QMenu*>(factory->container("analysis", this));
997  menu->clear();
998  view->createAnalysisMenu(menu);
999  menu->setEnabled(true);
1000 
1001  //populate worksheet-toolbar
1002  auto* toolbar = qobject_cast<QToolBar*>(factory->container("worksheet_toolbar", this));
1003  toolbar->clear();
1004  view->fillToolBar(toolbar);
1005  toolbar->setVisible(true);
1006  toolbar->setEnabled(true);
1007 
1008  //populate the toolbar for cartesian plots
1009  toolbar = qobject_cast<QToolBar*>(factory->container("cartesian_plot_toolbar", this));
1010  toolbar->clear();
1011  view->fillCartesianPlotToolBar(toolbar);
1012  toolbar->setVisible(true);
1013  toolbar->setEnabled(true);
1014 
1015  //populate the touchbar on Mac
1016 #ifdef Q_OS_MAC
1017  view->fillTouchBar(m_touchBar);
1018 #endif
1019  //hide the spreadsheet toolbar
1020  factory->container("spreadsheet_toolbar", this)->setVisible(false);
1021  } else {
1022  factory->container("worksheet", this)->setEnabled(false);
1023  factory->container("worksheet_toolbar", this)->setVisible(false);
1024  factory->container("analysis", this)->setEnabled(false);
1025 // factory->container("drawing", this)->setEnabled(false);
1026  factory->container("worksheet_toolbar", this)->setEnabled(false);
1027  factory->container("cartesian_plot_toolbar", this)->setEnabled(false);
1028  }
1029 
1030  //Handle the Spreadsheet-object
1031  const auto* spreadsheet = this->activeSpreadsheet();
1032  if (!spreadsheet)
1033  spreadsheet = dynamic_cast<Spreadsheet*>(m_currentAspect->parent(AspectType::Spreadsheet));
1034  if (spreadsheet) {
1035  //populate spreadsheet-menu
1036  auto* view = qobject_cast<SpreadsheetView*>(spreadsheet->view());
1037  auto* menu = qobject_cast<QMenu*>(factory->container("spreadsheet", this));
1038  menu->clear();
1039  view->createContextMenu(menu);
1040  menu->setEnabled(true);
1041 
1042  //populate spreadsheet-toolbar
1043  auto* toolbar = qobject_cast<QToolBar*>(factory->container("spreadsheet_toolbar", this));
1044  toolbar->clear();
1045  view->fillToolBar(toolbar);
1046  toolbar->setVisible(true);
1047  toolbar->setEnabled(true);
1048 
1049  //populate the touchbar on Mac
1050 #ifdef Q_OS_MAC
1051  m_touchBar->addAction(m_importFileAction);
1052  view->fillTouchBar(m_touchBar);
1053 #endif
1054  } else {
1055  factory->container("spreadsheet", this)->setEnabled(false);
1056  factory->container("spreadsheet_toolbar", this)->setVisible(false);
1057  }
1058 
1059  //Handle the Matrix-object
1060  const Matrix* matrix = dynamic_cast<Matrix*>(m_currentAspect);
1061  if (!matrix)
1062  matrix = dynamic_cast<Matrix*>(m_currentAspect->parent(AspectType::Matrix));
1063  if (matrix) {
1064  //populate matrix-menu
1065  auto* view = qobject_cast<MatrixView*>(matrix->view());
1066  auto* menu = qobject_cast<QMenu*>(factory->container("matrix", this));
1067  menu->clear();
1068  view->createContextMenu(menu);
1069  menu->setEnabled(true);
1070 
1071  //populate the touchbar on Mac
1072 #ifdef Q_OS_MAC
1073  m_touchBar->addAction(m_importFileAction);
1074  //view->fillTouchBar(m_touchBar);
1075 #endif
1076  } else
1077  factory->container("matrix", this)->setEnabled(false);
1078 
1079 #ifdef HAVE_CANTOR_LIBS
1080  const CantorWorksheet* cantorworksheet = dynamic_cast<CantorWorksheet*>(m_currentAspect);
1081  if (!cantorworksheet)
1082  cantorworksheet = dynamic_cast<CantorWorksheet*>(m_currentAspect->parent(AspectType::CantorWorksheet));
1083  if (cantorworksheet) {
1084  auto* view = qobject_cast<CantorWorksheetView*>(cantorworksheet->view());
1085  auto* menu = qobject_cast<QMenu*>(factory->container("cas_worksheet", this));
1086  menu->clear();
1087  view->createContextMenu(menu);
1088  menu->setEnabled(true);
1089 
1090  auto* toolbar = qobject_cast<QToolBar*>(factory->container("cas_worksheet_toolbar", this));
1091  toolbar->setVisible(true);
1092  toolbar->clear();
1093  view->fillToolBar(toolbar);
1094  } else {
1095  //no Cantor worksheet selected -> deactivate Cantor worksheet related menu and toolbar
1096  factory->container("cas_worksheet", this)->setEnabled(false);
1097  factory->container("cas_worksheet_toolbar", this)->setVisible(false);
1098  }
1099 #endif
1100 
1101  const Datapicker* datapicker = dynamic_cast<Datapicker*>(m_currentAspect);
1102  if (!datapicker)
1103  datapicker = dynamic_cast<Datapicker*>(m_currentAspect->parent(AspectType::Datapicker));
1104  if (!datapicker) {
1106  datapicker = dynamic_cast<Datapicker*>(m_currentAspect->parentAspect());
1107  }
1108 
1109  if (datapicker) {
1110  //populate datapicker-menu
1111  auto* view = qobject_cast<DatapickerView*>(datapicker->view());
1112  auto* menu = qobject_cast<QMenu*>(factory->container("datapicker", this));
1113  menu->clear();
1114  view->createContextMenu(menu);
1115  menu->setEnabled(true);
1116 
1117  //populate spreadsheet-toolbar
1118  auto* toolbar = qobject_cast<QToolBar*>(factory->container("datapicker_toolbar", this));
1119  toolbar->clear();
1120  view->fillToolBar(toolbar);
1121  toolbar->setVisible(true);
1122  } else {
1123  factory->container("datapicker", this)->setEnabled(false);
1124  factory->container("datapicker_toolbar", this)->setVisible(false);
1125  }
1126 }
1127 
1128 /*!
1129  creates a new empty project. Returns \c true, if a new project was created.
1130 */
1132  //close the current project, if available
1133  if (!closeProject())
1134  return false;
1135 
1136 // if (dynamic_cast<QQuickWidget*>(centralWidget())) {
1137 // createMdiArea();
1138 // setCentralWidget(m_mdiArea);
1139 // }
1140 
1141  QApplication::processEvents(QEventLoop::AllEvents, 100);
1142 
1143  if (m_project)
1144  delete m_project;
1145 
1146  if (m_aspectTreeModel)
1147  delete m_aspectTreeModel;
1148 
1149  m_project = new Project();
1152 
1153  KConfigGroup group = KSharedConfig::openConfig()->group(QLatin1String("Settings_General"));
1154  auto vis = Project::MdiWindowVisibility(group.readEntry("MdiWindowVisibility", 0));
1157  m_visibilityFolderAction->setChecked(true);
1159  m_visibilitySubfolderAction->setChecked(true);
1160  else
1161  m_visibilityAllAction->setChecked(true);
1162 
1164  connect(m_aspectTreeModel, &AspectTreeModel::statusInfo, [=](const QString& text){ statusBar()->showMessage(text); });
1165 
1166  //newProject is called for the first time, there is no project explorer yet
1167  //-> initialize the project explorer, the GUI-observer and the dock widgets.
1168  if (!m_projectExplorer) {
1169  group = KSharedConfig::openConfig()->group(QLatin1String("MainWin"));
1170 
1171  m_projectExplorerDock = new QDockWidget(this);
1172  m_projectExplorerDock->setObjectName("projectexplorer");
1173  m_projectExplorerDock->setWindowTitle(i18nc("@title:window", "Project Explorer"));
1174 
1177 
1180  connect(m_projectExplorerDock, &QDockWidget::visibilityChanged, this, &MainWin::projectExplorerDockVisibilityChanged);
1181 
1182  //Properties dock
1183  m_propertiesDock = new QDockWidget(this);
1184  m_propertiesDock->setObjectName("aspect_properties_dock");
1185  m_propertiesDock->setWindowTitle(i18nc("@title:window", "Properties"));
1186 
1187  //restore the position of the dock widgets:
1188  //"WindowState" doesn't always contain the positions of the dock widgets,
1189  //user opened the application and closed it without creating a new project
1190  //and with this the dock widgets - this creates a "WindowState" section in the settings without dock widgets positions.
1191  //So, we set our default positions first and then read from the saved "WindowState" section
1192  addDockWidget(Qt::LeftDockWidgetArea, m_projectExplorerDock);
1193  addDockWidget(Qt::RightDockWidgetArea, m_propertiesDock);
1194  if (group.keyList().indexOf("WindowState") != -1)
1195  restoreState(group.readEntry("WindowState", QByteArray()));
1196 
1197  auto* scrollArea = new QScrollArea(m_propertiesDock);
1198  scrollArea->setWidgetResizable(true);
1199 
1200  stackedWidget = new QStackedWidget(scrollArea);
1201  scrollArea->setWidget(stackedWidget); // stacked widget inside scroll area
1202  m_propertiesDock->setWidget(scrollArea); // scroll area inside dock
1203 
1204  connect(m_propertiesDock, &QDockWidget::visibilityChanged, this, &MainWin::propertiesDockVisibilityChanged);
1205 
1206  //GUI-observer;
1207  m_guiObserver = new GuiObserver(this);
1208  }
1209 
1213 
1214  m_projectExplorerDock->show();
1215  m_propertiesDock->show();
1217 
1221  connect(m_project, SIGNAL(statusInfo(QString)), statusBar(), SLOT(showMessage(QString)));
1227 
1228  m_undoViewEmptyLabel = i18n("%1: created", m_project->name());
1229  updateTitleBar();
1230 
1231  return true;
1232 }
1233 
1235  bool supportOthers = false;
1236  QString allExtensions = Project::supportedExtensions();
1237  QString extensions = i18n("LabPlot Projects (%1)", allExtensions);
1238  if (m_lastOpenFileFilter.isEmpty())
1239  m_lastOpenFileFilter = extensions;
1240 
1241 #ifdef HAVE_LIBORIGIN
1242  extensions += QLatin1String(";;") + i18n("Origin Projects (%1)", OriginProjectParser::supportedExtensions());
1243  allExtensions += " " + OriginProjectParser::supportedExtensions();
1244  supportOthers = true;
1245 #endif
1246 
1247 #ifdef HAVE_CANTOR_LIBS
1248  extensions += QLatin1String(";;") + i18n("Cantor Projects (*.cws)");
1249  extensions += QLatin1String(";;") + i18n("Jupyter Notebooks (*.ipynb)");
1250  allExtensions += QLatin1String(" *.cws *.ipynb");
1251  supportOthers = true;
1252 #endif
1253 
1254  //add an entry for "All supported files" if we support more than labplot
1255  if (supportOthers)
1256  extensions = i18n("All supported files (%1)", allExtensions) + QLatin1String(";;") + extensions;
1257 
1258  KConfigGroup group(KSharedConfig::openConfig(), "MainWin");
1259  const QString& dir = group.readEntry("LastOpenDir", "");
1260  const QString& path = QFileDialog::getOpenFileName(this,i18n("Open Project"), dir, extensions, &m_lastOpenFileFilter);
1261  if (path.isEmpty())// "Cancel" was clicked
1262  return;
1263 
1264  this->openProject(path);
1265 
1266  //save new "last open directory"
1267  int pos = path.lastIndexOf(QLatin1String("/"));
1268  if (pos != -1) {
1269  const QString& newDir = path.left(pos);
1270  if (newDir != dir)
1271  group.writeEntry("LastOpenDir", newDir);
1272  }
1273 }
1274 
1275 void MainWin::openProject(const QString& filename) {
1276  if (filename == m_currentFileName) {
1277  KMessageBox::information(this, i18n("The project file %1 is already opened.", filename), i18n("Open Project"));
1278  return;
1279  }
1280 
1281 // if (dynamic_cast<QQuickWidget*>(centralWidget())) {
1282 // createMdiArea();
1283 // setCentralWidget(m_mdiArea);
1284 // }
1285 
1286  if (!newProject())
1287  return;
1288 
1289  WAIT_CURSOR;
1290  m_project->setFileName(filename);
1291  QElapsedTimer timer;
1292  timer.start();
1293  bool rc = false;
1294  if (Project::isLabPlotProject(filename)) {
1295  rc = m_project->load(filename);
1296  }
1297 #ifdef HAVE_LIBORIGIN
1298  else if (OriginProjectParser::isOriginProject(filename)) {
1300  parser.setProjectFileName(filename);
1301  parser.importTo(m_project, QStringList()); //TODO: add return code
1302  rc = true;
1303  }
1304 #endif
1305 
1306 #ifdef HAVE_CANTOR_LIBS
1307  else if (QFileInfo(filename).completeSuffix() == QLatin1String("cws")) {
1308  QFile file(filename);
1309  KZip archive(&file);
1310  rc = archive.open(QIODevice::ReadOnly);
1311  if (rc) {
1312  const auto* contentEntry = archive.directory()->entry(QLatin1String("content.xml"));
1313  if (contentEntry && contentEntry->isFile()) {
1314  const auto* contentFile = static_cast<const KArchiveFile*>(contentEntry);
1315  QByteArray data = contentFile->data();
1316  archive.close();
1317 
1318  //determine the name of the backend
1319  QDomDocument doc;
1320  doc.setContent(data);
1321  QString backendName = doc.documentElement().attribute(QLatin1String("backend"));
1322 
1323  if (!backendName.isEmpty()) {
1324  //create new Cantor worksheet and load the data
1325  auto* worksheet = new CantorWorksheet(backendName);
1326  worksheet->setName(QFileInfo(filename).fileName());
1327  worksheet->setComment(filename);
1328 
1329  rc = file.open(QIODevice::ReadOnly);
1330  if (rc) {
1331  QByteArray content = file.readAll();
1332  rc = worksheet->init(&content);
1333  if (rc)
1334  m_project->addChild(worksheet);
1335  else {
1336  delete worksheet;
1337  RESET_CURSOR;
1338  QMessageBox::critical(this, i18n("Failed to open project"), i18n("Failed to process the content of the file '%1'.", filename));
1339  }
1340  }else {
1341  RESET_CURSOR;
1342  QMessageBox::critical(this, i18n("Failed to open project"), i18n("Failed to open the file '%1'.", filename));
1343  }
1344  } else {
1345  RESET_CURSOR;
1346  rc = false;
1347  QMessageBox::critical(this, i18n("Failed to open project"), i18n("Failed to process the content of the file '%1'.", filename));
1348  }
1349  } else {
1350  RESET_CURSOR;
1351  rc = false;
1352  QMessageBox::critical(this, i18n("Failed to open project"), i18n("Failed to process the content of the file '%1'.", filename));
1353  }
1354  } else {
1355  RESET_CURSOR;
1356  QMessageBox::critical(this, i18n("Failed to open project"), i18n("Failed to open the file '%1'.", filename));
1357  }
1358  } else if (QFileInfo(filename).completeSuffix() == QLatin1String("ipynb")) {
1359  QFile file(filename);
1360  rc = file.open(QIODevice::ReadOnly);
1361  if (rc) {
1362  QByteArray content = file.readAll();
1363  QJsonParseError error;
1364  // TODO: use QJsonDocument& doc = QJsonDocument::fromJson(content, &error); if minimum Qt version is at least 5.10
1365  const QJsonDocument& jsonDoc = QJsonDocument::fromJson(content, &error);
1366  const QJsonObject& doc = jsonDoc.object();
1367  if (error.error == QJsonParseError::NoError) {
1368  //determine the backend name
1369  QString backendName;
1370  // TODO: use doc["metadata"]["kernelspec"], etc. if minimum Qt version is at least 5.10
1371  if ((doc["metadata"] != QJsonValue::Undefined && doc["metadata"].isObject())
1372  && (doc["metadata"].toObject()["kernelspec"] != QJsonValue::Undefined && doc["metadata"].toObject()["kernelspec"].isObject()) ) {
1373 
1374  QString kernel;
1375  if (doc["metadata"].toObject()["kernelspec"].toObject()["name"] != QJsonValue::Undefined)
1376  kernel = doc["metadata"].toObject()["kernelspec"].toObject()["name"].toString();
1377 
1378  if (!kernel.isEmpty()) {
1379  if (kernel.startsWith(QLatin1String("julia")))
1380  backendName = QLatin1String("julia");
1381  else if (kernel == QLatin1String("sagemath"))
1382  backendName = QLatin1String("sage");
1383  else if (kernel == QLatin1String("ir"))
1384  backendName = QLatin1String("r");
1385  else if (kernel == QLatin1String("python3") || kernel == QLatin1String("python2"))
1386  backendName = QLatin1String("python");
1387  else
1388  backendName = kernel;
1389  } else
1390  backendName = doc["metadata"].toObject()["kernelspec"].toObject()["language"].toString();
1391 
1392  if (!backendName.isEmpty()) {
1393  //create new Cantor worksheet and load the data
1394  auto* worksheet = new CantorWorksheet(backendName);
1395  worksheet->setName(QFileInfo(filename).fileName());
1396  worksheet->setComment(filename);
1397  rc = worksheet->init(&content);
1398  if (rc)
1399  m_project->addChild(worksheet);
1400  else {
1401  delete worksheet;
1402  RESET_CURSOR;
1403  QMessageBox::critical(this, i18n("Failed to open project"), i18n("Failed to process the content of the file '%1'.", filename));
1404  }
1405  } else {
1406  RESET_CURSOR;
1407  rc = false;
1408  QMessageBox::critical(this, i18n("Failed to open project"), i18n("Failed to process the content of the file '%1'.", filename));
1409  }
1410  } else {
1411  RESET_CURSOR;
1412  rc = false;
1413  QMessageBox::critical(this, i18n("Failed to open project"), i18n("Failed to process the content of the file '%1'.", filename));
1414  }
1415  }
1416  } else {
1417  RESET_CURSOR;
1418  rc = false;
1419  QMessageBox::critical(this, i18n("Failed to open project"), i18n("Failed to open the file '%1'.", filename));
1420  }
1421  }
1422 #endif
1423 
1424  m_project->setChanged(false);
1425 
1426  if (!rc) {
1427  closeProject();
1428  RESET_CURSOR;
1429  return;
1430  }
1431 
1432  m_currentFileName = filename;
1433  m_project->undoStack()->clear();
1434  m_undoViewEmptyLabel = i18n("%1: opened", m_project->name());
1435  m_recentProjectsAction->addUrl( QUrl(filename) );
1436  updateTitleBar();
1438  updateGUI(); //there are most probably worksheets or spreadsheets in the open project -> update the GUI
1440  m_saveAction->setEnabled(false);
1441 
1442  statusBar()->showMessage( i18n("Project successfully opened (in %1 seconds).", (float)timer.elapsed()/1000) );
1443 
1444  KConfigGroup group = KSharedConfig::openConfig()->group(QLatin1String("MainWin"));
1445  group.writeEntry("LastOpenProject", filename);
1446 
1447  if (m_autoSaveActive)
1448  m_autoSaveTimer.start();
1449 
1450  RESET_CURSOR;
1451 }
1452 
1453 void MainWin::openRecentProject(const QUrl& url) {
1454 // if (dynamic_cast<QQuickWidget*>(centralWidget())) {
1455 // createMdiArea();
1456 // setCentralWidget(m_mdiArea);
1457 // }
1458 
1459  if (url.isLocalFile()) // fix for Windows
1460  this->openProject(url.toLocalFile());
1461  else
1462  this->openProject(url.path());
1463 }
1464 
1465 /*!
1466  Closes the current project, if available. Return \c true, if the project was closed.
1467 */
1469  if (m_project == nullptr)
1470  return true; //nothing to close
1471 
1472  if (warnModified())
1473  return false;
1474 
1475  if (!m_closing) {
1476 // if (dynamic_cast<QQuickWidget*>(centralWidget()) && m_showWelcomeScreen) {
1477 // m_welcomeWidget = createWelcomeScreen();
1478 // setCentralWidget(m_welcomeWidget);
1479 // }
1480  }
1481 
1482  //hide the sub-windows prior to deleting them in order to get rid of the shadows
1483  //drawn across the sub-windows by the style. The shadow is removed by closing/hiding
1484  //the sub-window exlicitely but not if we just delete it.
1485  //TODO: the actual fix is in https://invent.kde.org/plasma/breeze/-/merge_requests/43,
1486  //we can remove this hack later.
1487  for (auto* window : m_mdiArea->subWindowList())
1488  window->hide();
1489 
1490  m_projectClosing = true;
1491  statusBar()->clearMessage();
1492  delete m_aspectTreeModel;
1493  m_aspectTreeModel = nullptr;
1494  delete m_project;
1495  m_project = nullptr;
1496  m_currentFileName.clear();
1497  m_projectClosing = false;
1498 
1499  //update the UI if we're just closing a project
1500  //and not closing(quitting) the application
1501  if (!m_closing) {
1502  m_projectExplorerDock->hide();
1503  m_propertiesDock->hide();
1504  m_currentAspect = nullptr;
1505  m_currentFolder = nullptr;
1507 
1508  if (m_autoSaveActive)
1509  m_autoSaveTimer.stop();
1510  }
1511 
1512  removeDockWidget(cursorDock);
1513  delete cursorDock;
1514  cursorDock = nullptr;
1515  cursorWidget = nullptr; // is deleted, because it's the cild of cursorDock
1516 
1517  return true;
1518 }
1519 
1521  const QString& fileName = m_project->fileName();
1522  if (fileName.isEmpty())
1523  return saveProjectAs();
1524  else
1525  return save(fileName);
1526 }
1527 
1529  KConfigGroup conf(KSharedConfig::openConfig(), "MainWin");
1530  const QString& dir = conf.readEntry("LastOpenDir", "");
1531  QString path = QFileDialog::getSaveFileName(this, i18n("Save Project As"), dir,
1532  i18n("LabPlot Projects (*.lml *.lml.gz *.lml.bz2 *.lml.xz *.LML *.LML.GZ *.LML.BZ2 *.LML.XZ)"));
1533 
1534  if (path.isEmpty())// "Cancel" was clicked
1535  return false;
1536 
1537  if (path.contains(QLatin1String(".lml"), Qt::CaseInsensitive) == false)
1538  path.append(QLatin1String(".lml"));
1539 
1540  //save new "last open directory"
1541  int pos = path.lastIndexOf(QLatin1String("/"));
1542  if (pos != -1) {
1543  const QString& newDir = path.left(pos);
1544  if (newDir != dir)
1545  conf.writeEntry("LastOpenDir", newDir);
1546  }
1547 
1548  return save(path);
1549 }
1550 
1551 /*!
1552  * auxiliary function that does the actual saving of the project
1553  */
1554 bool MainWin::save(const QString& fileName) {
1555  QTemporaryFile tempFile(QDir::tempPath() + QLatin1Char('/') + QLatin1String("labplot_save_XXXXXX"));
1556  if (!tempFile.open()) {
1557  KMessageBox::error(this, i18n("Couldn't open the temporary file for writing."));
1558  return false;
1559  }
1560 
1561  WAIT_CURSOR;
1562  const QString& tempFileName = tempFile.fileName();
1563  DEBUG("Using temporary file " << STDSTRING(tempFileName))
1564  tempFile.close();
1565 
1566  // use file ending to find out how to compress file
1567  QIODevice* file;
1568  // if ending is .lml, do gzip compression anyway
1569  if (fileName.endsWith(QLatin1String(".lml")))
1570  file = new KCompressionDevice(tempFileName, KCompressionDevice::GZip);
1571  else
1572  file = new KFilterDev(tempFileName);
1573 
1574  if (file == nullptr)
1575  file = new QFile(tempFileName);
1576 
1577  bool ok;
1578  if (file->open(QIODevice::WriteOnly)) {
1579  m_project->setFileName(fileName);
1580 
1581  QPixmap thumbnail = centralWidget()->grab();
1582 
1583  QXmlStreamWriter writer(file);
1584  m_project->setFileName(fileName);
1585  m_project->save(thumbnail, &writer);
1586  m_project->undoStack()->clear();
1587  m_project->setChanged(false);
1588  file->close();
1589 
1590  // target file must not exist
1591  if (QFile::exists(fileName))
1592  QFile::remove(fileName);
1593 
1594  // do not rename temp file. Qt still holds a handle (which fails renaming on Windows) and deletes it
1595  bool rc = QFile::copy(tempFileName, fileName);
1596  if (rc) {
1597  updateTitleBar();
1598  statusBar()->showMessage(i18n("Project saved"));
1599  m_saveAction->setEnabled(false);
1600  m_recentProjectsAction->addUrl( QUrl(fileName) );
1601  ok = true;
1602 
1603  //if the project dock is visible, refresh the shown content
1604  //(version and modification time might have been changed)
1605  if (stackedWidget->currentWidget() == projectDock)
1607 
1608  //we have a file name now
1609  // -> auto save can be activated now if not happened yet
1610  if (m_autoSaveActive && !m_autoSaveTimer.isActive())
1611  m_autoSaveTimer.start();
1612  } else {
1613  RESET_CURSOR;
1614  KMessageBox::error(this, i18n("Couldn't save the file '%1'.", fileName));
1615  ok = false;
1616  }
1617  } else {
1618  RESET_CURSOR;
1619  KMessageBox::error(this, i18n("Couldn't open the file '%1' for writing.", fileName));
1620  ok = false;
1621  }
1622 
1623  delete file;
1624 
1625  RESET_CURSOR;
1626  return ok;
1627 }
1628 
1629 /*!
1630  * automatically saves the project in the specified time interval.
1631  */
1633  //don't auto save when there are no changes or the file name
1634  //was not provided yet (the project was never explicitly saved yet).
1635  if ( !m_project->hasChanged() || m_project->fileName().isEmpty())
1636  return;
1637 
1638  this->saveProject();
1639 }
1640 
1642  QString title;
1643  if (m_project) {
1644  switch (m_titleBarMode) {
1646  title = m_project->name();
1647  break;
1649  if (m_project->fileName().isEmpty())
1650  title = m_project->name();
1651  else {
1652  QFileInfo fi(m_project->fileName());
1653  title = fi.baseName();
1654  }
1655  break;
1657  if (m_project->fileName().isEmpty())
1658  title = m_project->name();
1659  else
1660  title = m_project->fileName();
1661  }
1662 
1663  if (m_project->hasChanged())
1664  title += QLatin1String(" [") + i18n("Changed") + QLatin1Char(']');
1665  } else
1666  title = QLatin1String("LabPlot");
1667 
1668  setCaption(title);
1669 }
1670 
1671 /*!
1672  prints the current sheet (worksheet, spreadsheet or matrix)
1673 */
1675  QMdiSubWindow* win = m_mdiArea->currentSubWindow();
1676  if (!win)
1677  return;
1678 
1679  AbstractPart* part = static_cast<PartMdiView*>(win)->part();
1680  statusBar()->showMessage(i18n("Preparing printing of %1", part->name()));
1681  if (part->printView())
1682  statusBar()->showMessage(i18n("%1 printed", part->name()));
1683  else
1684  statusBar()->clearMessage();
1685 }
1686 
1688  QMdiSubWindow* win = m_mdiArea->currentSubWindow();
1689  if (!win)
1690  return;
1691 
1692  AbstractPart* part = static_cast<PartMdiView*>(win)->part();
1693  statusBar()->showMessage(i18n("Preparing printing of %1", part->name()));
1694  if (part->printPreview())
1695  statusBar()->showMessage(i18n("%1 printed", part->name()));
1696  else
1697  statusBar()->clearMessage();
1698 }
1699 
1700 /**************************************************************************************/
1701 
1702 /*!
1703  adds a new Folder to the project.
1704 */
1706  Folder* folder = new Folder(i18n("Folder"));
1707  this->addAspectToProject(folder);
1708 }
1709 
1710 /*!
1711  adds a new Workbook to the project.
1712 */
1714  Workbook* workbook = new Workbook(i18n("Workbook"));
1715  this->addAspectToProject(workbook);
1716 }
1717 
1718 /*!
1719  adds a new Datapicker to the project.
1720 */
1722  Datapicker* datapicker = new Datapicker(i18n("Datapicker"));
1723  this->addAspectToProject(datapicker);
1724 }
1725 /*!
1726  adds a new Spreadsheet to the project.
1727 */
1729  Spreadsheet* spreadsheet = new Spreadsheet(i18n("Spreadsheet"));
1730 
1731  //if the current active window is a workbook and no folder/project is selected in the project explorer,
1732  //add the new spreadsheet to the workbook
1733  Workbook* workbook = dynamic_cast<Workbook*>(m_currentAspect);
1734  if (workbook) {
1735  QModelIndex index = m_projectExplorer->currentIndex();
1736  const auto* aspect = static_cast<AbstractAspect*>(index.internalPointer());
1737  if (!aspect->inherits(AspectType::Folder)) {
1738  workbook->addChild(spreadsheet);
1739  return;
1740  }
1741  }
1742 
1743  this->addAspectToProject(spreadsheet);
1744 }
1745 
1746 /*!
1747  adds a new Matrix to the project.
1748 */
1750  Matrix* matrix = new Matrix(i18n("Matrix"));
1751 
1752  //if the current active window is a workbook and no folder/project is selected in the project explorer,
1753  //add the new matrix to the workbook
1754  Workbook* workbook = dynamic_cast<Workbook*>(m_currentAspect);
1755  if (workbook) {
1756  QModelIndex index = m_projectExplorer->currentIndex();
1757  const auto* aspect = static_cast<AbstractAspect*>(index.internalPointer());
1758  if (!aspect->inherits(AspectType::Folder)) {
1759  workbook->addChild(matrix);
1760  return;
1761  }
1762  }
1763 
1764  this->addAspectToProject(matrix);
1765 }
1766 
1767 /*!
1768  adds a new Worksheet to the project.
1769 */
1771  Worksheet* worksheet = new Worksheet(i18n("Worksheet"));
1772  this->addAspectToProject(worksheet);
1773 }
1774 
1775 /*!
1776  adds a new Note to the project.
1777 */
1779  Note* notes = new Note(i18n("Note"));
1780  this->addAspectToProject(notes);
1781 }
1782 
1783 /*!
1784  returns a pointer to a \c Spreadsheet object, if the currently active Mdi-Subwindow
1785  or if the currently selected tab in a \c WorkbookView is a \c SpreadsheetView
1786  Otherwise returns \c 0.
1787 */
1789 // if (dynamic_cast<QQuickWidget*>(centralWidget()))
1790 // return nullptr;
1791 
1792  if (!m_currentAspect)
1793  return nullptr;
1794 
1795  Spreadsheet* spreadsheet = nullptr;
1797  spreadsheet = dynamic_cast<Spreadsheet*>(m_currentAspect);
1798  else {
1799  //check whether one of spreadsheet columns is selected and determine the spreadsheet
1800  auto* parent = m_currentAspect->parentAspect();
1801  if (parent && parent->type() == AspectType::Spreadsheet)
1802  spreadsheet = dynamic_cast<Spreadsheet*>(parent);
1803  }
1804 
1805  return spreadsheet;
1806 }
1807 
1808 #ifdef HAVE_CANTOR_LIBS
1809 /*
1810  adds a new Cantor Spreadsheet to the project.
1811 */
1812 void MainWin::newCantorWorksheet(QAction* action) {
1813  auto* cantorworksheet = new CantorWorksheet(action->data().toString());
1814  this->addAspectToProject(cantorworksheet);
1815 }
1816 
1817 /********************************************************************************/
1818 #endif
1819 
1820 /*!
1821  called if there were changes in the project.
1822  Adds "changed" to the window caption and activates the save-Action.
1823 */
1825  updateTitleBar();
1826  m_saveAction->setEnabled(true);
1827  m_undoAction->setEnabled(true);
1828 }
1829 
1830 void MainWin::handleCurrentSubWindowChanged(QMdiSubWindow* win) {
1831  if (!win) {
1832  updateGUI();
1833  return;
1834  }
1835 
1836  auto* view = static_cast<PartMdiView*>(win);
1837  if (view == m_currentSubWindow) {
1838  //do nothing, if the current sub-window gets selected again.
1839  //This event happens, when labplot loses the focus (modal window is opened or the user switches to another application)
1840  //and gets it back (modal window is closed or the user switches back to labplot).
1841  return;
1842  } else
1843  m_currentSubWindow = view;
1844 
1845  updateGUI();
1847  m_projectExplorer->setCurrentAspect(view->part());
1848 }
1849 
1851  const auto* part = dynamic_cast<const AbstractPart*>(aspect);
1852  if (part) {
1853 // connect(part, &AbstractPart::importFromFileRequested, this, &MainWin::importFileDialog);
1854  connect(part, &AbstractPart::importFromFileRequested, this, [=]() {importFileDialog();});
1856  //TODO: export, print and print preview should be handled in the views and not in MainWin.
1857  connect(part, &AbstractPart::exportRequested, this, &MainWin::exportDialog);
1858  connect(part, &AbstractPart::printRequested, this, &MainWin::print);
1861 
1862  const auto* worksheet = dynamic_cast<const Worksheet*>(aspect);
1863  if (worksheet) {
1866  }
1867  }
1868 }
1869 
1870 void MainWin::handleAspectRemoved(const AbstractAspect* parent,const AbstractAspect* before,const AbstractAspect* aspect) {
1871  Q_UNUSED(before);
1872  Q_UNUSED(aspect);
1873 
1874  //no need to react on removal of
1875  // - AbstractSimpleFilter
1876  // - columns in the data spreadsheet of a datapicker curve,
1877  // this can only happen when changing the error type and is done on the level of DatapickerImage
1878  if (!dynamic_cast<const AbstractSimpleFilter*>(aspect)
1879  && !(parent->parentAspect() && parent->parentAspect()->type() == AspectType::DatapickerCurve) )
1881 }
1882 
1884  const auto* part = dynamic_cast<const AbstractPart*>(aspect);
1885  if (!part)
1886  return;
1887 
1888  const auto* workbook = dynamic_cast<const Workbook*>(aspect->parentAspect());
1889  auto* datapicker = dynamic_cast<const Datapicker*>(aspect->parentAspect());
1890  if (!datapicker)
1891  datapicker = dynamic_cast<const Datapicker*>(aspect->parentAspect()->parentAspect());
1892 
1893  if (!workbook && !datapicker) {
1894  PartMdiView* win = part->mdiSubWindow();
1895  if (win)
1896  m_mdiArea->removeSubWindow(win);
1897  }
1898 }
1899 
1900 /*!
1901  called when the current aspect in the tree of the project explorer was changed.
1902  Selects the new aspect.
1903 */
1905  if (!aspect)
1906  aspect = m_project; // should never happen, just in case
1907 
1909  if (aspect->folder() != m_currentFolder) {
1910  m_currentFolder = aspect->folder();
1912  }
1913 
1914  m_currentAspect = aspect;
1915 
1916  //activate the corresponding MDI sub window for the current aspect
1919 
1920  updateGUI();
1921 }
1922 
1924  Q_ASSERT(aspect);
1925  const auto* part = dynamic_cast<const AbstractPart*>(aspect);
1926  if (part) {
1927  PartMdiView* win{nullptr};
1928 
1929  //for aspects being children of a Workbook, we show workbook's window, otherwise the window of the selected part
1930  const auto* workbook = dynamic_cast<const Workbook*>(aspect->parentAspect());
1931  auto* datapicker = dynamic_cast<const Datapicker*>(aspect->parentAspect());
1932  if (!datapicker)
1933  datapicker = dynamic_cast<const Datapicker*>(aspect->parentAspect()->parentAspect());
1934 
1935  if (workbook)
1936  win = workbook->mdiSubWindow();
1937  else if (datapicker)
1938  win = datapicker->mdiSubWindow();
1939  else
1940  win = part->mdiSubWindow();
1941 
1942  if (m_mdiArea && m_mdiArea->subWindowList().indexOf(win) == -1) {
1943  if (dynamic_cast<const Note*>(part))
1944  m_mdiArea->addSubWindow(win, Qt::Tool);
1945  else
1946  m_mdiArea->addSubWindow(win);
1947  win->show();
1948 
1949  //Qt provides its own "system menu" for every sub-window. The shortcut for the close-action
1950  //in this menu collides with our global m_closeAction.
1951  //remove the shortcuts in the system menu to avoid this collision.
1952  QMenu* menu = win->systemMenu();
1953  if (menu) {
1954  for (QAction* action : menu->actions())
1955  action->setShortcut(QKeySequence());
1956  }
1957  }
1958  if (m_mdiArea)
1959  m_mdiArea->setActiveSubWindow(win);
1960  } else {
1961  //activate the mdiView of the parent, if a child was selected
1962  const AbstractAspect* parent = aspect->parentAspect();
1963  if (parent) {
1965 
1966  //if the parent's parent is a Workbook (a column of a spreadsheet in workbook was selected),
1967  //we need to select the corresponding tab in WorkbookView too
1968  if (parent->parentAspect()) {
1969  auto* workbook = dynamic_cast<Workbook*>(parent->parentAspect());
1970  auto* datapicker = dynamic_cast<Datapicker*>(parent->parentAspect());
1971  if (!datapicker)
1972  datapicker = dynamic_cast<Datapicker*>(parent->parentAspect()->parentAspect());
1973 
1974  if (workbook)
1975  workbook->childSelected(parent);
1976  else if (datapicker)
1977  datapicker->childSelected(parent);
1978  }
1979  }
1980  }
1981  return;
1982 }
1983 
1986 }
1987 
1988 /*!
1989  shows the sub window of a worksheet, matrix or a spreadsheet.
1990  Used if the window was closed before and the user asks to show
1991  the window again via the context menu in the project explorer.
1992 */
1994  if (m_currentAspect)
1996 }
1997 
1998 /*!
1999  this is called on a right click on the root folder in the project explorer
2000 */
2001 void MainWin::createContextMenu(QMenu* menu) const {
2002  QAction* firstAction = nullptr;
2003  // if we're populating the context menu for the project explorer, then
2004  //there're already actions available there. Skip the first title-action
2005  //and insert the action at the beginning of the menu.
2006  if (menu->actions().size()>1)
2007  firstAction = menu->actions().at(1);
2008 
2009  menu->insertMenu(firstAction, m_newMenu);
2010 
2011  //The tabbed view collides with the visibility policy for the subwindows.
2012  //Hide the menus for the visibility policy if the tabbed view is used.
2013  if (m_mdiArea->viewMode() != QMdiArea::TabbedView) {
2014  menu->insertSeparator(firstAction);
2015  menu->insertMenu(firstAction, m_visibilityMenu);
2016  menu->insertSeparator(firstAction);
2017  }
2018 }
2019 
2020 /*!
2021  this is called on a right click on a non-root folder in the project explorer
2022 */
2023 void MainWin::createFolderContextMenu(const Folder* folder, QMenu* menu) const {
2024  Q_UNUSED(folder);
2025 
2026  //Folder provides it's own context menu. Add a separator before adding additional actions.
2027  menu->addSeparator();
2028  this->createContextMenu(menu);
2029 }
2030 
2032  WAIT_CURSOR;
2033  m_project->setSuppressAspectAddedSignal(true);//don't change the current aspect in the project explorer
2034  m_project->undoStack()->undo();
2036  if (m_project->undoStack()->index() == 0) {
2037  updateTitleBar();
2038  m_saveAction->setEnabled(false);
2039  m_undoAction->setEnabled(false);
2040  m_project->setChanged(false);
2041  }
2042  m_redoAction->setEnabled(true);
2043  RESET_CURSOR;
2044 }
2045 
2047  WAIT_CURSOR;
2049  m_project->undoStack()->redo();
2051  projectChanged();
2052  if (m_project->undoStack()->index() == m_project->undoStack()->count())
2053  m_redoAction->setEnabled(false);
2054  RESET_CURSOR;
2055 }
2056 
2057 /*!
2058  Shows/hides mdi sub-windows depending on the current visibility policy.
2059 */
2061  auto windows = m_mdiArea->subWindowList();
2062  switch (m_project->mdiWindowVisibility()) {
2064  for (auto* window : windows)
2065  window->show();
2066 
2067  break;
2069  for (auto* window : windows) {
2070  auto* view = static_cast<PartMdiView*>(window);
2071  bool visible = view->part()->folder() == m_currentFolder;
2072  window->setVisible(visible);
2073  }
2074  break;
2076  for (auto* window : windows) {
2077  auto* view = static_cast<PartMdiView*>(window);
2078  bool visible = view->part()->isDescendantOf(m_currentFolder);
2079  window->setVisible(visible);
2080  }
2081  break;
2082  }
2083 }
2084 
2086  if (action->objectName() == "toggle_project_explorer_dock") {
2087  if (m_projectExplorerDock->isVisible())
2088  m_projectExplorerDock->hide();
2089 // toggleHideWidget(m_projectExplorerDock, true);
2090  else
2091  m_projectExplorerDock->show();
2092 // toggleShowWidget(m_projectExplorerDock, true);
2093  } else if (action->objectName() == "toggle_properties_explorer_dock") {
2094  if (m_propertiesDock->isVisible())
2095  m_propertiesDock->hide();
2096 // toggleHideWidget(m_propertiesDock, false);
2097  else
2098  m_propertiesDock->show();
2099 // toggleShowWidget(m_propertiesDock, false);
2100  }
2101 }
2102 
2104  statusBar()->setVisible(!statusBar()->isVisible());
2105  m_toggleMemoryInfoAction->setEnabled(statusBar()->isVisible());
2106 
2107  //show the current setting here because in the desctuctor the status bar is not visible anymore
2108  KConfigGroup group = KSharedConfig::openConfig()->group("MainWin");
2109  group.writeEntry(QLatin1String("ShowStatusBar"), statusBar()->isVisible());
2110 }
2111 
2113  if (m_memoryInfoWidget) {
2114  statusBar()->removeWidget(m_memoryInfoWidget);
2115  delete m_memoryInfoWidget;
2116  m_memoryInfoWidget = nullptr;
2117  } else {
2118  m_memoryInfoWidget = new MemoryWidget(statusBar());
2119  statusBar()->addPermanentWidget(m_memoryInfoWidget);
2120  }
2121 }
2122 
2124  if (!m_propertiesDock->isVisible())
2125  m_propertiesDock->show();
2126 }
2127 
2128 /*
2129 void MainWin::toggleHideWidget(QWidget* widget, bool hideToLeft)
2130 {
2131  auto* timeline = new QTimeLine(800, this);
2132  timeline->setEasingCurve(QEasingCurve::InOutQuad);
2133 
2134  connect(timeline, &QTimeLine::valueChanged, [=] {
2135  const qreal value = timeline->currentValue();
2136  const int widgetWidth = widget->width();
2137  const int widgetPosY = widget->pos().y();
2138 
2139  int moveX = 0;
2140  if (hideToLeft) {
2141  moveX = static_cast<int>(value * widgetWidth) - widgetWidth;
2142  }
2143  else {
2144  const int frameRight = this->frameGeometry().right();
2145  moveX = frameRight - static_cast<int>(value * widgetWidth);
2146  }
2147  widget->move(moveX, widgetPosY);
2148  });
2149  timeline->setDirection(QTimeLine::Backward);
2150  timeline->start();
2151 
2152  connect(timeline, &QTimeLine::finished, [widget] {widget->hide();});
2153  connect(timeline, &QTimeLine::finished, timeline, &QTimeLine::deleteLater);
2154 }
2155 
2156 void MainWin::toggleShowWidget(QWidget* widget, bool showToRight)
2157 {
2158  auto* timeline = new QTimeLine(800, this);
2159  timeline->setEasingCurve(QEasingCurve::InOutQuad);
2160 
2161  connect(timeline, &QTimeLine::valueChanged, [=]() {
2162  if (widget->isHidden()) {
2163  widget->show();
2164  }
2165  const qreal value = timeline->currentValue();
2166  const int widgetWidth = widget->width();
2167  const int widgetPosY = widget->pos().y();
2168  int moveX = 0;
2169 
2170  if (showToRight) {
2171  moveX = static_cast<int>(value * widgetWidth) - widgetWidth;
2172  }
2173  else {
2174  const int frameRight = this->frameGeometry().right();
2175  moveX = frameRight - static_cast<int>(value * widgetWidth);
2176  }
2177  widget->move(moveX, widgetPosY);
2178  });
2179 
2180  timeline->setDirection(QTimeLine::Forward);
2181  timeline->start();
2182 
2183  connect(timeline, &QTimeLine::finished, timeline, &QTimeLine::deleteLater);
2184 }
2185 */
2187  m_toggleProjectExplorerDockAction->setChecked(visible);
2188 }
2189 
2191  m_togglePropertiesDockAction->setChecked(visible);
2192 }
2193 
2195  //if the cursor dock was closed, switch to the "Select and Edit" mouse mode
2196  if (!visible) {
2197 // auto* worksheet = activeWorksheet();
2198  //TODO:
2199  }
2200 }
2201 
2204  if (cursorDock)
2205  cursorDock->hide();
2206  } else {
2207  if (!cursorDock) {
2208  cursorDock = new QDockWidget(i18n("Cursor"), this);
2210  cursorDock->setWidget(cursorWidget);
2211  connect(cursorDock, &QDockWidget::visibilityChanged, this, &MainWin::cursorDockVisibilityChanged);
2212  // cursorDock->setFloating(true);
2213 
2214  // does not work. Don't understand why
2215  // if (m_propertiesDock)
2216  // tabifyDockWidget(cursorDock, m_propertiesDock);
2217  // else
2218  addDockWidget(Qt::DockWidgetArea::RightDockWidgetArea, cursorDock);
2219  }
2220 
2221  auto* worksheet = static_cast<Worksheet*>(QObject::sender());
2222  cursorWidget->setWorksheet(worksheet);
2223  cursorDock->show();
2224  }
2225 }
2226 
2228  m_toggleFullScreenAction->setFullScreen(this, t);
2229 }
2230 
2231 void MainWin::closeEvent(QCloseEvent* event) {
2232  m_closing = true;
2233  if (!this->closeProject()) {
2234  m_closing = false;
2235  event->ignore();
2236  }
2237 }
2238 
2239 void MainWin::dragEnterEvent(QDragEnterEvent* event) {
2240  event->accept();
2241 }
2242 
2243 void MainWin::dropEvent(QDropEvent* event) {
2244  if (event->mimeData() && !event->mimeData()->urls().isEmpty()) {
2245  QUrl url = event->mimeData()->urls().at(0);
2246  const QString& f = url.toLocalFile();
2247 
2248  bool open = Project::isLabPlotProject(f);
2249 #ifdef HAVE_LIBORIGIN
2250  if (!open)
2252 #endif
2253 
2254 #ifdef HAVE_CANTOR_LIBS
2255  if (!open) {
2256  QFileInfo fi(f);
2257  open = (fi.completeSuffix() == QLatin1String("cws")) || (fi.completeSuffix() == QLatin1String("ipynb"));
2258  }
2259 #endif
2260 
2261  if (open)
2262  openProject(f);
2263  else {
2264  if (!m_project)
2265  newProject();
2266  importFileDialog(f);
2267  }
2268 
2269  event->accept();
2270  } else
2271  event->ignore();
2272 }
2273 
2275  const KConfigGroup group = KSharedConfig::openConfig()->group( "Settings_General" );
2276 
2277  //title bar
2278  MainWin::TitleBarMode titleBarMode = static_cast<MainWin::TitleBarMode>(group.readEntry("TitleBar", 0));
2279  if (titleBarMode != m_titleBarMode) {
2280  m_titleBarMode = titleBarMode;
2281  updateTitleBar();
2282  }
2283 
2284  //view mode
2285 // if (dynamic_cast<QQuickWidget*>(centralWidget()) == nullptr) {
2286  QMdiArea::ViewMode viewMode = QMdiArea::ViewMode(group.readEntry("ViewMode", 0));
2287  if (m_mdiArea->viewMode() != viewMode) {
2288  m_mdiArea->setViewMode(viewMode);
2289  if (viewMode == QMdiArea::SubWindowView)
2290  this->updateMdiWindowVisibility();
2291  }
2292 
2293  if (m_mdiArea->viewMode() == QMdiArea::TabbedView) {
2294  m_tileWindowsAction->setVisible(false);
2295  m_cascadeWindowsAction->setVisible(false);
2296  QTabWidget::TabPosition tabPosition = QTabWidget::TabPosition(group.readEntry("TabPosition", 0));
2297  if (m_mdiArea->tabPosition() != tabPosition)
2298  m_mdiArea->setTabPosition(tabPosition);
2299  } else {
2300  m_tileWindowsAction->setVisible(true);
2301  m_cascadeWindowsAction->setVisible(true);
2302  }
2303 // }
2304 
2305  //window visibility
2306  auto vis = Project::MdiWindowVisibility(group.readEntry("MdiWindowVisibility", 0));
2307  if (m_project && (vis != m_project->mdiWindowVisibility())) {
2309  m_visibilityFolderAction->setChecked(true);
2311  m_visibilitySubfolderAction->setChecked(true);
2312  else
2313  m_visibilityAllAction->setChecked(true);
2315  }
2316 
2317  //autosave
2318  bool autoSave = group.readEntry("AutoSave", 0);
2319  if (m_autoSaveActive != autoSave) {
2320  m_autoSaveActive = autoSave;
2321  if (autoSave)
2322  m_autoSaveTimer.start();
2323  else
2324  m_autoSaveTimer.stop();
2325  }
2326 
2327  int interval = group.readEntry("AutoSaveInterval", 1);
2328  interval *= 60*1000;
2329  if (interval != m_autoSaveTimer.interval())
2330  m_autoSaveTimer.setInterval(interval);
2331 
2332  //update the locale and the units in the dock widgets
2333  if (stackedWidget) {
2334  for (int i = 0; i < stackedWidget->count(); ++i) {
2335  auto* widget = stackedWidget->widget(i);
2336  BaseDock* dock = dynamic_cast<BaseDock*>(widget);
2337  if (dock) {
2338  dock->updateLocale();
2339  dock->updateUnits();
2340  } else {
2341  auto* labelWidget = dynamic_cast<LabelWidget*>(widget);
2342  if (labelWidget)
2343  labelWidget->updateUnits();
2344  }
2345  }
2346  }
2347 
2348  //update spreadsheet header
2349  if (m_project) {
2351  for (auto* spreadsheet : spreadsheets) {
2352  spreadsheet->updateHorizontalHeader();
2353  spreadsheet->updateLocale();
2354  }
2355  }
2356 
2357  bool showWelcomeScreen = group.readEntry<bool>(QLatin1String("ShowWelcomeScreen"), true);
2358  if (m_showWelcomeScreen != showWelcomeScreen)
2359  m_showWelcomeScreen = showWelcomeScreen;
2360 }
2361 
2363  newProject();
2364 // addAspectToProject(m_welcomeScreenHelper->releaseConfiguredSpreadsheet());
2365 }
2366 
2367 /***************************************************************************************/
2368 /************************************** dialogs ***************************************/
2369 /***************************************************************************************/
2370 /*!
2371  shows the dialog with the Undo-history.
2372 */
2374  if (!m_project->undoStack())
2375  return;
2376 
2377  auto* dialog = new HistoryDialog(this, m_project->undoStack(), m_undoViewEmptyLabel);
2378  int index = m_project->undoStack()->index();
2379  if (dialog->exec() != QDialog::Accepted) {
2380  if (m_project->undoStack()->count() != 0)
2381  m_project->undoStack()->setIndex(index);
2382  }
2383 
2384  //disable undo/redo-actions if the history was cleared
2385  //(in both cases, when accepted or rejected in the dialog)
2386  if (m_project->undoStack()->count() == 0) {
2387  m_undoAction->setEnabled(false);
2388  m_redoAction->setEnabled(false);
2389  }
2390 }
2391 
2392 /*!
2393  Opens the dialog to import data to the selected workbook, spreadsheet or matrix
2394 */
2395 void MainWin::importFileDialog(const QString& fileName) {
2396  DEBUG("MainWin::importFileDialog()");
2397  auto* dlg = new ImportFileDialog(this, false, fileName);
2398 
2399  // select existing container
2403  dlg->setCurrentIndex(m_projectExplorer->currentIndex());
2404  else if (m_currentAspect->type() == AspectType::Column &&
2407 
2408  if (dlg->exec() == QDialog::Accepted) {
2409  dlg->importTo(statusBar());
2410  m_project->setChanged(true);
2411  }
2412 
2413  delete dlg;
2414  DEBUG("MainWin::importFileDialog() DONE");
2415 }
2416 
2418  DEBUG("MainWin::importSqlDialog()");
2419  auto* dlg = new ImportSQLDatabaseDialog(this);
2420 
2421  // select existing container
2425  dlg->setCurrentIndex(m_projectExplorer->currentIndex());
2426  else if (m_currentAspect->type() == AspectType::Column &&
2429 
2430 
2431  if (dlg->exec() == QDialog::Accepted) {
2432  dlg->importTo(statusBar());
2433  m_project->setChanged(true);
2434  }
2435 
2436  delete dlg;
2437  DEBUG("MainWin::importSqlDialog() DONE");
2438 }
2439 
2441  DEBUG("MainWin::importProjectDialog()");
2442 
2444  if (QObject::sender() == m_importOpjAction)
2446  else
2448 
2449  auto* dlg = new ImportProjectDialog(this, type);
2450 
2451  // set current folder
2452  dlg->setCurrentFolder(m_currentFolder);
2453 
2454  if (dlg->exec() == QDialog::Accepted) {
2455  dlg->importTo(statusBar());
2456  m_project->setChanged(true);
2457  }
2458 
2459  delete dlg;
2460 
2461  DEBUG("MainWin::importProjectDialog() DONE");
2462 }
2463 
2464 /*!
2465  * \brief opens a dialog to import datasets
2466  */
2468  auto* dlg = new ImportDatasetDialog(this);
2469  if (dlg->exec() == QDialog::Accepted) {
2470  Spreadsheet* spreadsheet = new Spreadsheet(i18n("Dataset%1", 1));
2471  auto* dataset = new DatasetHandler(spreadsheet);
2472  dlg->importToDataset(dataset, statusBar());
2473 
2474  QTimer timer;
2475  timer.setSingleShot(true);
2476  QEventLoop loop;
2477  connect(dataset, &DatasetHandler::downloadCompleted, &loop, &QEventLoop::quit);
2478  connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
2479  timer.start(1500);
2480  loop.exec();
2481 
2482  if (timer.isActive()) {
2483  timer.stop();
2484  addAspectToProject(spreadsheet);
2485  }
2486  delete dataset;
2487  }
2488  delete dlg;
2489 }
2490 
2491 /*!
2492  opens the dialog for the export of the currently active worksheet, spreadsheet or matrix.
2493  */
2495  QMdiSubWindow* win = m_mdiArea->currentSubWindow();
2496  if (!win)
2497  return;
2498 
2499  AbstractPart* part = static_cast<PartMdiView*>(win)->part();
2500  if (part->exportView())
2501  statusBar()->showMessage(i18n("%1 exported", part->name()));
2502 }
2503 
2505  auto* editDialog = new FITSHeaderEditDialog(this);
2506  if (editDialog->exec() == QDialog::Accepted) {
2507  if (editDialog->saved())
2508  statusBar()->showMessage(i18n("FITS files saved"));
2509  }
2510 }
2511 
2512 /*!
2513  adds a new file data source to the current project.
2514 */
2516  auto* dlg = new ImportFileDialog(this, true);
2517  if (dlg->exec() == QDialog::Accepted) {
2518  if (dlg->sourceType() == LiveDataSource::SourceType::MQTT) {
2519 #ifdef HAVE_MQTT
2520  auto* mqttClient = new MQTTClient(i18n("MQTT Client%1", 1));
2521  dlg->importToMQTT(mqttClient);
2522 
2523  //doesn't make sense to have more MQTTClients connected to the same broker
2525  bool found = false;
2526  for (const auto* client : clients) {
2527  if (client->clientHostName() == mqttClient->clientHostName() && client->clientPort() == mqttClient->clientPort()) {
2528  found = true;
2529  break;
2530  }
2531  }
2532 
2533  if (!found) {
2534  mqttClient->setName(mqttClient->clientHostName());
2535  addAspectToProject(mqttClient);
2536  } else {
2537  delete mqttClient;
2538  QMessageBox::warning(this, "Warning", "There already is a MQTTClient with this host!");
2539  }
2540 #endif
2541  } else {
2542  auto* dataSource = new LiveDataSource(i18n("Live data source%1", 1), false);
2543  dlg->importToLiveDataSource(dataSource, statusBar());
2544  addAspectToProject(dataSource);
2545  }
2546  }
2547  delete dlg;
2548 }
2549 
2551  const QModelIndex& index = m_projectExplorer->currentIndex();
2552  if (index.isValid()) {
2553  auto* parent = static_cast<AbstractAspect*>(index.internalPointer());
2554 #ifdef HAVE_MQTT
2555  //doesn't make sense to add a new MQTTClient to an existing MQTTClient or to any of its successors
2556  QString className = parent->metaObject()->className();
2557  MQTTClient* clientAncestor = parent->ancestor<MQTTClient>();
2558  if (className == "MQTTClient")
2559  parent = parent->parentAspect();
2560  else if (clientAncestor != nullptr)
2561  parent = clientAncestor->parentAspect();
2562 #endif
2563  parent->folder()->addChild(aspect);
2564  } else
2565  m_project->addChild(aspect);
2566 }
2567 
2569  auto* dlg = new SettingsDialog(this);
2571 // connect (dlg, &SettingsDialog::resetWelcomeScreen, this, &MainWin::resetWelcomeScreen);
2572  dlg->exec();
2573 }
2574 
2575 #ifdef HAVE_CANTOR_LIBS
2576 void MainWin::cantorSettingsDialog() {
2577  static auto* emptyConfig = new KCoreConfigSkeleton();
2578  auto* cantorDialog = new KConfigDialog(this, QLatin1String("Cantor Settings"), emptyConfig);
2579  for (auto* backend : Cantor::Backend::availableBackends())
2580  if (backend->config()) //It has something to configure, so add it to the dialog
2581  cantorDialog->addPage(backend->settingsWidget(cantorDialog), backend->config(), backend->name(), backend->icon());
2582 
2583  cantorDialog->show();
2584 }
2585 #endif
Base class of all persistent objects in a Project.
AspectType type() const
void aspectAdded(const AbstractAspect *)
Emitted after a new Aspect has been added to the tree.
Folder * folder()
Return the folder the Aspect is contained in or 0 if there is none.
@ Recursive
Recursively handle all descendents, not just immediate children.
void addChild(AbstractAspect *)
Add the given Aspect to my list of children.
bool isDescendantOf(AbstractAspect *other)
Return whether the there is a path upwards to the given aspect.
AbstractAspect * parent(AspectType type) const
In the parent-child hierarchy, return the first parent of type.
void aspectAboutToBeRemoved(const AbstractAspect *)
Emitted before an aspect is removed from its parent.
T * ancestor() const
Return the closest ancestor of class T (or NULL if none found).
QString name() const
bool setName(const QString &, bool autoUnique=true)
AbstractAspect::setName sets the name of the abstract aspect.
void aspectRemoved(const AbstractAspect *parent, const AbstractAspect *before, const AbstractAspect *child)
Emitted from the parent after removing a child.
AbstractAspect * parentAspect() const
Return my parent Aspect or 0 if I currently don't have one.
bool isLoading() const
QVector< AbstractAspect * > children(AspectType type, ChildIndexFlags flags={}) const
Base class of Aspects with MDI windows as views (AspectParts).
Definition: AbstractPart.h:36
void printPreviewRequested()
PartMdiView * mdiSubWindow() const
Wrap the view() into a PartMdiView.
virtual bool exportView() const =0
void printRequested()
void importFromFileRequested()
void importFromSQLDatabaseRequested()
virtual bool printPreview() const =0
void exportRequested()
virtual bool printView()=0
void showRequested()
Simplified filter interface for filters with only one output port.
Represents a tree of AbstractAspect objects as a Qt item model.
void statusInfo(const QString &)
QModelIndex modelIndexOfAspect(const AbstractAspect *, int column=0) const
Convenience wrapper around QAbstractItemModel::createIndex().
virtual void updateUnits()
Definition: BaseDock.h:64
virtual void updateLocale()
Definition: BaseDock.h:63
QWidget * view() const override
Construct a primary view on me.
The CursorDock class This class represents the data from the cursors from the cartesian plots in a tr...
Definition: CursorDock.h:45
void setWorksheet(Worksheet *)
Definition: CursorDock.cpp:66
Top-level container for DatapickerCurve and DatapickerImage.
Definition: Datapicker.h:45
void childSelected(const AbstractAspect *) override
Definition: Datapicker.cpp:152
QWidget * view() const override
Construct a primary view on me.
Definition: Datapicker.cpp:88
Provides functionality to process a metadata file of a dataset, configure a spreadsheet and filter ba...
void downloadCompleted()
Dialog class for editing FITS header units.
Folder in a project.
Definition: Folder.h:35
Display the content of project's undo stack.
Definition: HistoryDialog.h:38
Dialog for importing data from a dataset. Embeds ImportDatasetWidget and provides the standard button...
Dialog for importing data from a file. Embeds ImportFileWidget and provides the standard buttons.
Dialog for importing project files.
Dialog for importing data from a SQL database. Embeds ImportSQLDatabaseWidget and provides the standa...
Widget for editing the properties of a TextLabel object, mostly used in an appropriate dock widget.
Definition: LabelWidget.h:48
void updateUnits()
The MQTT Client connects to the broker set in ImportFileWidget. It manages the MQTTSubscriptions,...
Definition: MQTTClient.h:48
Main application window.
Definition: MainWin.h:112
QDockWidget * cursorDock
Definition: MainWin.h:225
bool save(const QString &)
Definition: MainWin.cpp:1554
QAction * m_newProjectAction
Definition: MainWin.h:190
Project * project() const
Definition: MainWin.cpp:235
KRecentFilesAction * m_recentProjectsAction
Definition: MainWin.h:169
bool m_autoSaveActive
Definition: MainWin.h:147
void initGUI(const QString &)
Definition: MainWin.cpp:239
bool m_projectClosing
Definition: MainWin.h:146
bool saveProjectAs()
Definition: MainWin.cpp:1528
void updateGUI()
Definition: MainWin.cpp:930
void newMatrix()
Definition: MainWin.cpp:1749
QAction * m_togglePropertiesDockAction
Definition: MainWin.h:206
QAction * m_closeAllWindowsAction
Definition: MainWin.h:196
void openDatasetExample()
Definition: MainWin.cpp:2362
void toggleStatusBar()
Definition: MainWin.cpp:2103
QAction * m_printAction
Definition: MainWin.h:172
QMenu * m_visibilityMenu
Definition: MainWin.h:217
QAction * m_openProjectAction
Definition: MainWin.h:191
bool m_suppressCurrentSubWindowChangedEvent
Definition: MainWin.h:144
void newNotes()
Definition: MainWin.cpp:1778
QAction * m_toggleStatusBarAction
Definition: MainWin.h:207
QAction * m_newSpreadsheetAction
Definition: MainWin.h:183
void settingsDialog()
Definition: MainWin.cpp:2568
void updateGUIOnProjectChanges()
Definition: MainWin.cpp:852
KColorSchemeManager * m_schemeManager
Definition: MainWin.h:133
void closeEvent(QCloseEvent *) override
Definition: MainWin.cpp:2231
QAction * m_newNotesAction
Definition: MainWin.h:186
void handleCurrentAspectChanged(AbstractAspect *)
Definition: MainWin.cpp:1904
void toggleDockWidget(QAction *)
Definition: MainWin.cpp:2085
void toggleMemoryInfo()
Definition: MainWin.cpp:2112
void updateTitleBar()
Definition: MainWin.cpp:1641
QAction * m_newWorksheetAction
Definition: MainWin.h:185
Folder * m_currentFolder
Definition: MainWin.h:141
bool m_closing
Definition: MainWin.h:145
QStackedWidget * stackedWidget
Definition: MainWin.h:223
void print()
Definition: MainWin.cpp:1674
void updateMdiWindowVisibility() const
Definition: MainWin.cpp:2060
QDockWidget * m_projectExplorerDock
Definition: MainWin.h:138
QAction * m_importLabPlotAction
Definition: MainWin.h:177
void handleAspectAboutToBeRemoved(const AbstractAspect *)
Definition: MainWin.cpp:1883
void historyDialog()
Definition: MainWin.cpp:2373
QTimer m_autoSaveTimer
Definition: MainWin.h:148
QString m_undoViewEmptyLabel
Definition: MainWin.h:143
void toggleFullScreen(bool)
Definition: MainWin.cpp:2227
void importSqlDialog()
Definition: MainWin.cpp:2417
void initMenus()
Definition: MainWin.cpp:701
QAction * m_cascadeWindowsAction
Definition: MainWin.h:198
QAction * m_newWorkbookAction
Definition: MainWin.h:182
QAction * m_saveAction
Definition: MainWin.h:170
void cursorDockVisibilityChanged(bool)
Definition: MainWin.cpp:2194
QMenu * m_editMenu
Definition: MainWin.h:220
QAction * m_undoAction
Definition: MainWin.h:193
QAction * m_visibilityFolderAction
Definition: MainWin.h:212
void openProject()
Definition: MainWin.cpp:1234
QAction * m_saveAsAction
Definition: MainWin.h:171
TitleBarMode
Definition: MainWin.h:125
QMenu * m_importMenu
Definition: MainWin.h:219
bool warnModified()
Definition: MainWin.cpp:830
void handleAspectAdded(const AbstractAspect *)
Definition: MainWin.cpp:1850
QAction * m_historyAction
Definition: MainWin.h:192
void colorSchemeChanged(QAction *)
Definition: MainWin.cpp:811
QMenu * m_newMenu
Definition: MainWin.h:218
QAction * m_newMatrixAction
Definition: MainWin.h:184
QAction * m_newDatapickerAction
Definition: MainWin.h:201
void propertiesExplorerRequested()
Definition: MainWin.cpp:2123
void createContextMenu(QMenu *) const
Definition: MainWin.cpp:2001
Project * m_project
Definition: MainWin.h:135
void newDatapicker()
Definition: MainWin.cpp:1721
QAction * m_newFolderAction
Definition: MainWin.h:181
AspectTreeModel * model() const
Definition: MainWin.cpp:231
GuiObserver * m_guiObserver
Definition: MainWin.h:273
QAction * m_importFileAction
Definition: MainWin.h:174
void importDatasetDialog()
opens a dialog to import datasets
Definition: MainWin.cpp:2467
QAction * m_visibilityAllAction
Definition: MainWin.h:214
void activateSubWindowForAspect(const AbstractAspect *) const
Definition: MainWin.cpp:1923
void propertiesDockVisibilityChanged(bool)
Definition: MainWin.cpp:2190
QString m_lastOpenFileFilter
Definition: MainWin.h:156
QAction * m_visibilitySubfolderAction
Definition: MainWin.h:213
QAction * m_toggleProjectExplorerDockAction
Definition: MainWin.h:205
QAction * m_prevWindowAction
Definition: MainWin.h:200
Spreadsheet * activeSpreadsheet() const
Definition: MainWin.cpp:1788
friend class GuiObserver
Definition: MainWin.h:272
QAction * m_tileWindowsAction
Definition: MainWin.h:197
TitleBarMode m_titleBarMode
Definition: MainWin.h:157
void autoSaveProject()
Definition: MainWin.cpp:1632
void dropEvent(QDropEvent *) override
Definition: MainWin.cpp:2243
QAction * m_newLiveDataSourceAction
Definition: MainWin.h:187
bool m_showWelcomeScreen
Definition: MainWin.h:149
QAction * m_closeWindowAction
Definition: MainWin.h:195
QMdiSubWindow * m_currentSubWindow
Definition: MainWin.h:134
AbstractAspect * m_currentAspect
Definition: MainWin.h:140
void openRecentProject(const QUrl &)
Definition: MainWin.cpp:1453
QAction * m_nextWindowAction
Definition: MainWin.h:199
void createMdiArea()
Creates a new welcome screen to be set as central widget.
Definition: MainWin.cpp:448
void createFolderContextMenu(const Folder *, QMenu *) const
Definition: MainWin.cpp:2023
void cartesianPlotMouseModeChanged(CartesianPlot::MouseMode)
Definition: MainWin.cpp:2202
void undo()
Definition: MainWin.cpp:2031
QAction * m_importDatasetAction
Definition: MainWin.h:176
ProjectDock * projectDock
Definition: MainWin.h:234
QString m_currentFileName
Definition: MainWin.h:142
ProjectExplorer * m_projectExplorer
Definition: MainWin.h:137
void initActions()
Definition: MainWin.cpp:482
void newLiveDataSourceActionTriggered()
Definition: MainWin.cpp:2515
bool newProject()
Definition: MainWin.cpp:1131
void projectExplorerDockVisibilityChanged(bool)
Definition: MainWin.cpp:2186
void importProjectDialog()
Definition: MainWin.cpp:2440
QAction * m_toggleMemoryInfoAction
Definition: MainWin.h:208
void newFolder()
Definition: MainWin.cpp:1705
KToggleFullScreenAction * m_toggleFullScreenAction
Definition: MainWin.h:209
void addAspectToProject(AbstractAspect *)
Definition: MainWin.cpp:2550
QAction * m_importOpjAction
Definition: MainWin.h:178
LoadOnStart
Definition: MainWin.h:124
void handleAspectRemoved(const AbstractAspect *, const AbstractAspect *, const AbstractAspect *)
Definition: MainWin.cpp:1870
QAction * m_exportAction
Definition: MainWin.h:179
MainWin(QWidget *parent=nullptr, const QString &filename=nullptr)
Definition: MainWin.cpp:151
void showPresenter()
Definition: MainWin.cpp:212
~MainWin() override
Definition: MainWin.cpp:179
QDockWidget * m_propertiesDock
Definition: MainWin.h:139
void redo()
Definition: MainWin.cpp:2046
CursorDock * cursorWidget
Definition: MainWin.h:226
QAction * m_closeAction
Definition: MainWin.h:180
void handleCurrentSubWindowChanged(QMdiSubWindow *)
Definition: MainWin.cpp:1830
void newWorkbook()
Definition: MainWin.cpp:1713
QAction * m_editFitsFileAction
Definition: MainWin.h:202
QMdiArea * m_mdiArea
Definition: MainWin.h:132
void newWorksheet()
Definition: MainWin.cpp:1770
void newSpreadsheet()
Definition: MainWin.cpp:1728
AspectTreeModel * m_aspectTreeModel
Definition: MainWin.h:136
void exportDialog()
Definition: MainWin.cpp:2494
void editFitsFileDialog()
Definition: MainWin.cpp:2504
QAction * m_importSqlAction
Definition: MainWin.h:175
void projectChanged()
Definition: MainWin.cpp:1824
void importFileDialog(const QString &fileName=QString())
Definition: MainWin.cpp:2395
void handleShowSubWindowRequested()
Definition: MainWin.cpp:1993
MemoryWidget * m_memoryInfoWidget
Definition: MainWin.h:151
void handleSettingsChanges()
Definition: MainWin.cpp:2274
void dragEnterEvent(QDragEnterEvent *) override
Definition: MainWin.cpp:2239
bool closeProject()
Definition: MainWin.cpp:1468
void setMdiWindowVisibility(QAction *)
Definition: MainWin.cpp:1984
bool saveProject()
Definition: MainWin.cpp:1520
QAction * m_printPreviewAction
Definition: MainWin.h:173
QAction * m_redoAction
Definition: MainWin.h:194
void printPreview()
Definition: MainWin.cpp:1687
Definition: Matrix.h:41
QWidget * view() const override
Construct a primary view on me.
Definition: Matrix.cpp:120
Definition: Note.h:41
parser for Origin projects.
static bool isOriginProject(const QString &fileName)
static QString supportedExtensions()
QMdiSubWindow wrapper for aspect views.
Definition: PartMdiView.h:38
AbstractPart * part() const
Definition: PartMdiView.cpp:63
void setProject(Project *)
Definition: ProjectDock.cpp:62
A tree view for displaying and editing an AspectTreeModel.
void activateView(AbstractAspect *)
void currentAspectChanged(AbstractAspect *)
void setProject(Project *)
void setCurrentAspect(const AbstractAspect *)
QModelIndex currentIndex() const
void setModel(AspectTreeModel *)
Represents a project.
Definition: Project.h:42
MdiWindowVisibility
MDI subwindow visibility setting.
Definition: Project.h:46
@ folderOnly
only show MDI windows corresponding to Parts in the current folder
@ allMdiWindows
show MDI windows for all Parts in the project simultaneously
void setMdiWindowVisibility(MdiWindowVisibility visibility)
Definition: Project.cpp:173
void changed()
MdiWindowVisibility mdiWindowVisibility() const
Definition: Project.cpp:178
QUndoStack * undoStack() const override
Return the undo stack of the Project, or 0 if this Aspect is not part of a Project.
Definition: Project.cpp:150
static QString supportedExtensions()
Definition: Project.cpp:355
void mdiWindowVisibilityChanged()
bool hasChanged() const
Definition: Project.cpp:211
void requestFolderContextMenu(const Folder *, QMenu *)
void setChanged(const bool value=true)
Definition: Project.cpp:193
bool load(XmlStreamReader *, bool preview) override
Load from XML.
Definition: Project.cpp:505
void closeRequested()
void setSuppressAspectAddedSignal(bool)
Definition: Project.cpp:203
void save(const QPixmap &, QXmlStreamWriter *) const
Definition: Project.cpp:379
static bool isLabPlotProject(const QString &fileName)
Definition: Project.cpp:348
void requestProjectContextMenu(QMenu *)
void settingsChanged()
Aspect providing a spreadsheet table with column logic.
Definition: Spreadsheet.h:40
void updateHorizontalHeader()
Top-level container for Spreadsheet and Matrix.
Definition: Workbook.h:40
Worksheet view.
Definition: WorksheetView.h:51
Top-level container for worksheet elements like plot, labels, etc.
Definition: Worksheet.h:46
QWidget * view() const override
Construct a primary view on me.
Definition: Worksheet.cpp:165
void cartesianPlotMouseModeChanged(CartesianPlot::MouseMode)
void propertiesExplorerRequested()
#define WAIT_CURSOR
Definition: macros.h:63
#define STDSTRING(qstr)
Definition: macros.h:67
#define RESET_CURSOR
Definition: macros.h:64
#define DEBUG(x)
Definition: macros.h:50
@ UserRole
Definition: qxtnamespace.h:91
@ NoError
Definition: qxtnamespace.h:64
#define i18n(m)
Definition: nsl_common.h:38