diff --git a/src/commonfrontend/worksheet/WorksheetView.cpp b/src/commonfrontend/worksheet/WorksheetView.cpp index 78ed6509f..ec1d364a5 100644 --- a/src/commonfrontend/worksheet/WorksheetView.cpp +++ b/src/commonfrontend/worksheet/WorksheetView.cpp @@ -1,2057 +1,2065 @@ /*************************************************************************** File : WorksheetView.cpp Project : LabPlot Description : Worksheet view -------------------------------------------------------------------- Copyright : (C) 2009-2019 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2016-2018 Stefan-Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "commonfrontend/worksheet/WorksheetView.h" #include "backend/core/AbstractColumn.h" #include "backend/worksheet/plots/cartesian/Axis.h" #include "backend/worksheet/plots/cartesian/XYCurve.h" #include "backend/worksheet/plots/cartesian/XYCurvePrivate.h" #include "backend/worksheet/Image.h" #include "backend/worksheet/TextLabel.h" #include "commonfrontend/core/PartMdiView.h" #include "kdefrontend/widgets/ThemesWidget.h" #include "kdefrontend/worksheet/GridDialog.h" #include "kdefrontend/worksheet/PresenterWidget.h" #include "kdefrontend/worksheet/DynamicPresenterWidget.h" #include "backend/lib/trace.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef Q_OS_MAC #include "3rdparty/kdmactouchbar/src/kdmactouchbar.h" #endif #include /** * \class WorksheetView * \brief Worksheet view */ /*! Constructur of the class. Creates a view for the Worksheet \c worksheet and initializes the internal model. */ WorksheetView::WorksheetView(Worksheet* worksheet) : QGraphicsView(), m_worksheet(worksheet) { setScene(m_worksheet->scene()); setRenderHint(QPainter::Antialiasing); setRubberBandSelectionMode(Qt::ContainsItemBoundingRect); setTransformationAnchor(QGraphicsView::AnchorViewCenter); setResizeAnchor(QGraphicsView::AnchorViewCenter); setMinimumSize(16, 16); setFocusPolicy(Qt::StrongFocus); if (m_worksheet->useViewSize()) { setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } viewport()->setAttribute( Qt::WA_OpaquePaintEvent ); viewport()->setAttribute( Qt::WA_NoSystemBackground ); setAcceptDrops(true); setCacheMode(QGraphicsView::CacheBackground); m_gridSettings.style = WorksheetView::NoGrid; //signal/slot connections connect(m_worksheet, &Worksheet::requestProjectContextMenu, this, &WorksheetView::createContextMenu); connect(m_worksheet, &Worksheet::itemSelected, this, &WorksheetView::selectItem); connect(m_worksheet, &Worksheet::itemDeselected, this, &WorksheetView::deselectItem); connect(m_worksheet, &Worksheet::requestUpdate, this, &WorksheetView::updateBackground); connect(m_worksheet, &Worksheet::aspectAboutToBeRemoved, this, &WorksheetView::aspectAboutToBeRemoved); connect(m_worksheet, &Worksheet::useViewSizeRequested, this, &WorksheetView::useViewSizeRequested); connect(m_worksheet, &Worksheet::layoutChanged, this, &WorksheetView::layoutChanged); connect(scene(), &QGraphicsScene::selectionChanged, this, &WorksheetView::selectionChanged); //resize the view to make the complete scene visible. //no need to resize the view when the project is being opened, //all views will be resized to the stored values at the end if (!m_worksheet->isLoading()) { float w = Worksheet::convertFromSceneUnits(sceneRect().width(), Worksheet::Inch); float h = Worksheet::convertFromSceneUnits(sceneRect().height(), Worksheet::Inch); w *= QApplication::desktop()->physicalDpiX(); h *= QApplication::desktop()->physicalDpiY(); resize(w*1.1, h*1.1); } //rescale to the original size static const float hscale = QApplication::desktop()->physicalDpiX()/(Worksheet::convertToSceneUnits(1,Worksheet::Inch)); static const float vscale = QApplication::desktop()->physicalDpiY()/(Worksheet::convertToSceneUnits(1,Worksheet::Inch)); setTransform(QTransform::fromScale(hscale, vscale)); initBasicActions(); } /*! * initializes couple of actions that have shortcuts assigned in the constructor as opposed * to other actions in initAction() that are create on demand only if the context menu is requested */ void WorksheetView::initBasicActions() { selectAllAction = new QAction(QIcon::fromTheme("edit-select-all"), i18n("Select All"), this); this->addAction(selectAllAction); connect(selectAllAction, &QAction::triggered, this, &WorksheetView::selectAllElements); deleteAction = new QAction(QIcon::fromTheme("edit-delete"), i18n("Delete"), this); this->addAction(deleteAction); connect(deleteAction, &QAction::triggered, this, &WorksheetView::deleteElement); backspaceAction = new QAction(this); this->addAction(backspaceAction); connect(backspaceAction, &QAction::triggered, this, &WorksheetView::deleteElement); //Zoom actions zoomInViewAction = new QAction(QIcon::fromTheme("zoom-in"), i18n("Zoom In"), this); zoomOutViewAction = new QAction(QIcon::fromTheme("zoom-out"), i18n("Zoom Out"), this); zoomOriginAction = new QAction(QIcon::fromTheme("zoom-original"), i18n("Original Size"), this); } void WorksheetView::initActions() { auto* addNewActionGroup = new QActionGroup(this); auto* zoomActionGroup = new QActionGroup(this); auto* mouseModeActionGroup = new QActionGroup(this); auto* layoutActionGroup = new QActionGroup(this); auto* gridActionGroup = new QActionGroup(this); gridActionGroup->setExclusive(true); auto* magnificationActionGroup = new QActionGroup(this); zoomActionGroup->addAction(zoomInViewAction); zoomActionGroup->addAction(zoomOutViewAction); zoomActionGroup->addAction(zoomOriginAction); zoomFitPageHeightAction = new QAction(QIcon::fromTheme("zoom-fit-height"), i18n("Fit to Height"), zoomActionGroup); zoomFitPageWidthAction = new QAction(QIcon::fromTheme("zoom-fit-width"), i18n("Fit to Width"), zoomActionGroup); zoomFitSelectionAction = new QAction(i18n("Fit to Selection"), zoomActionGroup); // Mouse mode actions selectionModeAction = new QAction(QIcon::fromTheme("labplot-cursor-arrow"), i18n("Select and Edit"), mouseModeActionGroup); selectionModeAction->setCheckable(true); navigationModeAction = new QAction(QIcon::fromTheme("input-mouse"), i18n("Navigate"), mouseModeActionGroup); navigationModeAction->setCheckable(true); zoomSelectionModeAction = new QAction(QIcon::fromTheme("page-zoom"), i18n("Select and Zoom"), mouseModeActionGroup); zoomSelectionModeAction->setCheckable(true); //Magnification actions noMagnificationAction = new QAction(QIcon::fromTheme("labplot-1x-zoom"), i18n("No Magnification"), magnificationActionGroup); noMagnificationAction->setCheckable(true); noMagnificationAction->setChecked(true); twoTimesMagnificationAction = new QAction(QIcon::fromTheme("labplot-2x-zoom"), i18n("2x Magnification"), magnificationActionGroup); twoTimesMagnificationAction->setCheckable(true); threeTimesMagnificationAction = new QAction(QIcon::fromTheme("labplot-3x-zoom"), i18n("3x Magnification"), magnificationActionGroup); threeTimesMagnificationAction->setCheckable(true); fourTimesMagnificationAction = new QAction(QIcon::fromTheme("labplot-4x-zoom"), i18n("4x Magnification"), magnificationActionGroup); fourTimesMagnificationAction->setCheckable(true); fiveTimesMagnificationAction = new QAction(QIcon::fromTheme("labplot-5x-zoom"), i18n("5x Magnification"), magnificationActionGroup); fiveTimesMagnificationAction->setCheckable(true); //TODO implement later "group selection action" where multiple objects can be selected by drawing a rectangular // selectionModeAction = new QAction(QIcon::fromTheme("select-rectangular"), i18n("Selection"), mouseModeActionGroup); // selectionModeAction->setCheckable(true); //"Add new" related actions addCartesianPlot1Action = new QAction(QIcon::fromTheme("labplot-xy-plot-four-axes"), i18n("Box Plot, Four Axes"), addNewActionGroup); addCartesianPlot2Action = new QAction(QIcon::fromTheme("labplot-xy-plot-two-axes"), i18n("Box Plot, Two Axes"), addNewActionGroup); addCartesianPlot3Action = new QAction(QIcon::fromTheme("labplot-xy-plot-two-axes-centered"), i18n("Two Axes, Centered"), addNewActionGroup); addCartesianPlot4Action = new QAction(QIcon::fromTheme("labplot-xy-plot-two-axes-centered-origin"), i18n("Two Axes, Crossing at Origin"), addNewActionGroup); addTextLabelAction = new QAction(QIcon::fromTheme("draw-text"), i18n("Text Label"), addNewActionGroup); addImageAction = new QAction(QIcon::fromTheme("viewimage"), i18n("Image"), addNewActionGroup); //Layout actions //TODO: the icons labplot-editvlayout and labplot-edithlayout are confusing for the user. //the orientation is visualized as a horizontal or vertical line on the icon, but the user //percieves the two objects (resembles plots on the worksheet) separated by this line much stronger than the line itself. //with this, the two objects separated by a vertical line are percieved to be layed out in a _horizontal_ order and the //same for the vertical line. Because of this we change the icons here. We can rename the icons later in the breeze icon set. verticalLayoutAction = new QAction(QIcon::fromTheme("labplot-edithlayout"), i18n("Vertical Layout"), layoutActionGroup); verticalLayoutAction->setCheckable(true); horizontalLayoutAction = new QAction(QIcon::fromTheme("labplot-editvlayout"), i18n("Horizontal Layout"), layoutActionGroup); horizontalLayoutAction->setCheckable(true); gridLayoutAction = new QAction(QIcon::fromTheme("labplot-editgrid"), i18n("Grid Layout"), layoutActionGroup); gridLayoutAction->setCheckable(true); breakLayoutAction = new QAction(QIcon::fromTheme("labplot-editbreaklayout"), i18n("Break Layout"), layoutActionGroup); breakLayoutAction->setEnabled(false); //Grid actions noGridAction = new QAction(i18n("No Grid"), gridActionGroup); noGridAction->setCheckable(true); noGridAction->setChecked(true); noGridAction->setData(WorksheetView::NoGrid); denseLineGridAction = new QAction(i18n("Dense Line Grid"), gridActionGroup); denseLineGridAction->setCheckable(true); sparseLineGridAction = new QAction(i18n("Sparse Line Grid"), gridActionGroup); sparseLineGridAction->setCheckable(true); denseDotGridAction = new QAction(i18n("Dense Dot Grid"), gridActionGroup); denseDotGridAction->setCheckable(true); sparseDotGridAction = new QAction(i18n("Sparse Dot Grid"), gridActionGroup); sparseDotGridAction->setCheckable(true); customGridAction = new QAction(i18n("Custom Grid"), gridActionGroup); customGridAction->setCheckable(true); snapToGridAction = new QAction(i18n("Snap to Grid"), this); snapToGridAction->setCheckable(true); showPresenterMode = new QAction(QIcon::fromTheme("view-fullscreen"), i18n("Show in Presenter Mode"), this); //check the action corresponding to the currently active layout in worksheet this->layoutChanged(m_worksheet->layout()); connect(addNewActionGroup, &QActionGroup::triggered, this, &WorksheetView::addNew); connect(mouseModeActionGroup, &QActionGroup::triggered, this, &WorksheetView::mouseModeChanged); connect(zoomActionGroup, &QActionGroup::triggered, this, &WorksheetView::changeZoom); connect(magnificationActionGroup, &QActionGroup::triggered, this, &WorksheetView::magnificationChanged); connect(layoutActionGroup, &QActionGroup::triggered, this, &WorksheetView::changeLayout); connect(gridActionGroup, &QActionGroup::triggered, this, &WorksheetView::changeGrid); connect(snapToGridAction, &QAction::triggered, this, &WorksheetView::changeSnapToGrid); connect(showPresenterMode, &QAction::triggered, this, &WorksheetView::presenterMode); //worksheet control actions plotsLockedAction = new QAction(i18n("Non-interactive Plots"), this); plotsLockedAction->setToolTip(i18n("If activated, plots on the worksheet don't react on drag and mouse wheel events.")); plotsLockedAction->setCheckable(true); plotsLockedAction->setChecked(m_worksheet->plotsLocked()); connect(plotsLockedAction, &QAction::triggered, this, &WorksheetView::plotsLockedActionChanged); //action for cartesian plots auto* cartesianPlotActionModeActionGroup = new QActionGroup(this); cartesianPlotActionModeActionGroup->setExclusive(true); cartesianPlotApplyToSelectionAction = new QAction(i18n("Selected Plots"), cartesianPlotActionModeActionGroup); cartesianPlotApplyToSelectionAction->setCheckable(true); cartesianPlotApplyToAllAction = new QAction(i18n("All Plots"), cartesianPlotActionModeActionGroup); cartesianPlotApplyToAllAction->setCheckable(true); setCartesianPlotActionMode(m_worksheet->cartesianPlotActionMode()); connect(cartesianPlotActionModeActionGroup, &QActionGroup::triggered, this, &WorksheetView::cartesianPlotActionModeChanged); // cursor apply to all/selected auto* cartesianPlotActionCursorGroup = new QActionGroup(this); cartesianPlotActionCursorGroup->setExclusive(true); cartesianPlotApplyToSelectionCursor = new QAction(i18n("Selected Plots"), cartesianPlotActionCursorGroup); cartesianPlotApplyToSelectionCursor->setCheckable(true); cartesianPlotApplyToAllCursor = new QAction(i18n("All Plots"), cartesianPlotActionCursorGroup); cartesianPlotApplyToAllCursor->setCheckable(true); setCartesianPlotCursorMode(m_worksheet->cartesianPlotCursorMode()); connect(cartesianPlotActionCursorGroup, &QActionGroup::triggered, this, &WorksheetView::cartesianPlotCursorModeChanged); auto* cartesianPlotMouseModeActionGroup = new QActionGroup(this); cartesianPlotMouseModeActionGroup->setExclusive(true); cartesianPlotSelectionModeAction = new QAction(QIcon::fromTheme("labplot-cursor-arrow"), i18n("Select and Edit"), cartesianPlotMouseModeActionGroup); cartesianPlotSelectionModeAction->setCheckable(true); cartesianPlotSelectionModeAction->setChecked(true); cartesianPlotZoomSelectionModeAction = new QAction(QIcon::fromTheme("labplot-zoom-select"), i18n("Select Region and Zoom In"), cartesianPlotMouseModeActionGroup); cartesianPlotZoomSelectionModeAction->setCheckable(true); cartesianPlotZoomXSelectionModeAction = new QAction(QIcon::fromTheme("labplot-zoom-select-x"), i18n("Select x-region and Zoom In"), cartesianPlotMouseModeActionGroup); cartesianPlotZoomXSelectionModeAction->setCheckable(true); cartesianPlotZoomYSelectionModeAction = new QAction(QIcon::fromTheme("labplot-zoom-select-y"), i18n("Select y-region and Zoom In"), cartesianPlotMouseModeActionGroup); cartesianPlotZoomYSelectionModeAction->setCheckable(true); // TODO: change ICON cartesianPlotCursorModeAction = new QAction(QIcon::fromTheme("debug-execute-from-cursor"), i18n("Cursor"), cartesianPlotMouseModeActionGroup); cartesianPlotCursorModeAction->setCheckable(true); connect(cartesianPlotMouseModeActionGroup, &QActionGroup::triggered, this, &WorksheetView::cartesianPlotMouseModeChanged); auto* cartesianPlotAddNewActionGroup = new QActionGroup(this); addCurveAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("xy-curve"), cartesianPlotAddNewActionGroup); addHistogramAction = new QAction(QIcon::fromTheme("view-object-histogram-linear"), i18n("Histogram"), cartesianPlotAddNewActionGroup); addEquationCurveAction = new QAction(QIcon::fromTheme("labplot-xy-equation-curve"), i18n("xy-curve from a mathematical Equation"), cartesianPlotAddNewActionGroup); // TODO: no own icons yet addDataOperationCurveAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("Data Operation"), cartesianPlotAddNewActionGroup); // addDataOperationCurveAction = new QAction(QIcon::fromTheme("labplot-xy-data-operation-curve"), i18n("Data Operation"), cartesianPlotAddNewActionGroup); addDataReductionCurveAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("Data Reduction"), cartesianPlotAddNewActionGroup); // addDataReductionCurveAction = new QAction(QIcon::fromTheme("labplot-xy-data-reduction-curve"), i18n("Data Reduction"), cartesianPlotAddNewActionGroup); addDifferentiationCurveAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("Differentiation"), cartesianPlotAddNewActionGroup); // addDifferentiationCurveAction = new QAction(QIcon::fromTheme("labplot-xy-differentiation-curve"), i18n("Differentiation"), cartesianPlotAddNewActionGroup); addIntegrationCurveAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("Integration"), cartesianPlotAddNewActionGroup); // addIntegrationCurveAction = new QAction(QIcon::fromTheme("labplot-xy-integration-curve"), i18n("Integration"), cartesianPlotAddNewActionGroup); addConvolutionCurveAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("(De-)Convolution"), cartesianPlotAddNewActionGroup); // addConvolutionCurveAction = new QAction(QIcon::fromTheme("labplot-xy-convolution-curve"), i18n("(De-)Convolution"), cartesianPlotAddNewActionGroup); addCorrelationCurveAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("Auto-/Cross-Correlation"), cartesianPlotAddNewActionGroup); // addCorrelationCurveAction = new QAction(QIcon::fromTheme("labplot-xy-convolution-curve"), i18n("Auto-/Cross-Correlation"), cartesianPlotAddNewActionGroup); addInterpolationCurveAction = new QAction(QIcon::fromTheme("labplot-xy-interpolation-curve"), i18n("Interpolation"), cartesianPlotAddNewActionGroup); addSmoothCurveAction = new QAction(QIcon::fromTheme("labplot-xy-smoothing-curve"), i18n("Smooth"), cartesianPlotAddNewActionGroup); addFitCurveAction = new QAction(QIcon::fromTheme("labplot-xy-fit-curve"), i18n("Fit"), cartesianPlotAddNewActionGroup); addFourierFilterCurveAction = new QAction(QIcon::fromTheme("labplot-xy-fourier-filter-curve"), i18n("Fourier Filter"), cartesianPlotAddNewActionGroup); addFourierTransformCurveAction = new QAction(QIcon::fromTheme("labplot-xy-fourier-transform-curve"), i18n("Fourier Transform"), cartesianPlotAddNewActionGroup); addLegendAction = new QAction(QIcon::fromTheme("text-field"), i18n("Legend"), cartesianPlotAddNewActionGroup); addHorizontalAxisAction = new QAction(QIcon::fromTheme("labplot-axis-horizontal"), i18n("Horizontal Axis"), cartesianPlotAddNewActionGroup); addVerticalAxisAction = new QAction(QIcon::fromTheme("labplot-axis-vertical"), i18n("Vertical Axis"), cartesianPlotAddNewActionGroup); addPlotTextLabelAction = new QAction(QIcon::fromTheme("draw-text"), i18n("Text Label"), cartesianPlotAddNewActionGroup); addPlotImageAction = new QAction(QIcon::fromTheme("viewimage"), i18n("Image"), cartesianPlotAddNewActionGroup); addCustomPointAction = new QAction(QIcon::fromTheme("draw-cross"), i18n("Custom Point"), cartesianPlotAddNewActionGroup); // Analysis menu // TODO: no own icons yet addDataOperationAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("Data Operation"), cartesianPlotAddNewActionGroup); // addDataOperationAction = new QAction(QIcon::fromTheme("labplot-xy-data-operation-curve"), i18n("Data Operation"), cartesianPlotAddNewActionGroup); addDataReductionAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("Data Reduction"), cartesianPlotAddNewActionGroup); // addDataReductionAction = new QAction(QIcon::fromTheme("labplot-xy-data-reduction-curve"), i18n("Data Reduction"), cartesianPlotAddNewActionGroup); addDifferentiationAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("Differentiation"), cartesianPlotAddNewActionGroup); // addDifferentiationAction = new QAction(QIcon::fromTheme("labplot-xy-differentiation-curve"), i18n("Differentiation"), cartesianPlotAddNewActionGroup); addIntegrationAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("Integration"), cartesianPlotAddNewActionGroup); // addIntegrationAction = new QAction(QIcon::fromTheme("labplot-xy-integration-curve"), i18n("Integration"), cartesianPlotAddNewActionGroup); addConvolutionAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("Convolution/Deconvolution"), cartesianPlotAddNewActionGroup); // addConvolutionAction = new QAction(QIcon::fromTheme("labplot-xy-convolution-curve"), i18n("Convolution/Deconvolution"), cartesianPlotAddNewActionGroup); addCorrelationAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("Auto-/Cross-Correlation"), cartesianPlotAddNewActionGroup); // addCorrelationAction = new QAction(QIcon::fromTheme("labplot-xy-correlation-curve"), i18n("Auto-/Cross-Correlation"), cartesianPlotAddNewActionGroup); addInterpolationAction = new QAction(QIcon::fromTheme("labplot-xy-interpolation-curve"), i18n("Interpolation"), cartesianPlotAddNewActionGroup); addSmoothAction = new QAction(QIcon::fromTheme("labplot-xy-smoothing-curve"), i18n("Smooth"), cartesianPlotAddNewActionGroup); addFitAction = new QAction(QIcon::fromTheme("labplot-xy-fit-curve"), i18n("Fit"), cartesianPlotAddNewActionGroup); addFourierFilterAction = new QAction(QIcon::fromTheme("labplot-xy-fourier-filter-curve"), i18n("Fourier Filter"), cartesianPlotAddNewActionGroup); addFourierTransformAction = new QAction(QIcon::fromTheme("labplot-xy-fourier-transform-curve"), i18n("Fourier Transform"), cartesianPlotAddNewActionGroup); connect(cartesianPlotAddNewActionGroup, &QActionGroup::triggered, this, &WorksheetView::cartesianPlotAddNew); auto* cartesianPlotNavigationGroup = new QActionGroup(this); scaleAutoAction = new QAction(QIcon::fromTheme("labplot-auto-scale-all"), i18n("Auto Scale"), cartesianPlotNavigationGroup); scaleAutoAction->setData(CartesianPlot::ScaleAuto); scaleAutoXAction = new QAction(QIcon::fromTheme("labplot-auto-scale-x"), i18n("Auto Scale X"), cartesianPlotNavigationGroup); scaleAutoXAction->setData(CartesianPlot::ScaleAutoX); scaleAutoYAction = new QAction(QIcon::fromTheme("labplot-auto-scale-y"), i18n("Auto Scale Y"), cartesianPlotNavigationGroup); scaleAutoYAction->setData(CartesianPlot::ScaleAutoY); zoomInAction = new QAction(QIcon::fromTheme("zoom-in"), i18n("Zoom In"), cartesianPlotNavigationGroup); zoomInAction->setData(CartesianPlot::ZoomIn); zoomOutAction = new QAction(QIcon::fromTheme("zoom-out"), i18n("Zoom Out"), cartesianPlotNavigationGroup); zoomOutAction->setData(CartesianPlot::ZoomOut); zoomInXAction = new QAction(QIcon::fromTheme("labplot-zoom-in-x"), i18n("Zoom In X"), cartesianPlotNavigationGroup); zoomInXAction->setData(CartesianPlot::ZoomInX); zoomOutXAction = new QAction(QIcon::fromTheme("labplot-zoom-out-x"), i18n("Zoom Out X"), cartesianPlotNavigationGroup); zoomOutXAction->setData(CartesianPlot::ZoomOutX); zoomInYAction = new QAction(QIcon::fromTheme("labplot-zoom-in-y"), i18n("Zoom In Y"), cartesianPlotNavigationGroup); zoomInYAction->setData(CartesianPlot::ZoomInY); zoomOutYAction = new QAction(QIcon::fromTheme("labplot-zoom-out-y"), i18n("Zoom Out Y"), cartesianPlotNavigationGroup); zoomOutYAction->setData(CartesianPlot::ZoomOutY); shiftLeftXAction = new QAction(QIcon::fromTheme("labplot-shift-left-x"), i18n("Shift Left X"), cartesianPlotNavigationGroup); shiftLeftXAction->setData(CartesianPlot::ShiftLeftX); shiftRightXAction = new QAction(QIcon::fromTheme("labplot-shift-right-x"), i18n("Shift Right X"), cartesianPlotNavigationGroup); shiftRightXAction->setData(CartesianPlot::ShiftRightX); shiftUpYAction = new QAction(QIcon::fromTheme("labplot-shift-up-y"), i18n("Shift Up Y"), cartesianPlotNavigationGroup); shiftUpYAction->setData(CartesianPlot::ShiftUpY); shiftDownYAction = new QAction(QIcon::fromTheme("labplot-shift-down-y"), i18n("Shift Down Y"), cartesianPlotNavigationGroup); shiftDownYAction->setData(CartesianPlot::ShiftDownY); connect(cartesianPlotNavigationGroup, &QActionGroup::triggered, this, &WorksheetView::cartesianPlotNavigationChanged); //set some default values selectionModeAction->setChecked(true); handleCartesianPlotActions(); currentZoomAction = zoomInViewAction; currentMagnificationAction = noMagnificationAction; m_actionsInitialized = true; } void WorksheetView::initMenus() { if (!m_actionsInitialized) initActions(); m_addNewCartesianPlotMenu = new QMenu(i18n("xy-plot"), this); m_addNewCartesianPlotMenu->addAction(addCartesianPlot1Action); m_addNewCartesianPlotMenu->addAction(addCartesianPlot2Action); m_addNewCartesianPlotMenu->addAction(addCartesianPlot3Action); m_addNewCartesianPlotMenu->addAction(addCartesianPlot4Action); m_addNewMenu = new QMenu(i18n("Add New"), this); m_addNewMenu->setIcon(QIcon::fromTheme("list-add")); m_addNewMenu->addMenu(m_addNewCartesianPlotMenu)->setIcon(QIcon::fromTheme("office-chart-line")); m_addNewMenu->addSeparator(); m_addNewMenu->addAction(addTextLabelAction); m_addNewMenu->addAction(addImageAction); m_viewMouseModeMenu = new QMenu(i18n("Mouse Mode"), this); m_viewMouseModeMenu->setIcon(QIcon::fromTheme("input-mouse")); m_viewMouseModeMenu->addAction(selectionModeAction); m_viewMouseModeMenu->addAction(navigationModeAction); m_viewMouseModeMenu->addAction(zoomSelectionModeAction); m_zoomMenu = new QMenu(i18n("Zoom"), this); m_zoomMenu->setIcon(QIcon::fromTheme("zoom-draw")); m_zoomMenu->addAction(zoomInViewAction); m_zoomMenu->addAction(zoomOutViewAction); m_zoomMenu->addAction(zoomOriginAction); m_zoomMenu->addAction(zoomFitPageHeightAction); m_zoomMenu->addAction(zoomFitPageWidthAction); m_zoomMenu->addAction(zoomFitSelectionAction); m_magnificationMenu = new QMenu(i18n("Magnification"), this); m_magnificationMenu->setIcon(QIcon::fromTheme("zoom-in")); m_magnificationMenu->addAction(noMagnificationAction); m_magnificationMenu->addAction(twoTimesMagnificationAction); m_magnificationMenu->addAction(threeTimesMagnificationAction); m_magnificationMenu->addAction(fourTimesMagnificationAction); m_magnificationMenu->addAction(fiveTimesMagnificationAction); m_layoutMenu = new QMenu(i18n("Layout"), this); m_layoutMenu->setIcon(QIcon::fromTheme("labplot-editbreaklayout")); m_layoutMenu->addAction(verticalLayoutAction); m_layoutMenu->addAction(horizontalLayoutAction); m_layoutMenu->addAction(gridLayoutAction); m_layoutMenu->addSeparator(); m_layoutMenu->addAction(breakLayoutAction); m_gridMenu = new QMenu(i18n("Grid"), this); m_gridMenu->setIcon(QIcon::fromTheme("view-grid")); m_gridMenu->addAction(noGridAction); m_gridMenu->addSeparator(); m_gridMenu->addAction(sparseLineGridAction); m_gridMenu->addAction(denseLineGridAction); m_gridMenu->addSeparator(); m_gridMenu->addAction(sparseDotGridAction); m_gridMenu->addAction(denseDotGridAction); m_gridMenu->addSeparator(); m_gridMenu->addAction(customGridAction); //TODO: implement "snap to grid" and activate this action // m_gridMenu->addSeparator(); // m_gridMenu->addAction(snapToGridAction); m_cartesianPlotMenu = new QMenu(i18n("Cartesian Plot"), this); m_cartesianPlotMenu->setIcon(QIcon::fromTheme("office-chart-line")); m_cartesianPlotMouseModeMenu = new QMenu(i18n("Mouse Mode"), this); m_cartesianPlotMouseModeMenu->setIcon(QIcon::fromTheme("input-mouse")); m_cartesianPlotMouseModeMenu->addAction(cartesianPlotSelectionModeAction); m_cartesianPlotMouseModeMenu->addAction(cartesianPlotZoomSelectionModeAction); m_cartesianPlotMouseModeMenu->addAction(cartesianPlotZoomXSelectionModeAction); m_cartesianPlotMouseModeMenu->addAction(cartesianPlotZoomYSelectionModeAction); m_cartesianPlotMouseModeMenu->addSeparator(); m_cartesianPlotMouseModeMenu->addAction(cartesianPlotCursorModeAction); m_cartesianPlotMouseModeMenu->addSeparator(); m_cartesianPlotAddNewMenu = new QMenu(i18n("Add New"), this); m_cartesianPlotAddNewMenu->setIcon(QIcon::fromTheme("list-add")); m_cartesianPlotAddNewMenu->addAction(addCurveAction); m_cartesianPlotAddNewMenu->addAction(addHistogramAction); m_cartesianPlotAddNewMenu->addAction(addEquationCurveAction); m_cartesianPlotAddNewMenu->addSeparator(); m_cartesianPlotAddNewAnalysisMenu = new QMenu(i18n("Analysis Curve")); m_cartesianPlotAddNewAnalysisMenu->addAction(addFitCurveAction); m_cartesianPlotAddNewAnalysisMenu->addSeparator(); m_cartesianPlotAddNewAnalysisMenu->addAction(addDifferentiationCurveAction); m_cartesianPlotAddNewAnalysisMenu->addAction(addIntegrationCurveAction); m_cartesianPlotAddNewAnalysisMenu->addSeparator(); m_cartesianPlotAddNewAnalysisMenu->addAction(addInterpolationCurveAction); m_cartesianPlotAddNewAnalysisMenu->addAction(addSmoothCurveAction); m_cartesianPlotAddNewAnalysisMenu->addSeparator(); m_cartesianPlotAddNewAnalysisMenu->addAction(addFourierFilterCurveAction); m_cartesianPlotAddNewAnalysisMenu->addAction(addFourierTransformCurveAction); m_cartesianPlotAddNewAnalysisMenu->addSeparator(); m_cartesianPlotAddNewAnalysisMenu->addAction(addConvolutionCurveAction); m_cartesianPlotAddNewAnalysisMenu->addAction(addCorrelationCurveAction); m_cartesianPlotAddNewAnalysisMenu->addSeparator(); // m_cartesianPlotAddNewAnalysisMenu->addAction(addDataOperationCurveAction); m_cartesianPlotAddNewAnalysisMenu->addAction(addDataReductionCurveAction); m_cartesianPlotAddNewMenu->addMenu(m_cartesianPlotAddNewAnalysisMenu); m_cartesianPlotAddNewMenu->addSeparator(); m_cartesianPlotAddNewMenu->addAction(addLegendAction); m_cartesianPlotAddNewMenu->addSeparator(); m_cartesianPlotAddNewMenu->addAction(addHorizontalAxisAction); m_cartesianPlotAddNewMenu->addAction(addVerticalAxisAction); m_cartesianPlotAddNewMenu->addSeparator(); m_cartesianPlotAddNewMenu->addAction(addPlotTextLabelAction); m_cartesianPlotAddNewMenu->addAction(addPlotImageAction); m_cartesianPlotAddNewMenu->addSeparator(); m_cartesianPlotAddNewMenu->addAction(addCustomPointAction); m_cartesianPlotZoomMenu = new QMenu(i18n("Zoom/Navigate"), this); m_cartesianPlotZoomMenu->setIcon(QIcon::fromTheme("zoom-draw")); m_cartesianPlotZoomMenu->addAction(scaleAutoAction); m_cartesianPlotZoomMenu->addAction(scaleAutoXAction); m_cartesianPlotZoomMenu->addAction(scaleAutoYAction); m_cartesianPlotZoomMenu->addSeparator(); m_cartesianPlotZoomMenu->addAction(zoomInAction); m_cartesianPlotZoomMenu->addAction(zoomOutAction); m_cartesianPlotZoomMenu->addSeparator(); m_cartesianPlotZoomMenu->addAction(zoomInXAction); m_cartesianPlotZoomMenu->addAction(zoomOutXAction); m_cartesianPlotZoomMenu->addSeparator(); m_cartesianPlotZoomMenu->addAction(zoomInYAction); m_cartesianPlotZoomMenu->addAction(zoomOutYAction); m_cartesianPlotZoomMenu->addSeparator(); m_cartesianPlotZoomMenu->addAction(shiftLeftXAction); m_cartesianPlotZoomMenu->addAction(shiftRightXAction); m_cartesianPlotZoomMenu->addSeparator(); m_cartesianPlotZoomMenu->addAction(shiftUpYAction); m_cartesianPlotZoomMenu->addAction(shiftDownYAction); m_cartesianPlotActionModeMenu = new QMenu(i18n("Apply Actions to"), this); m_cartesianPlotActionModeMenu->setIcon(QIcon::fromTheme("dialog-ok-apply")); m_cartesianPlotActionModeMenu->addAction(cartesianPlotApplyToSelectionAction); m_cartesianPlotActionModeMenu->addAction(cartesianPlotApplyToAllAction); m_cartesianPlotCursorModeMenu = new QMenu(i18n("Apply Cursor to"), this); m_cartesianPlotCursorModeMenu->addAction(cartesianPlotApplyToSelectionCursor); m_cartesianPlotCursorModeMenu->addAction(cartesianPlotApplyToAllCursor); m_cartesianPlotMenu->addMenu(m_cartesianPlotAddNewMenu); m_cartesianPlotMenu->addSeparator(); m_cartesianPlotMenu->addMenu(m_cartesianPlotMouseModeMenu); m_cartesianPlotMenu->addMenu(m_cartesianPlotZoomMenu); m_cartesianPlotMenu->addSeparator(); m_cartesianPlotMenu->addMenu(m_cartesianPlotActionModeMenu); m_cartesianPlotMenu->addMenu(m_cartesianPlotCursorModeMenu); m_cartesianPlotMenu->addSeparator(); m_cartesianPlotMenu->addAction(plotsLockedAction); // Data manipulation menu m_dataManipulationMenu = new QMenu(i18n("Data Manipulation") ,this); m_dataManipulationMenu->setIcon(QIcon::fromTheme("zoom-draw")); m_dataManipulationMenu->addAction(addDataOperationAction); m_dataManipulationMenu->addAction(addDataReductionAction); //themes menu m_themeMenu = new QMenu(i18n("Apply Theme"), this); m_themeMenu->setIcon(QIcon::fromTheme("color-management")); auto* themeWidget = new ThemesWidget(nullptr); connect(themeWidget, &ThemesWidget::themeSelected, m_worksheet, &Worksheet::setTheme); connect(themeWidget, &ThemesWidget::themeSelected, m_themeMenu, &QMenu::close); auto* widgetAction = new QWidgetAction(this); widgetAction->setDefaultWidget(themeWidget); m_themeMenu->addAction(widgetAction); m_menusInitialized = true; } /*! * Populates the menu \c menu with the worksheet and worksheet view relevant actions. * The menu is used * - as the context menu in WorksheetView * - as the "worksheet menu" in the main menu-bar (called form MainWin) * - as a part of the worksheet context menu in project explorer */ void WorksheetView::createContextMenu(QMenu* menu) { Q_ASSERT(menu != nullptr); if (!m_menusInitialized) initMenus(); QAction* firstAction = nullptr; // if we're populating the context menu for the project explorer, then //there're already actions available there. Skip the first title-action //and insert the action at the beginning of the menu. if (menu->actions().size() > 1) firstAction = menu->actions().at(1); menu->insertMenu(firstAction, m_addNewMenu); menu->insertSeparator(firstAction); menu->insertMenu(firstAction, m_viewMouseModeMenu); menu->insertMenu(firstAction, m_zoomMenu); menu->insertMenu(firstAction, m_magnificationMenu); menu->insertMenu(firstAction, m_layoutMenu); menu->insertMenu(firstAction, m_gridMenu); menu->insertMenu(firstAction, m_themeMenu); menu->insertSeparator(firstAction); menu->insertAction(firstAction, plotsLockedAction); menu->insertSeparator(firstAction); menu->insertMenu(firstAction, m_cartesianPlotMenu); menu->insertSeparator(firstAction); menu->insertAction(firstAction, showPresenterMode); menu->insertSeparator(firstAction); } void WorksheetView::createAnalysisMenu(QMenu* menu) { Q_ASSERT(menu != nullptr); if (!m_menusInitialized) initMenus(); // Data manipulation menu // menu->insertMenu(nullptr, m_dataManipulationMenu); menu->addAction(addFitAction); menu->addSeparator(); menu->addAction(addDifferentiationAction); menu->addAction(addIntegrationAction); menu->addSeparator(); menu->addAction(addInterpolationAction); menu->addAction(addSmoothAction); menu->addSeparator(); menu->addAction(addFourierFilterAction); menu->addAction(addFourierTransformAction); menu->addSeparator(); menu->addAction(addConvolutionAction); menu->addAction(addCorrelationAction); menu->addSeparator(); menu->addAction(addDataReductionAction); } void WorksheetView::fillToolBar(QToolBar* toolBar) { toolBar->addSeparator(); tbNewCartesianPlot = new QToolButton(toolBar); tbNewCartesianPlot->setPopupMode(QToolButton::MenuButtonPopup); tbNewCartesianPlot->setMenu(m_addNewCartesianPlotMenu); tbNewCartesianPlot->setDefaultAction(addCartesianPlot1Action); toolBar->addWidget(tbNewCartesianPlot); toolBar->addAction(addTextLabelAction); toolBar->addAction(addImageAction); toolBar->addSeparator(); toolBar->addAction(verticalLayoutAction); toolBar->addAction(horizontalLayoutAction); toolBar->addAction(gridLayoutAction); toolBar->addAction(breakLayoutAction); toolBar->addSeparator(); toolBar->addAction(selectionModeAction); toolBar->addAction(navigationModeAction); toolBar->addAction(zoomSelectionModeAction); toolBar->addSeparator(); tbZoom = new QToolButton(toolBar); tbZoom->setPopupMode(QToolButton::MenuButtonPopup); tbZoom->setMenu(m_zoomMenu); tbZoom->setDefaultAction(currentZoomAction); toolBar->addWidget(tbZoom); tbMagnification = new QToolButton(toolBar); tbMagnification->setPopupMode(QToolButton::MenuButtonPopup); tbMagnification->setMenu(m_magnificationMenu); tbMagnification->setDefaultAction(currentMagnificationAction); toolBar->addWidget(tbMagnification); } #ifdef Q_OS_MAC void WorksheetView::fillTouchBar(KDMacTouchBar* touchBar){ //touchBar->addAction(addCartesianPlot1Action); touchBar->addAction(zoomInViewAction); touchBar->addAction(zoomOutViewAction); touchBar->addAction(showPresenterMode); } #endif void WorksheetView::fillCartesianPlotToolBar(QToolBar* toolBar) { toolBar->addAction(cartesianPlotSelectionModeAction); toolBar->addAction(cartesianPlotZoomSelectionModeAction); toolBar->addAction(cartesianPlotZoomXSelectionModeAction); toolBar->addAction(cartesianPlotZoomYSelectionModeAction); toolBar->addAction(cartesianPlotCursorModeAction); toolBar->addSeparator(); toolBar->addAction(addCurveAction); toolBar->addAction(addHistogramAction); toolBar->addAction(addEquationCurveAction); // don't over-populate the tool bar // toolBar->addAction(addDifferentiationCurveAction); // toolBar->addAction(addIntegrationCurveAction); // toolBar->addAction(addDataOperationCurveAction); // toolBar->addAction(addDataReductionCurveAction); // toolBar->addAction(addInterpolationCurveAction); // toolBar->addAction(addSmoothCurveAction); // toolBar->addAction(addFitCurveAction); // toolBar->addAction(addFourierFilterCurveAction); // toolBar->addAction(addFourierTransformCurveAction); // toolBar->addAction(addConvolutionCurveAction); // toolBar->addAction(addCorrelationCurveAction); toolBar->addSeparator(); toolBar->addAction(addLegendAction); toolBar->addSeparator(); toolBar->addAction(addHorizontalAxisAction); toolBar->addAction(addVerticalAxisAction); toolBar->addSeparator(); toolBar->addAction(addPlotTextLabelAction); toolBar->addAction(addPlotImageAction); toolBar->addSeparator(); toolBar->addAction(scaleAutoAction); toolBar->addAction(scaleAutoXAction); toolBar->addAction(scaleAutoYAction); toolBar->addAction(zoomInAction); toolBar->addAction(zoomOutAction); toolBar->addAction(zoomInXAction); toolBar->addAction(zoomOutXAction); toolBar->addAction(zoomInYAction); toolBar->addAction(zoomOutYAction); toolBar->addAction(shiftLeftXAction); toolBar->addAction(shiftRightXAction); toolBar->addAction(shiftUpYAction); toolBar->addAction(shiftDownYAction); toolBar->addSeparator(); handleCartesianPlotActions(); } void WorksheetView::setScene(QGraphicsScene* scene) { QGraphicsView::setScene(scene); } void WorksheetView::setIsClosing() { m_isClosing = true; } void WorksheetView::setCartesianPlotActionMode(Worksheet::CartesianPlotActionMode mode) { if (mode == Worksheet::CartesianPlotActionMode::ApplyActionToAll) cartesianPlotApplyToAllAction->setChecked(true); else cartesianPlotApplyToSelectionAction->setChecked(true); } void WorksheetView::setCartesianPlotCursorMode(Worksheet::CartesianPlotActionMode mode) { if (mode == Worksheet::CartesianPlotActionMode::ApplyActionToAll) cartesianPlotApplyToAllCursor->setChecked(true); else cartesianPlotApplyToSelectionCursor->setChecked(true); } void WorksheetView::setPlotLock(bool lock) { plotsLockedAction->setChecked(lock); } void WorksheetView::drawForeground(QPainter* painter, const QRectF& rect) { if (m_mouseMode == ZoomSelectionMode && m_selectionBandIsShown) { painter->save(); const QRectF& selRect = mapToScene(QRect(m_selectionStart, m_selectionEnd).normalized()).boundingRect(); //TODO: don't hardcode for black here, use a a different color depending on the theme of the worksheet/plot under the mouse cursor? painter->setPen(QPen(Qt::black, 5/transform().m11())); painter->drawRect(selRect); painter->setBrush(QApplication::palette().color(QPalette::Highlight)); painter->setOpacity(0.2); painter->drawRect(selRect); painter->restore(); } QGraphicsView::drawForeground(painter, rect); } void WorksheetView::drawBackgroundItems(QPainter* painter, const QRectF& scene_rect) { // canvas painter->setOpacity(m_worksheet->backgroundOpacity()); if (m_worksheet->backgroundType() == PlotArea::Color) { switch (m_worksheet->backgroundColorStyle()) { case PlotArea::SingleColor: { painter->setBrush(QBrush(m_worksheet->backgroundFirstColor())); break; } case PlotArea::HorizontalLinearGradient: { QLinearGradient linearGrad(scene_rect.topLeft(), scene_rect.topRight()); linearGrad.setColorAt(0, m_worksheet->backgroundFirstColor()); linearGrad.setColorAt(1, m_worksheet->backgroundSecondColor()); painter->setBrush(QBrush(linearGrad)); break; } case PlotArea::VerticalLinearGradient: { QLinearGradient linearGrad(scene_rect.topLeft(), scene_rect.bottomLeft()); linearGrad.setColorAt(0, m_worksheet->backgroundFirstColor()); linearGrad.setColorAt(1, m_worksheet->backgroundSecondColor()); painter->setBrush(QBrush(linearGrad)); break; } case PlotArea::TopLeftDiagonalLinearGradient: { QLinearGradient linearGrad(scene_rect.topLeft(), scene_rect.bottomRight()); linearGrad.setColorAt(0, m_worksheet->backgroundFirstColor()); linearGrad.setColorAt(1, m_worksheet->backgroundSecondColor()); painter->setBrush(QBrush(linearGrad)); break; } case PlotArea::BottomLeftDiagonalLinearGradient: { QLinearGradient linearGrad(scene_rect.bottomLeft(), scene_rect.topRight()); linearGrad.setColorAt(0, m_worksheet->backgroundFirstColor()); linearGrad.setColorAt(1, m_worksheet->backgroundSecondColor()); painter->setBrush(QBrush(linearGrad)); break; } case PlotArea::RadialGradient: { QRadialGradient radialGrad(scene_rect.center(), scene_rect.width()/2); radialGrad.setColorAt(0, m_worksheet->backgroundFirstColor()); radialGrad.setColorAt(1, m_worksheet->backgroundSecondColor()); painter->setBrush(QBrush(radialGrad)); break; } //default: // painter->setBrush(QBrush(m_worksheet->backgroundFirstColor())); } painter->drawRect(scene_rect); } else if (m_worksheet->backgroundType() == PlotArea::Image) { // background image const QString& backgroundFileName = m_worksheet->backgroundFileName().trimmed(); if ( !backgroundFileName.isEmpty() ) { QPixmap pix(backgroundFileName); switch (m_worksheet->backgroundImageStyle()) { case PlotArea::ScaledCropped: pix = pix.scaled(scene_rect.size().toSize(),Qt::KeepAspectRatioByExpanding,Qt::SmoothTransformation); painter->drawPixmap(scene_rect.topLeft(),pix); break; case PlotArea::Scaled: pix = pix.scaled(scene_rect.size().toSize(),Qt::IgnoreAspectRatio,Qt::SmoothTransformation); painter->drawPixmap(scene_rect.topLeft(),pix); break; case PlotArea::ScaledAspectRatio: pix = pix.scaled(scene_rect.size().toSize(),Qt::KeepAspectRatio,Qt::SmoothTransformation); painter->drawPixmap(scene_rect.topLeft(),pix); break; case PlotArea::Centered: painter->drawPixmap(QPointF(scene_rect.center().x()-pix.size().width()/2,scene_rect.center().y()-pix.size().height()/2),pix); break; case PlotArea::Tiled: painter->drawTiledPixmap(scene_rect,pix); break; case PlotArea::CenterTiled: painter->drawTiledPixmap(scene_rect,pix,QPoint(scene_rect.size().width()/2,scene_rect.size().height()/2)); break; //default: // painter->drawPixmap(scene_rect.topLeft(),pix); } } } else if (m_worksheet->backgroundType() == PlotArea::Pattern) { // background pattern painter->setBrush(QBrush(m_worksheet->backgroundFirstColor(),m_worksheet->backgroundBrushStyle())); painter->drawRect(scene_rect); } //grid if (m_gridSettings.style != WorksheetView::NoGrid) { QColor c = m_gridSettings.color; c.setAlphaF(m_gridSettings.opacity); painter->setPen(c); qreal x, y; qreal left = scene_rect.left(); qreal right = scene_rect.right(); qreal top = scene_rect.top(); qreal bottom = scene_rect.bottom(); if (m_gridSettings.style == WorksheetView::LineGrid) { QLineF line; //horizontal lines y = top + m_gridSettings.verticalSpacing; while (y < bottom) { line.setLine( left, y, right, y ); painter->drawLine(line); y += m_gridSettings.verticalSpacing; } //vertical lines x = left + m_gridSettings.horizontalSpacing; while (x < right) { line.setLine( x, top, x, bottom ); painter->drawLine(line); x += m_gridSettings.horizontalSpacing; } } else { //DotGrid y = top + m_gridSettings.verticalSpacing; while (y < bottom) { x = left;// + m_gridSettings.horizontalSpacing; while (x < right) { x += m_gridSettings.horizontalSpacing; painter->drawPoint(x, y); } y += m_gridSettings.verticalSpacing; } } } } void WorksheetView::drawBackground(QPainter* painter, const QRectF& rect) { painter->save(); //painter->setRenderHint(QPainter::Antialiasing); QRectF scene_rect = sceneRect(); if (!m_worksheet->useViewSize()) { // background KColorScheme scheme(QPalette::Active, KColorScheme::Window); const QColor& color = scheme.background().color(); if (!scene_rect.contains(rect)) painter->fillRect(rect, color); //shadow // int shadowSize = scene_rect.width()*0.02; // QRectF rightShadowRect(scene_rect.right(), scene_rect.top() + shadowSize, shadowSize, scene_rect.height()); // QRectF bottomShadowRect(scene_rect.left() + shadowSize, scene_rect.bottom(), scene_rect.width(), shadowSize); // // const QColor& shadeColor = scheme.shade(color, KColorScheme::MidShade); // painter->fillRect(rightShadowRect.intersected(rect), shadeColor); // painter->fillRect(bottomShadowRect.intersected(rect), shadeColor); } drawBackgroundItems(painter, scene_rect); invalidateScene(rect, QGraphicsScene::BackgroundLayer); painter->restore(); } bool WorksheetView::isPlotAtPos(QPoint pos) const { bool plot = false; QGraphicsItem* item = itemAt(pos); if (item) { plot = item->data(0).toInt() == WorksheetElement::NameCartesianPlot; if (!plot && item->parentItem()) plot = item->parentItem()->data(0).toInt() == WorksheetElement::NameCartesianPlot; } return plot; } CartesianPlot* WorksheetView::plotAt(QPoint pos) const { QGraphicsItem* item = itemAt(pos); if (!item) return nullptr; QGraphicsItem* plotItem = nullptr; if (item->data(0).toInt() == WorksheetElement::NameCartesianPlot) plotItem = item; else { if (item->parentItem() && item->parentItem()->data(0).toInt() == WorksheetElement::NameCartesianPlot) plotItem = item->parentItem(); } if (plotItem == nullptr) return nullptr; CartesianPlot* plot = nullptr; for (auto* p : m_worksheet->children()) { if (p->graphicsItem() == plotItem) { plot = p; break; } } return plot; } //############################################################################## //#################################### Events ############################### //############################################################################## void WorksheetView::resizeEvent(QResizeEvent* event) { if (m_isClosing) return; if (m_worksheet->useViewSize()) this->processResize(); QGraphicsView::resizeEvent(event); } void WorksheetView::wheelEvent(QWheelEvent* event) { //https://wiki.qt.io/Smooth_Zoom_In_QGraphicsView if (m_mouseMode == ZoomSelectionMode || (QApplication::keyboardModifiers() & Qt::ControlModifier)) { int numDegrees = event->delta() / 8; int numSteps = numDegrees / 15; // see QWheelEvent documentation zoom(numSteps); } else QGraphicsView::wheelEvent(event); } void WorksheetView::zoom(int numSteps) { m_numScheduledScalings += numSteps; if (m_numScheduledScalings * numSteps < 0) // if user moved the wheel in another direction, we reset previously scheduled scalings m_numScheduledScalings = numSteps; auto* anim = new QTimeLine(350, this); anim->setUpdateInterval(20); connect(anim, &QTimeLine::valueChanged, this, &WorksheetView::scalingTime); connect(anim, &QTimeLine::finished, this, &WorksheetView::animFinished); anim->start(); } void WorksheetView::scalingTime() { qreal factor = 1.0 + qreal(m_numScheduledScalings) / 300.0; scale(factor, factor); } void WorksheetView::animFinished() { if (m_numScheduledScalings > 0) m_numScheduledScalings--; else m_numScheduledScalings++; sender()->~QObject(); } void WorksheetView::mousePressEvent(QMouseEvent* event) { //prevent the deselection of items when context menu event //was triggered (right button click) if (event->button() == Qt::RightButton) { event->accept(); return; } if (event->button() == Qt::LeftButton && m_mouseMode == ZoomSelectionMode) { m_selectionStart = event->pos(); m_selectionEnd = m_selectionStart; //select&zoom'g starts -> reset the end point to the start point m_selectionBandIsShown = true; QGraphicsView::mousePressEvent(event); return; } // select the worksheet in the project explorer if the view was clicked // and there is no selection currently. We need this for the case when // there is a single worksheet in the project and we change from the project-node // in the project explorer to the worksheet-node by clicking the view. if ( scene()->selectedItems().isEmpty() ) m_worksheet->setSelectedInView(true); QGraphicsView::mousePressEvent(event); } void WorksheetView::mouseReleaseEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton && m_mouseMode == ZoomSelectionMode) { m_selectionBandIsShown = false; viewport()->repaint(QRect(m_selectionStart, m_selectionEnd).normalized()); //don't zoom if very small region was selected, avoid occasional/unwanted zooming m_selectionEnd = event->pos(); if ( abs(m_selectionEnd.x() - m_selectionStart.x()) > 20 && abs(m_selectionEnd.y() - m_selectionStart.y()) > 20 ) fitInView(mapToScene(QRect(m_selectionStart, m_selectionEnd).normalized()).boundingRect(), Qt::KeepAspectRatio); } QGraphicsView::mouseReleaseEvent(event); } void WorksheetView::mouseMoveEvent(QMouseEvent* event) { if (m_mouseMode == SelectionMode && m_cartesianPlotMouseMode != CartesianPlot::SelectionMode ) { //check whether there is a cartesian plot under the cursor //and set the cursor appearance according to the current mouse mode for the cartesian plots if ( isPlotAtPos(event->pos()) ) { if (m_cartesianPlotMouseMode == CartesianPlot::ZoomSelectionMode) setCursor(Qt::CrossCursor); else if (m_cartesianPlotMouseMode == CartesianPlot::ZoomXSelectionMode) setCursor(Qt::SizeHorCursor); else if (m_cartesianPlotMouseMode == CartesianPlot::ZoomYSelectionMode) setCursor(Qt::SizeVerCursor); } else setCursor(Qt::ArrowCursor); } else if (m_mouseMode == SelectionMode && m_cartesianPlotMouseMode == CartesianPlot::SelectionMode ) setCursor(Qt::ArrowCursor); else if (m_selectionBandIsShown) { QRect rect = QRect(m_selectionStart, m_selectionEnd).normalized(); m_selectionEnd = event->pos(); rect = rect.united(QRect(m_selectionStart, m_selectionEnd).normalized()); qreal penWidth = 5/transform().m11(); rect.setX(rect.x()-penWidth); rect.setY(rect.y()-penWidth); rect.setHeight(rect.height()+2*penWidth); rect.setWidth(rect.width()+2*penWidth); viewport()->repaint(rect); } //show the magnification window if (magnificationFactor /*&& m_mouseMode == SelectAndEditMode*/) { if (!m_magnificationWindow) { m_magnificationWindow = new QGraphicsPixmapItem(nullptr); m_magnificationWindow->setZValue(std::numeric_limits::max()); scene()->addItem(m_magnificationWindow); } m_magnificationWindow->setVisible(false); //copy the part of the view to be shown magnified QPointF pos = mapToScene(event->pos()); const int size = Worksheet::convertToSceneUnits(2.0, Worksheet::Centimeter)/transform().m11(); const QRectF copyRect(pos.x() - size/(2*magnificationFactor), pos.y() - size/(2*magnificationFactor), size/magnificationFactor, size/magnificationFactor); QPixmap px = grab(mapFromScene(copyRect).boundingRect()); px = px.scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); //draw the bounding rect QPainter painter(&px); const QPen pen = QPen(Qt::lightGray, 2/transform().m11()); painter.setPen(pen); QRect rect = px.rect(); rect.setWidth(rect.width()-pen.widthF()/2); rect.setHeight(rect.height()-pen.widthF()/2); painter.drawRect(rect); //set the pixmap m_magnificationWindow->setPixmap(px); m_magnificationWindow->setPos(pos.x()- px.width()/2, pos.y()- px.height()/2); m_magnificationWindow->setVisible(true); } else if (m_magnificationWindow) m_magnificationWindow->setVisible(false); QGraphicsView::mouseMoveEvent(event); } void WorksheetView::contextMenuEvent(QContextMenuEvent* e) { if ( (m_magnificationWindow && m_magnificationWindow->isVisible() && items(e->pos()).size() == 1) || !itemAt(e->pos()) ) { //no item or only the magnification window under the cursor -> show the context menu for the worksheet QMenu *menu = new QMenu(this); this->createContextMenu(menu); menu->exec(QCursor::pos()); } else { //propagate the event to the scene and graphics items QGraphicsView::contextMenuEvent(e); } } void WorksheetView::keyPressEvent(QKeyEvent* event) { if (event->matches(QKeySequence::Copy)) { //add here copying of objects exportToClipboard(); } QGraphicsView::keyPressEvent(event); } void WorksheetView::keyReleaseEvent(QKeyEvent* event) { QGraphicsView::keyReleaseEvent(event); } void WorksheetView::dragEnterEvent(QDragEnterEvent* event) { //ignore events not related to internal drags of columns etc., e.g. dropping of external files onto LabPlot const QMimeData* mimeData = event->mimeData(); if (!mimeData) { event->ignore(); return; } if (mimeData->formats().at(0) != QLatin1String("labplot-dnd")) { event->ignore(); return; } //select the worksheet in the project explorer and bring the view to the foreground m_worksheet->setSelectedInView(true); m_worksheet->mdiSubWindow()->mdiArea()->setActiveSubWindow(m_worksheet->mdiSubWindow()); event->setAccepted(true); } void WorksheetView::dragMoveEvent(QDragMoveEvent* event) { // only accept drop events if we have a plot under the cursor where we can drop columns onto bool plot = isPlotAtPos(event->pos()); event->setAccepted(plot); } void WorksheetView::dropEvent(QDropEvent* event) { CartesianPlot* plot = plotAt(event->pos()); if (plot != nullptr) plot->processDropEvent(event); } //############################################################################## //#################################### SLOTs ################################ //############################################################################## void WorksheetView::useViewSizeRequested() { if (!m_actionsInitialized) initActions(); if (m_worksheet->useViewSize()) { setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); zoomFitPageHeightAction->setVisible(false); zoomFitPageWidthAction->setVisible(false); currentZoomAction = zoomInViewAction; if (tbZoom) tbZoom->setDefaultAction(zoomInViewAction); //determine and set the current view size this->processResize(); } else { setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); zoomFitPageHeightAction->setVisible(true); zoomFitPageWidthAction->setVisible(true); } } void WorksheetView::processResize() { if (size() != sceneRect().size()) { static const float hscale = QApplication::desktop()->physicalDpiX()/(Worksheet::convertToSceneUnits(1,Worksheet::Inch)); static const float vscale = QApplication::desktop()->physicalDpiY()/(Worksheet::convertToSceneUnits(1,Worksheet::Inch)); m_worksheet->setUndoAware(false); m_worksheet->setPageRect(QRectF(0.0, 0.0, width()/hscale, height()/vscale)); m_worksheet->setUndoAware(true); } } void WorksheetView::changeZoom(QAction* action) { if (action == zoomInViewAction) zoom(1); else if (action == zoomOutViewAction) zoom(-1); else if (action == zoomOriginAction) { static const float hscale = QApplication::desktop()->physicalDpiX()/(Worksheet::convertToSceneUnits(1,Worksheet::Inch)); static const float vscale = QApplication::desktop()->physicalDpiY()/(Worksheet::convertToSceneUnits(1,Worksheet::Inch)); setTransform(QTransform::fromScale(hscale, vscale)); } else if (action == zoomFitPageWidthAction) { float scaleFactor = viewport()->width()/scene()->sceneRect().width(); setTransform(QTransform::fromScale(scaleFactor, scaleFactor)); } else if (action == zoomFitPageHeightAction) { float scaleFactor = viewport()->height()/scene()->sceneRect().height(); setTransform(QTransform::fromScale(scaleFactor, scaleFactor)); } else if (action == zoomFitSelectionAction) fitInView(scene()->selectionArea().boundingRect(),Qt::KeepAspectRatio); currentZoomAction = action; if (tbZoom) tbZoom->setDefaultAction(action); } void WorksheetView::magnificationChanged(QAction* action) { if (action == noMagnificationAction) magnificationFactor = 0; else if (action == twoTimesMagnificationAction) magnificationFactor = 2; else if (action == threeTimesMagnificationAction) magnificationFactor = 3; else if (action == fourTimesMagnificationAction) magnificationFactor = 4; else if (action == fiveTimesMagnificationAction) magnificationFactor = 5; currentMagnificationAction = action; if (tbMagnification) tbMagnification->setDefaultAction(action); } void WorksheetView::mouseModeChanged(QAction* action) { if (action == selectionModeAction) { m_mouseMode = SelectionMode; setInteractive(true); setDragMode(QGraphicsView::NoDrag); } else if (action == navigationModeAction) { m_mouseMode = NavigationMode; setInteractive(false); setDragMode(QGraphicsView::ScrollHandDrag); } else { m_mouseMode = ZoomSelectionMode; setInteractive(false); setDragMode(QGraphicsView::NoDrag); } } //"Add new" related slots void WorksheetView::addNew(QAction* action) { WorksheetElement* aspect = nullptr; if (action == addCartesianPlot1Action) { CartesianPlot* plot = new CartesianPlot(i18n("xy-plot")); plot->initDefault(CartesianPlot::FourAxes); plot->setMouseMode(m_cartesianPlotMouseMode); aspect = plot; if (tbNewCartesianPlot) tbNewCartesianPlot->setDefaultAction(addCartesianPlot1Action); } else if (action == addCartesianPlot2Action) { CartesianPlot* plot = new CartesianPlot(i18n("xy-plot")); plot->initDefault(CartesianPlot::TwoAxes); plot->setMouseMode(m_cartesianPlotMouseMode); aspect = plot; if (tbNewCartesianPlot) tbNewCartesianPlot->setDefaultAction(addCartesianPlot2Action); } else if (action == addCartesianPlot3Action) { CartesianPlot* plot = new CartesianPlot(i18n("xy-plot")); plot->initDefault(CartesianPlot::TwoAxesCentered); plot->setMouseMode(m_cartesianPlotMouseMode); aspect = plot; if (tbNewCartesianPlot) tbNewCartesianPlot->setDefaultAction(addCartesianPlot3Action); } else if (action == addCartesianPlot4Action) { CartesianPlot* plot = new CartesianPlot(i18n("xy-plot")); plot->initDefault(CartesianPlot::TwoAxesCenteredZero); plot->setMouseMode(m_cartesianPlotMouseMode); aspect = plot; if (tbNewCartesianPlot) tbNewCartesianPlot->setDefaultAction(addCartesianPlot4Action); } else if (action == addTextLabelAction) { TextLabel* l = new TextLabel(i18n("Text Label")); l->setText(i18n("Text Label")); aspect = l; } else if (action == addImageAction) { Image* l = new Image(i18n("Image")); aspect = l; } if (!aspect) return; m_worksheet->addChild(aspect); //labels and images with their initial positions need to be retransformed //ater they have gotten a parent if (aspect->type() == AspectType::TextLabel || aspect->type() == AspectType::Image) aspect->retransform(); handleCartesianPlotActions(); if (!m_fadeInTimeLine) { m_fadeInTimeLine = new QTimeLine(1000, this); m_fadeInTimeLine->setFrameRange(0, 100); connect(m_fadeInTimeLine, &QTimeLine::valueChanged, this, &WorksheetView::fadeIn); } //if there is already an element fading in, stop the time line and show the element with the full opacity. if (m_fadeInTimeLine->state() == QTimeLine::Running) { m_fadeInTimeLine->stop(); auto* effect = new QGraphicsOpacityEffect(); effect->setOpacity(1); lastAddedWorksheetElement->graphicsItem()->setGraphicsEffect(effect); } //fade-in the newly added element lastAddedWorksheetElement = aspect; auto* effect = new QGraphicsOpacityEffect(); effect->setOpacity(0); lastAddedWorksheetElement->graphicsItem()->setGraphicsEffect(effect); m_fadeInTimeLine->start(); } /*! * select all top-level items */ void WorksheetView::selectAllElements() { //deselect all previously selected items since there can be some non top-level items belong them m_suppressSelectionChangedEvent = true; for (auto* item : m_selectedItems) m_worksheet->setItemSelectedInView(item, false); //select top-level items for (auto* item : scene()->items()) { if (!item->parentItem()) item->setSelected(true); } m_suppressSelectionChangedEvent = false; this->selectionChanged(); } /*! * deletes selected worksheet elements */ void WorksheetView::deleteElement() { if (m_selectedItems.isEmpty()) return; int rc = KMessageBox::warningYesNo( this, i18np("Do you really want to delete the selected object?", "Do you really want to delete the selected %1 objects?", m_selectedItems.size()), i18np("Delete selected object", "Delete selected objects", m_selectedItems.size())); if (rc == KMessageBox::No) return; m_suppressSelectionChangedEvent = true; m_worksheet->beginMacro(i18n("%1: Remove selected worksheet elements.", m_worksheet->name())); for (auto* item : m_selectedItems) m_worksheet->deleteAspectFromGraphicsItem(item); m_worksheet->endMacro(); m_suppressSelectionChangedEvent = false; } void WorksheetView::aspectAboutToBeRemoved(const AbstractAspect* aspect) { lastAddedWorksheetElement = dynamic_cast(const_cast(aspect)); if (!lastAddedWorksheetElement) return; //FIXME: fading-out doesn't work //also, the following code collides with undo/redo of the deletion //of a worksheet element (after redoing the element is not shown with the full opacity /* if (!m_fadeOutTimeLine) { m_fadeOutTimeLine = new QTimeLine(1000, this); m_fadeOutTimeLine->setFrameRange(0, 100); connect(m_fadeOutTimeLine, SIGNAL(valueChanged(qreal)), this, SLOT(fadeOut(qreal))); } //if there is already an element fading out, stop the time line if (m_fadeOutTimeLine->state() == QTimeLine::Running) m_fadeOutTimeLine->stop(); m_fadeOutTimeLine->start(); */ } void WorksheetView::fadeIn(qreal value) { auto* effect = new QGraphicsOpacityEffect(); effect->setOpacity(value); lastAddedWorksheetElement->graphicsItem()->setGraphicsEffect(effect); } void WorksheetView::fadeOut(qreal value) { auto* effect = new QGraphicsOpacityEffect(); effect->setOpacity(1 - value); lastAddedWorksheetElement->graphicsItem()->setGraphicsEffect(effect); } /*! * called when one of the layout-actions in WorkseetView was triggered. * sets the layout in Worksheet and enables/disables the layout actions. */ void WorksheetView::changeLayout(QAction* action) { if (action == breakLayoutAction) { verticalLayoutAction->setEnabled(true); verticalLayoutAction->setChecked(false); horizontalLayoutAction->setEnabled(true); horizontalLayoutAction->setChecked(false); gridLayoutAction->setEnabled(true); gridLayoutAction->setChecked(false); breakLayoutAction->setEnabled(false); m_worksheet->setLayout(Worksheet::NoLayout); } else { verticalLayoutAction->setEnabled(false); horizontalLayoutAction->setEnabled(false); gridLayoutAction->setEnabled(false); breakLayoutAction->setEnabled(true); if (action == verticalLayoutAction) { verticalLayoutAction->setChecked(true); m_worksheet->setLayout(Worksheet::VerticalLayout); } else if (action == horizontalLayoutAction) { horizontalLayoutAction->setChecked(true); m_worksheet->setLayout(Worksheet::HorizontalLayout); } else { gridLayoutAction->setChecked(true); m_worksheet->setLayout(Worksheet::GridLayout); } } } void WorksheetView::changeGrid(QAction* action) { if (action == noGridAction) { m_gridSettings.style = WorksheetView::NoGrid; snapToGridAction->setEnabled(false); } else if (action == sparseLineGridAction) { m_gridSettings.style = WorksheetView::LineGrid; m_gridSettings.color = Qt::gray; m_gridSettings.opacity = 0.7; m_gridSettings.horizontalSpacing = 15; m_gridSettings.verticalSpacing = 15; } else if (action == denseLineGridAction) { m_gridSettings.style = WorksheetView::LineGrid; m_gridSettings.color = Qt::gray; m_gridSettings.opacity = 0.7; m_gridSettings.horizontalSpacing = 5; m_gridSettings.verticalSpacing = 5; } else if (action == denseDotGridAction) { m_gridSettings.style = WorksheetView::DotGrid; m_gridSettings.color = Qt::black; m_gridSettings.opacity = 0.7; m_gridSettings.horizontalSpacing = 5; m_gridSettings.verticalSpacing = 5; } else if (action == sparseDotGridAction) { m_gridSettings.style = WorksheetView::DotGrid; m_gridSettings.color = Qt::black; m_gridSettings.opacity = 0.7; m_gridSettings.horizontalSpacing = 15; m_gridSettings.verticalSpacing = 15; } else if (action == customGridAction) { auto* dlg = new GridDialog(this); if (dlg->exec() == QDialog::Accepted) dlg->save(m_gridSettings); else return; } if (m_gridSettings.style == WorksheetView::NoGrid) snapToGridAction->setEnabled(false); else snapToGridAction->setEnabled(true); invalidateScene(sceneRect(), QGraphicsScene::BackgroundLayer); } //TODO void WorksheetView::changeSnapToGrid() { } /*! * Selects the QGraphicsItem \c item in \c WorksheetView. * The selection in \c ProjectExplorer is forwarded to \c Worksheet * and is finally handled here. */ void WorksheetView::selectItem(QGraphicsItem* item) { m_suppressSelectionChangedEvent = true; item->setSelected(true); m_selectedItems<setSelected(false); m_selectedItems.removeOne(item); handleCartesianPlotActions(); m_suppressSelectionChangedEvent = false; } /*! * Called on selection changes in the view. * Determines which items were selected and deselected * and forwards these changes to \c Worksheet */ void WorksheetView::selectionChanged() { //if the project is being closed, the scene items are being removed and the selection can change. //don't react on these changes since this can lead crashes (worksheet object is already in the destructor). if (m_isClosing) return; if (m_suppressSelectionChangedEvent) return; QList items = scene()->selectedItems(); //When making a graphics item invisible, it gets deselected in the scene. //In this case we don't want to deselect the item in the project explorer. bool invisibleDeselected = false; //check, whether the previously selected items were deselected now. //Forward the deselection prior to the selection of new items //in order to avoid the unwanted multiple selection in project explorer for (auto* item : m_selectedItems ) { if ( items.indexOf(item) == -1 ) { if (item->isVisible()) m_worksheet->setItemSelectedInView(item, false); else invisibleDeselected = true; } } //select new items if (items.isEmpty() && invisibleDeselected == false) { //no items selected -> select the worksheet again. m_worksheet->setSelectedInView(true); //if one of the "zoom&select" plot mouse modes was selected before, activate the default "selection mode" again //since no plots are selected now. if (m_mouseMode == SelectionMode && m_cartesianPlotMouseMode!= CartesianPlot::SelectionMode) { cartesianPlotSelectionModeAction->setChecked(true); cartesianPlotMouseModeChanged(cartesianPlotSelectionModeAction); } } else { for (const auto* item : items) m_worksheet->setItemSelectedInView(item, true); //items selected -> deselect the worksheet in the project explorer //prevents unwanted multiple selection with worksheet (if it was selected before) m_worksheet->setSelectedInView(false); } m_selectedItems = items; handleCartesianPlotActions(); } //check whether we have cartesian plots selected and activate/deactivate void WorksheetView::handleCartesianPlotActions() { if (!m_menusInitialized) return; bool plot = false; if (m_worksheet->cartesianPlotActionMode() == Worksheet::CartesianPlotActionMode::ApplyActionToSelection) { //check whether we have cartesian plots selected for (auto* item : m_selectedItems) { //TODO: or if a children of a plot is selected if (item->data(0).toInt() == WorksheetElement::NameCartesianPlot) { plot = true; break; } } } else { //actions are applied to all available plots -> check whether we have plots plot = (m_worksheet->children().size() != 0); } cartesianPlotSelectionModeAction->setEnabled(plot); cartesianPlotZoomSelectionModeAction->setEnabled(plot); cartesianPlotZoomXSelectionModeAction->setEnabled(plot); cartesianPlotZoomYSelectionModeAction->setEnabled(plot); cartesianPlotCursorModeAction->setEnabled(plot); m_cartesianPlotAddNewMenu->setEnabled(plot); m_cartesianPlotZoomMenu->setEnabled(plot); m_cartesianPlotMouseModeMenu->setEnabled(plot); // analysis menu //TODO: enable also if children of plots are selected // m_dataManipulationMenu->setEnabled(plot); // addDataOperationAction->setEnabled(false); addDataReductionAction->setEnabled(false); addDifferentiationAction->setEnabled(plot); addIntegrationAction->setEnabled(plot); addInterpolationAction->setEnabled(plot); addSmoothAction->setEnabled(plot); addFitAction->setEnabled(plot); addFourierFilterAction->setEnabled(plot); addFourierTransformAction->setEnabled(plot); addConvolutionAction->setEnabled(plot); addCorrelationAction->setEnabled(plot); } void WorksheetView::exportToFile(const QString& path, const ExportFormat format, const ExportArea area, const bool background, const int resolution) { QRectF sourceRect; //determine the rectangular to print if (area == WorksheetView::ExportBoundingBox) sourceRect = scene()->itemsBoundingRect(); else if (area == WorksheetView::ExportSelection) { //TODO doesn't work: rect = scene()->selectionArea().boundingRect(); for (const auto* item : m_selectedItems) sourceRect = sourceRect.united( item->mapToScene(item->boundingRect()).boundingRect() ); } else sourceRect = scene()->sceneRect(); //print if (format == WorksheetView::Pdf) { QPrinter printer(QPrinter::HighResolution); printer.setOutputFormat(QPrinter::PdfFormat); printer.setOutputFileName(path); int w = Worksheet::convertFromSceneUnits(sourceRect.width(), Worksheet::Millimeter); int h = Worksheet::convertFromSceneUnits(sourceRect.height(), Worksheet::Millimeter); printer.setPaperSize( QSizeF(w, h), QPrinter::Millimeter); printer.setPageMargins(0,0,0,0, QPrinter::Millimeter); printer.setPrintRange(QPrinter::PageRange); printer.setCreator(QLatin1String("LabPlot ") + LVERSION); QPainter painter(&printer); painter.setRenderHint(QPainter::Antialiasing); QRectF targetRect(0, 0, painter.device()->width(),painter.device()->height()); painter.begin(&printer); exportPaint(&painter, targetRect, sourceRect, background); painter.end(); } else if (format == WorksheetView::Svg) { QSvgGenerator generator; generator.setFileName(path); +// if (!generator.isValid()) { +// RESET_CURSOR; +// QMessageBox::critical(nullptr, i18n("Failed to export"), i18n("Failed to write to '%1'. Please check the path.", path)); +// } int w = Worksheet::convertFromSceneUnits(sourceRect.width(), Worksheet::Millimeter); int h = Worksheet::convertFromSceneUnits(sourceRect.height(), Worksheet::Millimeter); w = w*QApplication::desktop()->physicalDpiX()/25.4; h = h*QApplication::desktop()->physicalDpiY()/25.4; generator.setSize(QSize(w, h)); QRectF targetRect(0, 0, w, h); generator.setViewBox(targetRect); QPainter painter; painter.begin(&generator); exportPaint(&painter, targetRect, sourceRect, background); painter.end(); } else { //PNG //TODO add all formats supported by Qt in QImage int w = Worksheet::convertFromSceneUnits(sourceRect.width(), Worksheet::Millimeter); int h = Worksheet::convertFromSceneUnits(sourceRect.height(), Worksheet::Millimeter); w = w*resolution/25.4; h = h*resolution/25.4; QImage image(QSize(w, h), QImage::Format_ARGB32_Premultiplied); image.fill(Qt::transparent); QRectF targetRect(0, 0, w, h); QPainter painter; painter.begin(&image); painter.setRenderHint(QPainter::Antialiasing); exportPaint(&painter, targetRect, sourceRect, background); painter.end(); - if (!path.isEmpty()) - image.save(path, "PNG"); - else + if (!path.isEmpty()) { + bool rc = image.save(path, "PNG"); + if (!rc) { + RESET_CURSOR; + QMessageBox::critical(nullptr, i18n("Failed to export"), i18n("Failed to write to '%1'. Please check the path.", path)); + } + } else QApplication::clipboard()->setImage(image, QClipboard::Clipboard); } } void WorksheetView::exportToClipboard() { QRectF sourceRect; if (m_selectedItems.size() == 0) sourceRect = scene()->itemsBoundingRect(); else { //export selection for (const auto* item : m_selectedItems) sourceRect = sourceRect.united( item->mapToScene(item->boundingRect()).boundingRect() ); } int w = Worksheet::convertFromSceneUnits(sourceRect.width(), Worksheet::Millimeter); int h = Worksheet::convertFromSceneUnits(sourceRect.height(), Worksheet::Millimeter); w = w*QApplication::desktop()->physicalDpiX()/25.4; h = h*QApplication::desktop()->physicalDpiY()/25.4; QImage image(QSize(w, h), QImage::Format_ARGB32_Premultiplied); image.fill(Qt::transparent); QRectF targetRect(0, 0, w, h); QPainter painter; painter.begin(&image); painter.setRenderHint(QPainter::Antialiasing); exportPaint(&painter, targetRect, sourceRect, true); painter.end(); QApplication::clipboard()->setImage(image, QClipboard::Clipboard); } void WorksheetView::exportPaint(QPainter* painter, const QRectF& targetRect, const QRectF& sourceRect, const bool background) { //draw the background if (background) { painter->save(); painter->scale(targetRect.width()/sourceRect.width(), targetRect.height()/sourceRect.height()); drawBackground(painter, sourceRect); painter->restore(); } //draw the scene items m_worksheet->setPrinting(true); scene()->render(painter, QRectF(), sourceRect); m_worksheet->setPrinting(false); } void WorksheetView::print(QPrinter* printer) { m_worksheet->setPrinting(true); QPainter painter(printer); painter.setRenderHint(QPainter::Antialiasing); // draw background QRectF page_rect = printer->pageRect(); QRectF scene_rect = scene()->sceneRect(); float scale = qMax(scene_rect.width()/page_rect.width(),scene_rect.height()/page_rect.height()); drawBackgroundItems(&painter, QRectF(0,0,scene_rect.width()/scale,scene_rect.height()/scale)); // draw scene scene()->render(&painter); m_worksheet->setPrinting(false); } void WorksheetView::updateBackground() { invalidateScene(sceneRect(), QGraphicsScene::BackgroundLayer); } /*! * called when the layout was changed in Worksheet, * enables the corresponding action */ void WorksheetView::layoutChanged(Worksheet::Layout layout) { if (layout == Worksheet::NoLayout) { verticalLayoutAction->setEnabled(true); verticalLayoutAction->setChecked(false); horizontalLayoutAction->setEnabled(true); horizontalLayoutAction->setChecked(false); gridLayoutAction->setEnabled(true); gridLayoutAction->setChecked(false); breakLayoutAction->setEnabled(false); } else { verticalLayoutAction->setEnabled(false); horizontalLayoutAction->setEnabled(false); gridLayoutAction->setEnabled(false); breakLayoutAction->setEnabled(true); if (layout == Worksheet::VerticalLayout) verticalLayoutAction->setChecked(true); else if (layout == Worksheet::HorizontalLayout) horizontalLayoutAction->setChecked(true); else gridLayoutAction->setChecked(true); } } void WorksheetView::registerShortcuts() { selectAllAction->setShortcut(Qt::CTRL+Qt::Key_A); deleteAction->setShortcut(Qt::Key_Delete); backspaceAction->setShortcut(Qt::Key_Backspace); zoomInViewAction->setShortcut(Qt::CTRL+Qt::Key_Plus); zoomOutViewAction->setShortcut(Qt::CTRL+Qt::Key_Minus); zoomOriginAction->setShortcut(Qt::CTRL+Qt::Key_1); } void WorksheetView::unregisterShortcuts() { selectAllAction->setShortcut(QKeySequence()); deleteAction->setShortcut(QKeySequence()); backspaceAction->setShortcut(QKeySequence()); zoomInViewAction->setShortcut(QKeySequence()); zoomOutViewAction->setShortcut(QKeySequence()); zoomOriginAction->setShortcut(QKeySequence()); } //############################################################################## //######################## SLOTs for cartesian plots ######################## //############################################################################## void WorksheetView::cartesianPlotActionModeChanged(QAction* action) { if (action == cartesianPlotApplyToSelectionAction) m_worksheet->setCartesianPlotActionMode(Worksheet::CartesianPlotActionMode::ApplyActionToSelection); else m_worksheet->setCartesianPlotActionMode(Worksheet::CartesianPlotActionMode::ApplyActionToAll); handleCartesianPlotActions(); } void WorksheetView::cartesianPlotCursorModeChanged(QAction* action) { if (action == cartesianPlotApplyToSelectionCursor) m_worksheet->setCartesianPlotCursorMode(Worksheet::CartesianPlotActionMode::ApplyActionToSelection); else m_worksheet->setCartesianPlotCursorMode(Worksheet::CartesianPlotActionMode::ApplyActionToAll); handleCartesianPlotActions(); } void WorksheetView::plotsLockedActionChanged(bool checked) { m_worksheet->setPlotsLocked(checked); } void WorksheetView::cartesianPlotMouseModeChanged(QAction* action) { if (m_suppressMouseModeChange) return; if (action == cartesianPlotSelectionModeAction) m_cartesianPlotMouseMode = CartesianPlot::SelectionMode; else if (action == cartesianPlotZoomSelectionModeAction) m_cartesianPlotMouseMode = CartesianPlot::ZoomSelectionMode; else if (action == cartesianPlotZoomXSelectionModeAction) m_cartesianPlotMouseMode = CartesianPlot::ZoomXSelectionMode; else if (action == cartesianPlotZoomYSelectionModeAction) m_cartesianPlotMouseMode = CartesianPlot::ZoomYSelectionMode; else if (action == cartesianPlotCursorModeAction) m_cartesianPlotMouseMode = CartesianPlot::Cursor; for (auto* plot : m_worksheet->children() ) plot->setMouseMode(m_cartesianPlotMouseMode); } void WorksheetView::cartesianPlotMouseModeChangedSlot(CartesianPlot::MouseMode mouseMode) { if (!m_menusInitialized) return; m_suppressMouseModeChange = true; if (mouseMode == CartesianPlot::MouseMode::SelectionMode) cartesianPlotSelectionModeAction->setChecked(true); else if (mouseMode == CartesianPlot::MouseMode::ZoomSelectionMode) cartesianPlotZoomSelectionModeAction->setChecked(true); else if (mouseMode == CartesianPlot::MouseMode::ZoomXSelectionMode) cartesianPlotZoomXSelectionModeAction->setChecked(true); else if (mouseMode == CartesianPlot::MouseMode::ZoomYSelectionMode) cartesianPlotZoomYSelectionModeAction->setChecked(true); else if (mouseMode == CartesianPlot::MouseMode::Cursor) cartesianPlotCursorModeAction->setChecked(true); m_suppressMouseModeChange = false; } void WorksheetView::cartesianPlotAddNew(QAction* action) { QVector plots = m_worksheet->children(); if (m_worksheet->cartesianPlotActionMode() == Worksheet::ApplyActionToSelection) { int selectedPlots = 0; for (auto* plot : plots) { if (m_selectedItems.indexOf(plot->graphicsItem()) != -1) ++selectedPlots; else { //current plot is not selected, check if one of its children is selected auto children = plot->children(); for (auto* child : children) { if (m_selectedItems.indexOf(child->graphicsItem()) != -1) { ++selectedPlots; break; } } } } if (selectedPlots > 1) m_worksheet->beginMacro(i18n("%1: Add curve to %2 plots", m_worksheet->name(), selectedPlots)); for (auto* plot : plots) { if (m_selectedItems.indexOf(plot->graphicsItem()) != -1) this->cartesianPlotAdd(plot, action); else { //current plot is not selected, check if one of its children is selected auto children = plot->children(); for (auto* child : children) { if (m_selectedItems.indexOf(child->graphicsItem()) != -1) { this->cartesianPlotAdd(plot, action); break; } } } } if (selectedPlots > 1) m_worksheet->endMacro(); } else { if (plots.size() > 1) m_worksheet->beginMacro(i18n("%1: Add curve to %2 plots", m_worksheet->name(), plots.size())); for (auto* plot : plots) this->cartesianPlotAdd(plot, action); if (plots.size() > 1) m_worksheet->endMacro(); } } void WorksheetView::cartesianPlotAdd(CartesianPlot* plot, QAction* action) { DEBUG("WorksheetView::cartesianPlotAdd()"); if (action == addCurveAction) plot->addCurve(); else if (action == addHistogramAction) plot->addHistogram(); else if (action == addEquationCurveAction) plot->addEquationCurve(); else if (action == addDataReductionCurveAction) plot->addDataReductionCurve(); else if (action == addDifferentiationCurveAction) plot->addDifferentiationCurve(); else if (action == addIntegrationCurveAction) plot->addIntegrationCurve(); else if (action == addInterpolationCurveAction) plot->addInterpolationCurve(); else if (action == addSmoothCurveAction) plot->addSmoothCurve(); else if (action == addFitCurveAction) plot->addFitCurve(); else if (action == addFourierFilterCurveAction) plot->addFourierFilterCurve(); else if (action == addFourierTransformCurveAction) plot->addFourierTransformCurve(); else if (action == addConvolutionCurveAction) plot->addConvolutionCurve(); else if (action == addCorrelationCurveAction) plot->addCorrelationCurve(); else if (action == addLegendAction) plot->addLegend(); else if (action == addHorizontalAxisAction) plot->addHorizontalAxis(); else if (action == addVerticalAxisAction) plot->addVerticalAxis(); else if (action == addPlotTextLabelAction) plot->addTextLabel(); else if (action == addPlotImageAction) plot->addImage(); else if (action == addCustomPointAction) plot->addCustomPoint(); // analysis actions else if (action == addDataReductionAction) plot->addDataReductionCurve(); else if (action == addDifferentiationAction) plot->addDifferentiationCurve(); else if (action == addIntegrationAction) plot->addIntegrationCurve(); else if (action == addInterpolationAction) plot->addInterpolationCurve(); else if (action == addSmoothAction) plot->addSmoothCurve(); else if (action == addFitAction) plot->addFitCurve(); else if (action == addFourierFilterAction) plot->addFourierFilterCurve(); else if (action == addFourierTransformAction) plot->addFourierTransformCurve(); else if (action == addConvolutionAction) plot->addConvolutionCurve(); else if (action == addCorrelationAction) plot->addCorrelationCurve(); } void WorksheetView::cartesianPlotNavigationChanged(QAction* action) { CartesianPlot::NavigationOperation op = (CartesianPlot::NavigationOperation)action->data().toInt(); if (m_worksheet->cartesianPlotActionMode() == Worksheet::ApplyActionToSelection) { for (auto* plot : m_worksheet->children() ) { if (m_selectedItems.indexOf(plot->graphicsItem()) != -1) plot->navigate(op); else { // check if one of the plots childrend is selected. Do the operation there too. for (auto* child : plot->children()) { if (m_selectedItems.indexOf(child->graphicsItem()) != -1) { plot->navigate(op); break; } } } } } else { for (auto* plot : m_worksheet->children() ) plot->navigate(op); } } Worksheet::CartesianPlotActionMode WorksheetView::getCartesianPlotActionMode() { return m_worksheet->cartesianPlotActionMode(); } void WorksheetView::presenterMode() { KConfigGroup group = KSharedConfig::openConfig()->group("Settings_Worksheet"); //show dynamic presenter widget, if enabled if (group.readEntry("PresenterModeInteractive", false)) { auto* dynamicPresenterWidget = new DynamicPresenterWidget(m_worksheet); dynamicPresenterWidget->showFullScreen(); return; } //show static presenter widget (default) QRectF sourceRect(scene()->sceneRect()); int w = Worksheet::convertFromSceneUnits(sourceRect.width(), Worksheet::Millimeter); int h = Worksheet::convertFromSceneUnits(sourceRect.height(), Worksheet::Millimeter); w *= QApplication::desktop()->physicalDpiX()/25.4; h *= QApplication::desktop()->physicalDpiY()/25.4; QRectF targetRect(0, 0, w, h); const QRectF& screenSize = QGuiApplication::primaryScreen()->availableGeometry();; if (targetRect.width() > screenSize.width() || ((targetRect.height() > screenSize.height()))) { const double ratio = qMin(screenSize.width() / targetRect.width(), screenSize.height() / targetRect.height()); targetRect.setWidth(targetRect.width()* ratio); targetRect.setHeight(targetRect.height() * ratio); } QImage image(QSize(targetRect.width(), targetRect.height()), QImage::Format_ARGB32_Premultiplied); image.fill(Qt::transparent); QPainter painter; painter.begin(&image); painter.setRenderHint(QPainter::Antialiasing); exportPaint(&painter, targetRect, sourceRect, true); painter.end(); PresenterWidget* presenterWidget = new PresenterWidget(QPixmap::fromImage(image), m_worksheet->name()); presenterWidget->showFullScreen(); } diff --git a/src/kdefrontend/MainWin.cpp b/src/kdefrontend/MainWin.cpp index 206ebd4de..106db5c0a 100644 --- a/src/kdefrontend/MainWin.cpp +++ b/src/kdefrontend/MainWin.cpp @@ -1,2446 +1,2442 @@ /*************************************************************************** File : MainWin.cc Project : LabPlot Description : Main window of the application -------------------------------------------------------------------- Copyright : (C) 2008-2018 Stefan Gerlach (stefan.gerlach@uni.kn) Copyright : (C) 2009-2020 Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "MainWin.h" #include "backend/core/Project.h" #include "backend/core/Folder.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/Workbook.h" #include "backend/spreadsheet/Spreadsheet.h" #include "backend/matrix/Matrix.h" #include "backend/worksheet/Worksheet.h" #include "backend/datasources/LiveDataSource.h" #include "backend/datasources/DatasetHandler.h" #ifdef HAVE_LIBORIGIN #include "backend/datasources/projects/OriginProjectParser.h" #endif #ifdef HAVE_CANTOR_LIBS #include "backend/cantorWorksheet/CantorWorksheet.h" #endif #include "backend/datapicker/Datapicker.h" #include "backend/note/Note.h" #include "backend/lib/macros.h" #include "backend/worksheet/plots/cartesian/CartesianPlot.h" #ifdef HAVE_MQTT #include "backend/datasources/MQTTClient.h" #endif #include "commonfrontend/core/PartMdiView.h" #include "commonfrontend/ProjectExplorer.h" #include "commonfrontend/matrix/MatrixView.h" #include "commonfrontend/spreadsheet/SpreadsheetView.h" #include "commonfrontend/worksheet/WorksheetView.h" #ifdef HAVE_CANTOR_LIBS #include "commonfrontend/cantorWorksheet/CantorWorksheetView.h" #endif #include "commonfrontend/datapicker/DatapickerView.h" #include "commonfrontend/datapicker/DatapickerImageView.h" #include "commonfrontend/note/NoteView.h" #include "commonfrontend/widgets/MemoryWidget.h" #include "kdefrontend/datasources/ImportFileDialog.h" #include "kdefrontend/datasources/ImportDatasetDialog.h" #include "kdefrontend/datasources/ImportDatasetWidget.h" #include "kdefrontend/datasources/ImportProjectDialog.h" #include "kdefrontend/datasources/ImportSQLDatabaseDialog.h" #include #include "kdefrontend/dockwidgets/ProjectDock.h" #include "kdefrontend/HistoryDialog.h" #include "kdefrontend/SettingsDialog.h" #include "kdefrontend/GuiObserver.h" #include "kdefrontend/widgets/LabelWidget.h" #include "kdefrontend/widgets/FITSHeaderEditDialog.h" #include "DatasetModel.h" // #include "welcomescreen/WelcomeScreenHelper.h" #ifdef HAVE_KUSERFEEDBACK #include #include #include #include #include #include #endif #ifdef Q_OS_MAC #include "3rdparty/kdmactouchbar/src/kdmactouchbar.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include // #include // #include // #include // #include // #include // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_CANTOR_LIBS #include #include #include #include //required to parse Cantor and Jupyter files #include #include #include #include #endif /*! \class MainWin \brief Main application window. \ingroup kdefrontend */ MainWin::MainWin(QWidget *parent, const QString& filename) : KXmlGuiWindow(parent), m_schemeManager(new KColorSchemeManager(this)) { initGUI(filename); setAcceptDrops(true); #ifdef HAVE_KUSERFEEDBACK m_userFeedbackProvider.setProductIdentifier(QStringLiteral("org.kde.labplot")); m_userFeedbackProvider.setFeedbackServer(QUrl(QStringLiteral("https://telemetry.kde.org/"))); m_userFeedbackProvider.setSubmissionInterval(7); m_userFeedbackProvider.setApplicationStartsUntilEncouragement(5); m_userFeedbackProvider.setEncouragementDelay(30); // software version info m_userFeedbackProvider.addDataSource(new KUserFeedback::ApplicationVersionSource); m_userFeedbackProvider.addDataSource(new KUserFeedback::QtVersionSource); // info about the machine m_userFeedbackProvider.addDataSource(new KUserFeedback::PlatformInfoSource); m_userFeedbackProvider.addDataSource(new KUserFeedback::ScreenInfoSource); // usage info m_userFeedbackProvider.addDataSource(new KUserFeedback::StartCountSource); m_userFeedbackProvider.addDataSource(new KUserFeedback::UsageTimeSource); #endif //restore the geometry KConfigGroup group = KSharedConfig::openConfig()->group("MainWin"); restoreGeometry(group.readEntry("geometry", QByteArray())); } MainWin::~MainWin() { //save the recent opened files m_recentProjectsAction->saveEntries( KSharedConfig::openConfig()->group("Recent Files") ); KConfigGroup group = KSharedConfig::openConfig()->group("MainWin"); group.writeEntry("geometry", saveGeometry()); KSharedConfig::openConfig()->sync(); //if welcome screen is shown, save its settings prior to deleting it // if(dynamic_cast(centralWidget())) // QMetaObject::invokeMethod(m_welcomeWidget->rootObject(), "saveWidgetDimensions"); if (m_project) { // if(dynamic_cast(centralWidget()) == nullptr) // m_mdiArea->closeAllSubWindows(); disconnect(m_project, nullptr, this, nullptr); delete m_project; } if (m_aspectTreeModel) delete m_aspectTreeModel; if (m_guiObserver) delete m_guiObserver; // if(m_welcomeScreenHelper) // delete m_welcomeScreenHelper; } void MainWin::showPresenter() { const Worksheet* w = dynamic_cast(m_currentAspect); if (w) { auto* view = static_cast(w->view()); view->presenterMode(); } else { //currently active object is not a worksheet but we're asked to start in the presenter mode //determine the first available worksheet and show it in the presenter mode auto worksheets = m_project->children(); if (worksheets.size() > 0) { auto* view = static_cast(worksheets.constFirst()->view()); view->presenterMode(); } else { QMessageBox::information(this, i18n("Presenter Mode"), i18n("No worksheets are available in the project. The presenter mode will not be started.")); } } } AspectTreeModel* MainWin::model() const { return m_aspectTreeModel; } Project* MainWin::project() const { return m_project; } void MainWin::initGUI(const QString& fileName) { statusBar()->showMessage(i18nc("%1 is the LabPlot version", "Welcome to LabPlot %1", QLatin1String(LVERSION))); initActions(); #ifdef Q_OS_MAC setupGUI(Default, QLatin1String("/Applications/labplot2.app/Contents/Resources/labplot2ui.rc")); m_touchBar = new KDMacTouchBar(this); //m_touchBar->setTouchButtonStyle(KDMacTouchBar::IconOnly); #else setupGUI(Default, KXMLGUIClient::xmlFile()); // should be "labplot2ui.rc" #endif DEBUG("Component name: " << STDSTRING(KXMLGUIClient::componentName())); DEBUG("XML file: " << STDSTRING(KXMLGUIClient::xmlFile()) << " (should be \"labplot2ui.rc\")"); //all toolbars created via the KXMLGUI framework are locked on default: // * on the very first program start, unlock all toolbars // * on later program starts, set stored lock status //Furthermore, we want to show icons only after the first program start. KConfigGroup groupMain = KSharedConfig::openConfig()->group("MainWindow"); if (groupMain.exists()) { //KXMLGUI framework automatically stores "Disabled" for the key "ToolBarsMovable" //in case the toolbars are locked -> load this value const QString& str = groupMain.readEntry(QLatin1String("ToolBarsMovable"), ""); bool locked = (str == QLatin1String("Disabled")); KToolBar::setToolBarsLocked(locked); } else { //first start KToolBar::setToolBarsLocked(false); //show icons only for (auto* container : factory()->containers(QLatin1String("ToolBar"))) { auto* toolbar = dynamic_cast(container); if (toolbar) toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly); } } initMenus(); auto* mainToolBar = qobject_cast(factory()->container("main_toolbar", this)); if (!mainToolBar) { QMessageBox::critical(this, i18n("GUI configuration file not found"), i18n("%1 file was not found. Please check your installation.", KXMLGUIClient::xmlFile())); //TODO: the application is not really usable if the rc file was not found. We should quit the application. The following line crashes //the application because of the splash screen. We need to find another solution. // QMetaObject::invokeMethod(this, "close", Qt::QueuedConnection); //call close as soon as we enter the eventloop return; } auto* tbImport = new QToolButton(mainToolBar); tbImport->setPopupMode(QToolButton::MenuButtonPopup); tbImport->setMenu(m_importMenu); tbImport->setDefaultAction(m_importFileAction); mainToolBar->addWidget(tbImport); qobject_cast(factory()->container("import", this))->setIcon(QIcon::fromTheme("document-import")); setWindowIcon(QIcon::fromTheme("LabPlot2", QGuiApplication::windowIcon())); setAttribute( Qt::WA_DeleteOnClose ); //make the status bar of a fixed size in order to avoid height changes when placing a ProgressBar there. QFont font; font.setFamily(font.defaultFamily()); QFontMetrics fm(font); statusBar()->setFixedHeight(fm.height() + 5); //load recently used projects m_recentProjectsAction->loadEntries( KSharedConfig::openConfig()->group("Recent Files") ); //auto-save KConfigGroup group = KSharedConfig::openConfig()->group("Settings_General"); m_autoSaveActive = group.readEntry("AutoSave", false); int interval = group.readEntry("AutoSaveInterval", 1); interval = interval*60*1000; m_autoSaveTimer.setInterval(interval); connect(&m_autoSaveTimer, &QTimer::timeout, this, &MainWin::autoSaveProject); if (!fileName.isEmpty()) { createMdiArea(); setCentralWidget(m_mdiArea); #ifdef HAVE_LIBORIGIN if (Project::isLabPlotProject(fileName) || OriginProjectParser::isOriginProject(fileName)) { #else if (Project::isLabPlotProject(fileName)) { #endif QTimer::singleShot(0, this, [=] () { openProject(fileName); }); } else { newProject(); QTimer::singleShot(0, this, [=] () { importFileDialog(fileName); }); } } else { //There is no file to open. Depending on the settings do nothing, //create a new project or open the last used project. LoadOnStart load = (LoadOnStart)group.readEntry("LoadOnStart", (int)Nothing); if (load != WelcomeScreen) { createMdiArea(); setCentralWidget(m_mdiArea); if (load == NewProject) //create new project newProject(); else if (load == NewProjectWorksheet) { //create new project with a worksheet newProject(); newWorksheet(); } else if (load == LastProject) { //open last used project if (!m_recentProjectsAction->urls().isEmpty()) { QDEBUG("TO OPEN m_recentProjectsAction->urls() =" << m_recentProjectsAction->urls().constFirst()); openRecentProject( m_recentProjectsAction->urls().constFirst() ); } } updateGUIOnProjectChanges(); } else{ //welcome screen // m_showWelcomeScreen = true; // m_welcomeWidget = createWelcomeScreen(); // setCentralWidget(m_welcomeWidget); } } //show memory info const bool showMemoryInfo = group.readEntry(QLatin1String("ShowMemoryInfo"), true); if (showMemoryInfo) { m_memoryInfoWidget = new MemoryWidget(statusBar()); statusBar()->addPermanentWidget(m_memoryInfoWidget); } } /** * @brief Creates a new welcome screen to be set as central widget. */ /* QQuickWidget* MainWin::createWelcomeScreen() { QSize maxSize = qApp->primaryScreen()->availableSize(); resize(maxSize); setMinimumSize(700, 400); showMaximized(); KToolBar* toolbar = toolBar(); if(toolbar) toolbar->setVisible(false); QList recentList; for (QUrl& url : m_recentProjectsAction->urls()) recentList.append(QVariant(url)); //Set context property QQuickWidget* quickWidget = new QQuickWidget(this); QQmlContext* ctxt = quickWidget->rootContext(); QVariant variant(recentList); ctxt->setContextProperty("recentProjects", variant); //Create helper object if(m_welcomeScreenHelper) delete m_welcomeScreenHelper; m_welcomeScreenHelper = new WelcomeScreenHelper(); connect(m_welcomeScreenHelper, &WelcomeScreenHelper::openExampleProject, this, QOverload::of(&MainWin::openProject)); ctxt->setContextProperty("datasetModel", m_welcomeScreenHelper->getDatasetModel()); ctxt->setContextProperty("helper", m_welcomeScreenHelper); quickWidget->setSource(QUrl(QLatin1String("qrc:///main.qml"))); quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); QObject *item = quickWidget->rootObject(); //connect qml's signals QObject::connect(item, SIGNAL(recentProjectClicked(QUrl)), this, SLOT(openRecentProject(QUrl))); QObject::connect(item, SIGNAL(datasetClicked(QString,QString,QString)), m_welcomeScreenHelper, SLOT(datasetClicked(QString,QString,QString))); QObject::connect(item, SIGNAL(openDataset()), this, SLOT(openDatasetExample())); QObject::connect(item, SIGNAL(openExampleProject(QString)), m_welcomeScreenHelper, SLOT(exampleProjectClicked(QString))); emit m_welcomeScreenHelper->showFirstDataset(); return quickWidget; } */ /** * @brief Initiates resetting the layout of the welcome screen */ /* void MainWin::resetWelcomeScreen() { if(dynamic_cast(centralWidget())) QMetaObject::invokeMethod(m_welcomeWidget->rootObject(), "restoreOriginalLayout"); } */ /** * @brief Creates a new MDI area, to replace the Welcome Screen as central widget */ void MainWin::createMdiArea() { KToolBar* toolbar = toolBar(); if(toolbar) toolbar->setVisible(true); //Save welcome screen's dimensions. // if(m_showWelcomeScreen) // QMetaObject::invokeMethod(m_welcomeWidget->rootObject(), "saveWidgetDimensions"); m_mdiArea = new QMdiArea; setCentralWidget(m_mdiArea); connect(m_mdiArea, &QMdiArea::subWindowActivated, this, &MainWin::handleCurrentSubWindowChanged); //set the view mode of the mdi area KConfigGroup group = KSharedConfig::openConfig()->group( "Settings_General" ); int viewMode = group.readEntry("ViewMode", 0); if (viewMode == 1) { m_mdiArea->setViewMode(QMdiArea::TabbedView); int tabPosition = group.readEntry("TabPosition", 0); m_mdiArea->setTabPosition(QTabWidget::TabPosition(tabPosition)); m_mdiArea->setTabsClosable(true); m_mdiArea->setTabsMovable(true); m_tileWindowsAction->setVisible(false); m_cascadeWindowsAction->setVisible(false); } connect(m_closeWindowAction, &QAction::triggered, m_mdiArea, &QMdiArea::closeActiveSubWindow); connect(m_closeAllWindowsAction, &QAction::triggered, m_mdiArea, &QMdiArea::closeAllSubWindows); connect(m_tileWindowsAction, &QAction::triggered, m_mdiArea, &QMdiArea::tileSubWindows); connect(m_cascadeWindowsAction, &QAction::triggered, m_mdiArea, &QMdiArea::cascadeSubWindows); connect(m_nextWindowAction, &QAction::triggered, m_mdiArea, &QMdiArea::activateNextSubWindow); connect(m_prevWindowAction, &QAction::triggered, m_mdiArea, &QMdiArea::activatePreviousSubWindow); } void MainWin::initActions() { // ******************** File-menu ******************************* //add some standard actions m_newProjectAction = KStandardAction::openNew(this, SLOT(newProject()),actionCollection()); m_openProjectAction = KStandardAction::open(this, SLOT(openProject()),actionCollection()); m_recentProjectsAction = KStandardAction::openRecent(this, SLOT(openRecentProject(QUrl)),actionCollection()); m_closeAction = KStandardAction::close(this, SLOT(closeProject()),actionCollection()); actionCollection()->setDefaultShortcut(m_closeAction, QKeySequence()); //remove the shortcut, QKeySequence::Close will be used for closing sub-windows m_saveAction = KStandardAction::save(this, SLOT(saveProject()),actionCollection()); m_saveAsAction = KStandardAction::saveAs(this, SLOT(saveProjectAs()),actionCollection()); m_printAction = KStandardAction::print(this, SLOT(print()),actionCollection()); m_printPreviewAction = KStandardAction::printPreview(this, SLOT(printPreview()),actionCollection()); //TODO: on Mac OS when going full-screen we get a crash because of an stack-overflow #ifndef Q_OS_MAC KStandardAction::fullScreen(this, SLOT(toggleFullScreen()), this, actionCollection()); #endif //New Folder/Workbook/Spreadsheet/Matrix/Worksheet/Datasources m_newWorkbookAction = new QAction(QIcon::fromTheme("labplot-workbook-new"),i18n("Workbook"),this); actionCollection()->addAction("new_workbook", m_newWorkbookAction); m_newWorkbookAction->setWhatsThis(i18n("Creates a new workbook for collection spreadsheets, matrices and plots")); connect(m_newWorkbookAction, &QAction::triggered, this, &MainWin::newWorkbook); m_newDatapickerAction = new QAction(QIcon::fromTheme("color-picker-black"), i18n("Datapicker"), this); m_newDatapickerAction->setWhatsThis(i18n("Creates a data picker for getting data from a picture")); actionCollection()->addAction("new_datapicker", m_newDatapickerAction); connect(m_newDatapickerAction, &QAction::triggered, this, &MainWin::newDatapicker); m_newSpreadsheetAction = new QAction(QIcon::fromTheme("labplot-spreadsheet-new"),i18n("Spreadsheet"),this); // m_newSpreadsheetAction->setShortcut(Qt::CTRL+Qt::Key_Equal); m_newSpreadsheetAction->setWhatsThis(i18n("Creates a new spreadsheet for data editing")); actionCollection()->addAction("new_spreadsheet", m_newSpreadsheetAction); connect(m_newSpreadsheetAction, &QAction::triggered, this, &MainWin::newSpreadsheet); m_newMatrixAction = new QAction(QIcon::fromTheme("labplot-matrix-new"),i18n("Matrix"),this); // m_newMatrixAction->setShortcut(Qt::CTRL+Qt::Key_Equal); m_newMatrixAction->setWhatsThis(i18n("Creates a new matrix for data editing")); actionCollection()->addAction("new_matrix", m_newMatrixAction); connect(m_newMatrixAction, &QAction::triggered, this, &MainWin::newMatrix); m_newWorksheetAction = new QAction(QIcon::fromTheme("labplot-worksheet-new"),i18n("Worksheet"),this); // m_newWorksheetAction->setShortcut(Qt::ALT+Qt::Key_X); m_newWorksheetAction->setWhatsThis(i18n("Creates a new worksheet for data plotting")); actionCollection()->addAction("new_worksheet", m_newWorksheetAction); connect(m_newWorksheetAction, &QAction::triggered, this, &MainWin::newWorksheet); m_newNotesAction = new QAction(QIcon::fromTheme("document-new"),i18n("Note"),this); m_newNotesAction->setWhatsThis(i18n("Creates a new note for arbitrary text")); actionCollection()->addAction("new_notes", m_newNotesAction); connect(m_newNotesAction, &QAction::triggered, this, &MainWin::newNotes); // m_newScriptAction = new QAction(QIcon::fromTheme("insert-text"),i18n("Note/Script"),this); // actionCollection()->addAction("new_script", m_newScriptAction); // connect(m_newScriptAction, &QAction::triggered,SLOT(newScript())); m_newFolderAction = new QAction(QIcon::fromTheme("folder-new"),i18n("Folder"),this); m_newFolderAction->setWhatsThis(i18n("Creates a new folder to collect sheets and other elements")); actionCollection()->addAction("new_folder", m_newFolderAction); connect(m_newFolderAction, &QAction::triggered, this, &MainWin::newFolder); //"New file datasources" m_newLiveDataSourceAction = new QAction(QIcon::fromTheme("application-octet-stream"),i18n("Live Data Source"),this); m_newLiveDataSourceAction->setWhatsThis(i18n("Creates a live data source to read data from a real time device")); actionCollection()->addAction("new_live_datasource", m_newLiveDataSourceAction); connect(m_newLiveDataSourceAction, &QAction::triggered, this, &MainWin::newLiveDataSourceActionTriggered); //Import/Export m_importFileAction = new QAction(QIcon::fromTheme("document-import"), i18n("Import from File"), this); actionCollection()->setDefaultShortcut(m_importFileAction, Qt::CTRL+Qt::SHIFT+Qt::Key_I); m_importFileAction->setWhatsThis(i18n("Import data from a regular file")); actionCollection()->addAction("import_file", m_importFileAction); connect(m_importFileAction, &QAction::triggered, this, [=]() {importFileDialog();}); m_importSqlAction = new QAction(QIcon::fromTheme("network-server-database"), i18n("From SQL Database"), this); m_importSqlAction->setWhatsThis(i18n("Import data from a SQL database")); actionCollection()->addAction("import_sql", m_importSqlAction); connect(m_importSqlAction, &QAction::triggered, this, &MainWin::importSqlDialog); m_importDatasetAction = new QAction(QIcon::fromTheme(QLatin1String("database-index")), i18n("From Dataset Collection"), this); m_importDatasetAction->setWhatsThis(i18n("Imports data from an online dataset")); actionCollection()->addAction("import_dataset_datasource", m_importDatasetAction); connect(m_importDatasetAction, &QAction::triggered, this, &MainWin::importDatasetDialog); m_importLabPlotAction = new QAction(QIcon::fromTheme("project-open"), i18n("LabPlot Project"), this); m_importLabPlotAction->setWhatsThis(i18n("Import a project from a LabPlot project file (.lml)")); actionCollection()->addAction("import_labplot", m_importLabPlotAction); connect(m_importLabPlotAction, &QAction::triggered, this, &MainWin::importProjectDialog); #ifdef HAVE_LIBORIGIN m_importOpjAction = new QAction(QIcon::fromTheme("project-open"), i18n("Origin Project (OPJ)"), this); m_importOpjAction->setWhatsThis(i18n("Import a project from an OriginLab Origin project file (.opj)")); actionCollection()->addAction("import_opj", m_importOpjAction); connect(m_importOpjAction, &QAction::triggered, this, &MainWin::importProjectDialog); #endif m_exportAction = new QAction(QIcon::fromTheme("document-export"), i18n("Export"), this); m_exportAction->setWhatsThis(i18n("Export selected element")); actionCollection()->setDefaultShortcut(m_exportAction, Qt::CTRL+Qt::SHIFT+Qt::Key_E); actionCollection()->addAction("export", m_exportAction); connect(m_exportAction, &QAction::triggered, this, &MainWin::exportDialog); m_editFitsFileAction = new QAction(QIcon::fromTheme("editor"), i18n("FITS Metadata Editor"), this); m_editFitsFileAction->setWhatsThis(i18n("Open editor to edit FITS meta data")); actionCollection()->addAction("edit_fits", m_editFitsFileAction); connect(m_editFitsFileAction, &QAction::triggered, this, &MainWin::editFitsFileDialog); // Edit //Undo/Redo-stuff m_undoAction = KStandardAction::undo(this, SLOT(undo()), actionCollection()); m_redoAction = KStandardAction::redo(this, SLOT(redo()), actionCollection()); m_historyAction = new QAction(QIcon::fromTheme("view-history"), i18n("Undo/Redo History"),this); actionCollection()->addAction("history", m_historyAction); connect(m_historyAction, &QAction::triggered, this, &MainWin::historyDialog); #ifdef Q_OS_MAC m_undoIconOnlyAction = new QAction(m_undoAction->icon(), QString()); connect(m_undoIconOnlyAction, &QAction::triggered, this, &MainWin::undo); m_redoIconOnlyAction = new QAction(m_redoAction->icon(), QString()); connect(m_redoIconOnlyAction, &QAction::triggered, this, &MainWin::redo); #endif // TODO: more menus // Appearance // Analysis: see WorksheetView.cpp // Drawing // Script //Windows m_closeWindowAction = new QAction(i18n("&Close"), this); actionCollection()->setDefaultShortcut(m_closeWindowAction, QKeySequence::Close); m_closeWindowAction->setStatusTip(i18n("Close the active window")); actionCollection()->addAction("close window", m_closeWindowAction); m_closeAllWindowsAction = new QAction(i18n("Close &All"), this); m_closeAllWindowsAction->setStatusTip(i18n("Close all the windows")); actionCollection()->addAction("close all windows", m_closeAllWindowsAction); m_tileWindowsAction = new QAction(i18n("&Tile"), this); m_tileWindowsAction->setStatusTip(i18n("Tile the windows")); actionCollection()->addAction("tile windows", m_tileWindowsAction); m_cascadeWindowsAction = new QAction(i18n("&Cascade"), this); m_cascadeWindowsAction->setStatusTip(i18n("Cascade the windows")); actionCollection()->addAction("cascade windows", m_cascadeWindowsAction); m_nextWindowAction = new QAction(QIcon::fromTheme("go-next-view"), i18n("Ne&xt"), this); actionCollection()->setDefaultShortcut(m_nextWindowAction, QKeySequence::NextChild); m_nextWindowAction->setStatusTip(i18n("Move the focus to the next window")); actionCollection()->addAction("next window", m_nextWindowAction); m_prevWindowAction = new QAction(QIcon::fromTheme("go-previous-view"), i18n("Pre&vious"), this); actionCollection()->setDefaultShortcut(m_prevWindowAction, QKeySequence::PreviousChild); m_prevWindowAction->setStatusTip(i18n("Move the focus to the previous window")); actionCollection()->addAction("previous window", m_prevWindowAction); //"Standard actions" KStandardAction::preferences(this, SLOT(settingsDialog()), actionCollection()); KStandardAction::quit(this, SLOT(close()), actionCollection()); //Actions for window visibility auto* windowVisibilityActions = new QActionGroup(this); windowVisibilityActions->setExclusive(true); m_visibilityFolderAction = new QAction(QIcon::fromTheme("folder"), i18n("Current &Folder Only"), windowVisibilityActions); m_visibilityFolderAction->setCheckable(true); m_visibilityFolderAction->setData(Project::folderOnly); m_visibilitySubfolderAction = new QAction(QIcon::fromTheme("folder-documents"), i18n("Current Folder and &Subfolders"), windowVisibilityActions); m_visibilitySubfolderAction->setCheckable(true); m_visibilitySubfolderAction->setData(Project::folderAndSubfolders); m_visibilityAllAction = new QAction(i18n("&All"), windowVisibilityActions); m_visibilityAllAction->setCheckable(true); m_visibilityAllAction->setData(Project::allMdiWindows); connect(windowVisibilityActions, &QActionGroup::triggered, this, &MainWin::setMdiWindowVisibility); //Actions for hiding/showing the dock widgets auto* docksActions = new QActionGroup(this); docksActions->setExclusive(false); m_toggleProjectExplorerDockAction = new QAction(QIcon::fromTheme("view-list-tree"), i18n("Project Explorer"), docksActions); m_toggleProjectExplorerDockAction->setCheckable(true); m_toggleProjectExplorerDockAction->setChecked(true); actionCollection()->addAction("toggle_project_explorer_dock", m_toggleProjectExplorerDockAction); m_togglePropertiesDockAction = new QAction(QIcon::fromTheme("view-list-details"), i18n("Properties Explorer"), docksActions); m_togglePropertiesDockAction->setCheckable(true); m_togglePropertiesDockAction->setChecked(true); actionCollection()->addAction("toggle_properties_explorer_dock", m_togglePropertiesDockAction); connect(docksActions, &QActionGroup::triggered, this, &MainWin::toggleDockWidget); //global search QAction* searchAction = new QAction(actionCollection()); searchAction->setShortcut(QKeySequence::Find); connect(searchAction, &QAction::triggered, this, [=]() { if (m_project) { if (!m_projectExplorerDock->isVisible()) { m_toggleProjectExplorerDockAction->setChecked(true); toggleDockWidget(m_toggleProjectExplorerDockAction); } m_projectExplorer->search(); } }); this->addAction(searchAction); } void MainWin::initMenus() { //menu in the main toolbar for adding new aspects auto* menu = dynamic_cast(factory()->container("new", this)); menu->setIcon(QIcon::fromTheme("window-new")); //menu in the project explorer and in the toolbar for adding new aspects m_newMenu = new QMenu(i18n("Add New"), this); m_newMenu->setIcon(QIcon::fromTheme("window-new")); m_newMenu->addAction(m_newFolderAction); m_newMenu->addAction(m_newWorkbookAction); m_newMenu->addAction(m_newSpreadsheetAction); m_newMenu->addAction(m_newMatrixAction); m_newMenu->addAction(m_newWorksheetAction); m_newMenu->addAction(m_newNotesAction); m_newMenu->addAction(m_newDatapickerAction); m_newMenu->addSeparator(); m_newMenu->addAction(m_newLiveDataSourceAction); //import menu m_importMenu = new QMenu(this); m_importMenu->setIcon(QIcon::fromTheme("document-import")); m_importMenu ->addAction(m_importFileAction); m_importMenu ->addAction(m_importSqlAction); m_importMenu->addAction(m_importDatasetAction); m_importMenu->addSeparator(); m_importMenu->addAction(m_importLabPlotAction); #ifdef HAVE_LIBORIGIN m_importMenu ->addAction(m_importOpjAction); #endif #ifdef HAVE_CANTOR_LIBS m_newMenu->addSeparator(); m_newCantorWorksheetMenu = new QMenu(i18n("CAS Worksheet"), this); m_newCantorWorksheetMenu->setIcon(QIcon::fromTheme("archive-insert")); //"Adding Cantor backends to menu and context menu" QStringList m_availableBackend = Cantor::Backend::listAvailableBackends(); if (m_availableBackend.count() > 0) { unplugActionList(QLatin1String("backends_list")); QList newBackendActions; for (auto* backend : Cantor::Backend::availableBackends()) { if (!backend->isEnabled()) continue; QAction* action = new QAction(QIcon::fromTheme(backend->icon()), backend->name(), this); action->setData(backend->name()); newBackendActions << action; m_newCantorWorksheetMenu->addAction(action); } connect(m_newCantorWorksheetMenu, &QMenu::triggered, this, &MainWin::newCantorWorksheet); plugActionList(QLatin1String("backends_list"), newBackendActions); } m_newMenu->addMenu(m_newCantorWorksheetMenu); #else delete this->guiFactory()->container("cas_worksheet", this); delete this->guiFactory()->container("new_cas_worksheet", this); delete this->guiFactory()->container("cas_worksheet_toolbar", this); #endif //menu subwindow visibility policy m_visibilityMenu = new QMenu(i18n("Window Visibility Policy"), this); m_visibilityMenu->setIcon(QIcon::fromTheme("window-duplicate")); m_visibilityMenu ->addAction(m_visibilityFolderAction); m_visibilityMenu ->addAction(m_visibilitySubfolderAction); m_visibilityMenu ->addAction(m_visibilityAllAction); //menu for editing files m_editMenu = new QMenu(i18n("Edit"), this); m_editMenu->addAction(m_editFitsFileAction); //set the action for the current color scheme checked KConfigGroup group = KSharedConfig::openConfig()->group(QLatin1String("Settings_General")); #if KCONFIGWIDGETS_VERSION > QT_VERSION_CHECK(5,67,0) // Since version 5.67 KColorSchemeManager has a system default option QString schemeName = group.readEntry("ColorScheme"); #else KConfigGroup generalGlobalsGroup = KSharedConfig::openConfig(QLatin1String("kdeglobals"))->group("General"); QString defaultSchemeName = generalGlobalsGroup.readEntry("ColorScheme", QStringLiteral("Breeze")); QString schemeName = group.readEntry("ColorScheme", defaultSchemeName); #endif KActionMenu* schemesMenu = m_schemeManager->createSchemeSelectionMenu(i18n("Color Scheme"), schemeName, this); schemesMenu->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-color"))); QMenu* settingsMenu = dynamic_cast(factory()->container("settings", this)); if (settingsMenu) settingsMenu->insertMenu(settingsMenu->actions().constFirst(), schemesMenu->menu()); connect(schemesMenu->menu(), &QMenu::triggered, this, &MainWin::colorSchemeChanged); #ifdef HAVE_CANTOR_LIBS QAction* action = new QAction(QIcon::fromTheme(QLatin1String("cantor")), i18n("Configure CAS"), this); connect(action, &QAction::triggered, this, &MainWin::cantorSettingsDialog); if (settingsMenu) settingsMenu->addAction(action); #endif } void MainWin::colorSchemeChanged(QAction* action) { QString schemeName = KLocalizedString::removeAcceleratorMarker(action->text()); //background of the mdi area is not updated on theme changes, do it here. QModelIndex index = m_schemeManager->indexForScheme(schemeName); const QPalette& palette = KColorScheme::createApplicationPalette( KSharedConfig::openConfig(index.data(Qt::UserRole).toString()) ); const QBrush& brush = palette.brush(QPalette::Dark); m_mdiArea->setBackground(brush); //save the selected color scheme KConfigGroup group = KSharedConfig::openConfig()->group(QLatin1String("Settings_General")); group.writeEntry("ColorScheme", schemeName); group.sync(); } /*! Asks to save the project if it was modified. \return \c true if the project still needs to be saved ("cancel" clicked), \c false otherwise. */ bool MainWin::warnModified() { if (m_project->hasChanged()) { int want_save = KMessageBox::warningYesNoCancel( this, i18n("The current project %1 has been modified. Do you want to save it?", m_project->name()), i18n("Save Project")); switch (want_save) { case KMessageBox::Yes: return !saveProject(); case KMessageBox::No: break; case KMessageBox::Cancel: return true; } } return false; } /*! * updates the state of actions, menus and toolbars (enabled or disabled) * on project changes (project closes and opens) */ void MainWin::updateGUIOnProjectChanges() { if (m_closing) return; KXMLGUIFactory* factory = this->guiFactory(); if (factory->container("worksheet", this) == nullptr) { //no worksheet menu found, most probably labplot2ui.rc //was not properly installed -> return here in order not to crash return; } //disable all menus if there is no project bool b = (m_project == nullptr); m_saveAction->setEnabled(!b); m_saveAsAction->setEnabled(!b); m_printAction->setEnabled(!b); m_printPreviewAction->setEnabled(!b); m_importFileAction->setEnabled(!b); m_importSqlAction->setEnabled(!b); #ifdef HAVE_LIBORIGIN m_importOpjAction->setEnabled(!b); #endif m_exportAction->setEnabled(!b); m_newWorkbookAction->setEnabled(!b); m_newSpreadsheetAction->setEnabled(!b); m_newMatrixAction->setEnabled(!b); m_newWorksheetAction->setEnabled(!b); m_newDatapickerAction->setEnabled(!b); m_closeAction->setEnabled(!b); m_toggleProjectExplorerDockAction->setEnabled(!b); m_togglePropertiesDockAction->setEnabled(!b); if (!m_mdiArea || !m_mdiArea->currentSubWindow()) { factory->container("spreadsheet", this)->setEnabled(false); factory->container("matrix", this)->setEnabled(false); factory->container("worksheet", this)->setEnabled(false); factory->container("analysis", this)->setEnabled(false); factory->container("datapicker", this)->setEnabled(false); factory->container("spreadsheet_toolbar", this)->hide(); factory->container("worksheet_toolbar", this)->hide(); factory->container("cartesian_plot_toolbar", this)->hide(); // factory->container("histogram_toolbar",this)->hide(); // factory->container("barchart_toolbar",this)->hide(); factory->container("datapicker_toolbar", this)->hide(); #ifdef HAVE_CANTOR_LIBS factory->container("cas_worksheet", this)->setEnabled(false); factory->container("cas_worksheet_toolbar", this)->hide(); #endif } factory->container("new", this)->setEnabled(!b); factory->container("edit", this)->setEnabled(!b); factory->container("import", this)->setEnabled(!b); if (b) setCaption("LabPlot2"); else setCaption(m_project->name()); #ifdef Q_OS_MAC m_touchBar->clear(); if (b){ m_touchBar->addAction(m_newProjectAction); m_touchBar->addAction(m_openProjectAction); } else { m_touchBar->addAction(m_importFileAction); m_touchBar->addAction(m_newWorksheetAction); m_touchBar->addAction(m_newSpreadsheetAction); m_touchBar->addAction(m_newMatrixAction); } #endif // undo/redo actions are disabled in both cases - when the project is closed or opened m_undoAction->setEnabled(false); m_redoAction->setEnabled(false); } /* * updates the state of actions, menus and toolbars (enabled or disabled) * depending on the currently active window (worksheet or spreadsheet). */ void MainWin::updateGUI() { if (m_project == nullptr || m_project->isLoading()) return; if (m_closing || m_projectClosing) return; KXMLGUIFactory* factory = this->guiFactory(); if (factory->container("worksheet", this) == nullptr) { //no worksheet menu found, most probably labplot2ui.rc //was not properly installed -> return here in order not to crash return; } //reset the touchbar #ifdef Q_OS_MAC m_touchBar->clear(); m_touchBar->addAction(m_undoIconOnlyAction); m_touchBar->addAction(m_redoIconOnlyAction); m_touchBar->addSeparator(); #endif if (!m_mdiArea || !m_mdiArea->currentSubWindow()) { factory->container("spreadsheet", this)->setEnabled(false); factory->container("matrix", this)->setEnabled(false); factory->container("worksheet", this)->setEnabled(false); factory->container("analysis", this)->setEnabled(false); factory->container("datapicker", this)->setEnabled(false); factory->container("spreadsheet_toolbar", this)->hide(); factory->container("worksheet_toolbar", this)->hide(); // factory->container("histogram_toolbar",this)->hide(); // factory->container("barchart_toolbar",this)->hide(); factory->container("cartesian_plot_toolbar", this)->hide(); factory->container("datapicker_toolbar", this)->hide(); #ifdef HAVE_CANTOR_LIBS factory->container("cas_worksheet", this)->setEnabled(false); factory->container("cas_worksheet_toolbar", this)->hide(); #endif return; } #ifdef Q_OS_MAC if (dynamic_cast(m_currentAspect)) { m_touchBar->addAction(m_newWorksheetAction); m_touchBar->addAction(m_newSpreadsheetAction); m_touchBar->addAction(m_newMatrixAction); } #endif //Handle the Worksheet-object const Worksheet* w = dynamic_cast(m_currentAspect); if (!w) w = dynamic_cast(m_currentAspect->parent(AspectType::Worksheet)); if (w) { //populate worksheet menu auto* view = qobject_cast(w->view()); auto* menu = qobject_cast(factory->container("worksheet", this)); menu->clear(); view->createContextMenu(menu); menu->setEnabled(true); //populate analysis menu menu = qobject_cast(factory->container("analysis", this)); menu->clear(); view->createAnalysisMenu(menu); menu->setEnabled(true); //populate worksheet-toolbar auto* toolbar = qobject_cast(factory->container("worksheet_toolbar", this)); toolbar->clear(); view->fillToolBar(toolbar); toolbar->setVisible(true); toolbar->setEnabled(true); //populate the toolbar for cartesian plots toolbar = qobject_cast(factory->container("cartesian_plot_toolbar", this)); toolbar->clear(); view->fillCartesianPlotToolBar(toolbar); toolbar->setVisible(true); toolbar->setEnabled(true); //populate the touchbar on Mac #ifdef Q_OS_MAC view->fillTouchBar(m_touchBar); #endif //hide the spreadsheet toolbar factory->container("spreadsheet_toolbar", this)->setVisible(false); } else { factory->container("worksheet", this)->setEnabled(false); factory->container("worksheet_toolbar", this)->setVisible(false); factory->container("analysis", this)->setEnabled(false); // factory->container("drawing", this)->setEnabled(false); factory->container("worksheet_toolbar", this)->setEnabled(false); factory->container("cartesian_plot_toolbar", this)->setEnabled(false); } //Handle the Spreadsheet-object const auto* spreadsheet = this->activeSpreadsheet(); if (!spreadsheet) spreadsheet = dynamic_cast(m_currentAspect->parent(AspectType::Spreadsheet)); if (spreadsheet) { //populate spreadsheet-menu auto* view = qobject_cast(spreadsheet->view()); auto* menu = qobject_cast(factory->container("spreadsheet", this)); menu->clear(); view->createContextMenu(menu); menu->setEnabled(true); //populate spreadsheet-toolbar auto* toolbar = qobject_cast(factory->container("spreadsheet_toolbar", this)); toolbar->clear(); view->fillToolBar(toolbar); toolbar->setVisible(true); toolbar->setEnabled(true); //populate the touchbar on Mac #ifdef Q_OS_MAC m_touchBar->addAction(m_importFileAction); view->fillTouchBar(m_touchBar); #endif } else { factory->container("spreadsheet", this)->setEnabled(false); factory->container("spreadsheet_toolbar", this)->setVisible(false); } //Handle the Matrix-object const Matrix* matrix = dynamic_cast(m_currentAspect); if (!matrix) matrix = dynamic_cast(m_currentAspect->parent(AspectType::Matrix)); if (matrix) { //populate matrix-menu auto* view = qobject_cast(matrix->view()); auto* menu = qobject_cast(factory->container("matrix", this)); menu->clear(); view->createContextMenu(menu); menu->setEnabled(true); //populate the touchbar on Mac #ifdef Q_OS_MAC m_touchBar->addAction(m_importFileAction); //view->fillTouchBar(m_touchBar); #endif } else factory->container("matrix", this)->setEnabled(false); #ifdef HAVE_CANTOR_LIBS const CantorWorksheet* cantorworksheet = dynamic_cast(m_currentAspect); if (!cantorworksheet) cantorworksheet = dynamic_cast(m_currentAspect->parent(AspectType::CantorWorksheet)); if (cantorworksheet) { auto* view = qobject_cast(cantorworksheet->view()); auto* menu = qobject_cast(factory->container("cas_worksheet", this)); menu->clear(); view->createContextMenu(menu); menu->setEnabled(true); auto* toolbar = qobject_cast(factory->container("cas_worksheet_toolbar", this)); toolbar->setVisible(true); toolbar->clear(); view->fillToolBar(toolbar); } else { //no Cantor worksheet selected -> deactivate Cantor worksheet related menu and toolbar factory->container("cas_worksheet", this)->setEnabled(false); factory->container("cas_worksheet_toolbar", this)->setVisible(false); } #endif const Datapicker* datapicker = dynamic_cast(m_currentAspect); if (!datapicker) datapicker = dynamic_cast(m_currentAspect->parent(AspectType::Datapicker)); if (!datapicker) { if (m_currentAspect->type() == AspectType::DatapickerCurve) datapicker = dynamic_cast(m_currentAspect->parentAspect()); } if (datapicker) { //populate datapicker-menu auto* view = qobject_cast(datapicker->view()); auto* menu = qobject_cast(factory->container("datapicker", this)); menu->clear(); view->createContextMenu(menu); menu->setEnabled(true); //populate spreadsheet-toolbar auto* toolbar = qobject_cast(factory->container("datapicker_toolbar", this)); toolbar->clear(); view->fillToolBar(toolbar); toolbar->setVisible(true); } else { factory->container("datapicker", this)->setEnabled(false); factory->container("datapicker_toolbar", this)->setVisible(false); } } /*! creates a new empty project. Returns \c true, if a new project was created. */ bool MainWin::newProject() { //close the current project, if available if (!closeProject()) return false; // if(dynamic_cast(centralWidget())) { // createMdiArea(); // setCentralWidget(m_mdiArea); // } QApplication::processEvents(QEventLoop::AllEvents, 100); if (m_project) delete m_project; if (m_aspectTreeModel) delete m_aspectTreeModel; m_project = new Project(); m_currentAspect = m_project; m_currentFolder = m_project; KConfigGroup group = KSharedConfig::openConfig()->group( "Settings_General" ); Project::MdiWindowVisibility vis = Project::MdiWindowVisibility(group.readEntry("MdiWindowVisibility", 0)); m_project->setMdiWindowVisibility( vis ); if (vis == Project::folderOnly) m_visibilityFolderAction->setChecked(true); else if (vis == Project::folderAndSubfolders) m_visibilitySubfolderAction->setChecked(true); else m_visibilityAllAction->setChecked(true); m_aspectTreeModel = new AspectTreeModel(m_project, this); connect(m_aspectTreeModel, &AspectTreeModel::statusInfo, [=](const QString& text){ statusBar()->showMessage(text); }); //newProject is called for the first time, there is no project explorer yet //-> initialize the project explorer, the GUI-observer and the dock widgets. if (m_projectExplorer == nullptr) { m_projectExplorerDock = new QDockWidget(this); m_projectExplorerDock->setObjectName("projectexplorer"); m_projectExplorerDock->setWindowTitle(i18nc("@title:window", "Project Explorer")); addDockWidget(Qt::LeftDockWidgetArea, m_projectExplorerDock); m_projectExplorer = new ProjectExplorer(m_projectExplorerDock); m_projectExplorerDock->setWidget(m_projectExplorer); connect(m_projectExplorer, &ProjectExplorer::currentAspectChanged, this, &MainWin::handleCurrentAspectChanged); connect(m_projectExplorerDock, &QDockWidget::visibilityChanged, this, &MainWin::projectExplorerDockVisibilityChanged); //Properties dock m_propertiesDock = new QDockWidget(this); m_propertiesDock->setObjectName("aspect_properties_dock"); m_propertiesDock->setWindowTitle(i18nc("@title:window", "Properties")); addDockWidget(Qt::RightDockWidgetArea, m_propertiesDock); auto* sa = new QScrollArea(m_propertiesDock); stackedWidget = new QStackedWidget(sa); sa->setWidget(stackedWidget); sa->setWidgetResizable(true); m_propertiesDock->setWidget(sa); connect(m_propertiesDock, &QDockWidget::visibilityChanged, this, &MainWin::propertiesDockVisibilityChanged); //GUI-observer; m_guiObserver = new GuiObserver(this); } m_projectExplorer->setModel(m_aspectTreeModel); m_projectExplorer->setProject(m_project); m_projectExplorer->setCurrentAspect(m_project); m_projectExplorerDock->show(); m_propertiesDock->show(); updateGUIOnProjectChanges(); connect(m_project, &Project::aspectAdded, this, &MainWin::handleAspectAdded); connect(m_project, &Project::aspectRemoved, this, &MainWin::handleAspectRemoved); connect(m_project, &Project::aspectAboutToBeRemoved, this, &MainWin::handleAspectAboutToBeRemoved); connect(m_project, SIGNAL(statusInfo(QString)), statusBar(), SLOT(showMessage(QString))); connect(m_project, &Project::changed, this, &MainWin::projectChanged); connect(m_project, &Project::requestProjectContextMenu, this, &MainWin::createContextMenu); connect(m_project, &Project::requestFolderContextMenu, this, &MainWin::createFolderContextMenu); connect(m_project, &Project::mdiWindowVisibilityChanged, this, &MainWin::updateMdiWindowVisibility); connect(m_project, &Project::closeRequested, this, &MainWin::closeProject); m_undoViewEmptyLabel = i18n("%1: created", m_project->name()); setCaption(m_project->name()); return true; } void MainWin::openProject() { KConfigGroup conf(KSharedConfig::openConfig(), "MainWin"); const QString& dir = conf.readEntry("LastOpenDir", ""); QString extensions = i18n("LabPlot Projects (%1)", Project::supportedExtensions()); #ifdef HAVE_LIBORIGIN extensions += i18n(";;Origin Projects (%1)", OriginProjectParser::supportedExtensions()); #endif #ifdef HAVE_CANTOR_LIBS extensions += i18n(";;Cantor Projects (.cws)"); extensions += i18n(";;Jupyter Notebooks (.ipynb)"); #endif const QString& path = QFileDialog::getOpenFileName(this,i18n("Open Project"), dir, extensions); if (path.isEmpty())// "Cancel" was clicked return; this->openProject(path); //save new "last open directory" int pos = path.lastIndexOf(QDir::separator()); if (pos != -1) { const QString& newDir = path.left(pos); if (newDir != dir) conf.writeEntry("LastOpenDir", newDir); } } void MainWin::openProject(const QString& filename) { if (filename == m_currentFileName) { KMessageBox::information(this, i18n("The project file %1 is already opened.", filename), i18n("Open Project")); return; } // if(dynamic_cast(centralWidget())) { // createMdiArea(); // setCentralWidget(m_mdiArea); // } if (!newProject()) return; WAIT_CURSOR; QElapsedTimer timer; timer.start(); bool rc = false; if (Project::isLabPlotProject(filename)) { m_project->setFileName(filename); rc = m_project->load(filename); } #ifdef HAVE_LIBORIGIN else if (OriginProjectParser::isOriginProject(filename)) { OriginProjectParser parser; parser.setProjectFileName(filename); parser.importTo(m_project, QStringList()); //TODO: add return code rc = true; } #endif #ifdef HAVE_CANTOR_LIBS else if (QFileInfo(filename).completeSuffix() == QLatin1String("cws")) { QFile file(filename); KZip archive(&file); rc = archive.open(QIODevice::ReadOnly); if (rc) { const auto* contentEntry = archive.directory()->entry(QLatin1String("content.xml")); if (contentEntry && contentEntry->isFile()) { const auto* contentFile = static_cast(contentEntry); QByteArray data = contentFile->data(); archive.close(); //determine the name of the backend QDomDocument doc; doc.setContent(data); QString backendName = doc.documentElement().attribute(QLatin1String("backend")); if (!backendName.isEmpty()) { //create new Cantor worksheet and load the data auto* worksheet = new CantorWorksheet(backendName); worksheet->setName(QFileInfo(filename).fileName()); worksheet->setComment(filename); rc = file.open(QIODevice::ReadOnly); if (rc) { QByteArray content = file.readAll(); rc = worksheet->init(&content); if (rc) m_project->addChild(worksheet); else { delete worksheet; RESET_CURSOR; QMessageBox::critical(this, i18n("Failed to open project"), i18n("Failed to process the content of the file '%1'.", filename)); } }else { RESET_CURSOR; QMessageBox::critical(this, i18n("Failed to open project"), i18n("Failed to open the file '%1'.", filename)); } } else { RESET_CURSOR; rc = false; QMessageBox::critical(this, i18n("Failed to open project"), i18n("Failed to process the content of the file '%1'.", filename)); } } else { RESET_CURSOR; rc = false; QMessageBox::critical(this, i18n("Failed to open project"), i18n("Failed to process the content of the file '%1'.", filename)); } } else { RESET_CURSOR; QMessageBox::critical(this, i18n("Failed to open project"), i18n("Failed to open the file '%1'.", filename)); } } else if (QFileInfo(filename).completeSuffix() == QLatin1String("ipynb")) { QFile file(filename); rc = file.open(QIODevice::ReadOnly); if (rc) { QByteArray content = file.readAll(); QJsonParseError error; // TODO: use QJsonDocument& doc = QJsonDocument::fromJson(content, &error); if minimum Qt version is at least 5.10 const QJsonDocument& jsonDoc = QJsonDocument::fromJson(content, &error); const QJsonObject& doc = jsonDoc.object(); if (error.error == QJsonParseError::NoError) { //determine the backend name QString backendName; // TODO: use doc["metadata"]["kernelspec"], etc. if minimum Qt version is at least 5.10 if ((doc["metadata"] != QJsonValue::Undefined && doc["metadata"].isObject()) && (doc["metadata"].toObject()["kernelspec"] != QJsonValue::Undefined && doc["metadata"].toObject()["kernelspec"].isObject()) ) { QString kernel; if (doc["metadata"].toObject()["kernelspec"].toObject()["name"] != QJsonValue::Undefined) kernel = doc["metadata"].toObject()["kernelspec"].toObject()["name"].toString(); if (!kernel.isEmpty()) { if (kernel.startsWith(QLatin1String("julia"))) backendName = QLatin1String("julia"); else if (kernel == QLatin1String("sagemath")) backendName = QLatin1String("sage"); else if (kernel == QLatin1String("ir")) backendName = QLatin1String("r"); else backendName = kernel; } else backendName = doc["metadata"].toObject()["kernelspec"].toObject()["language"].toString(); if (!backendName.isEmpty()) { //create new Cantor worksheet and load the data auto* worksheet = new CantorWorksheet(backendName); worksheet->setName(QFileInfo(filename).fileName()); worksheet->setComment(filename); rc = worksheet->init(&content); if (rc) m_project->addChild(worksheet); else { delete worksheet; RESET_CURSOR; QMessageBox::critical(this, i18n("Failed to open project"), i18n("Failed to process the content of the file '%1'.", filename)); } } else { RESET_CURSOR; rc = false; QMessageBox::critical(this, i18n("Failed to open project"), i18n("Failed to process the content of the file '%1'.", filename)); } } else { RESET_CURSOR; rc = false; QMessageBox::critical(this, i18n("Failed to open project"), i18n("Failed to process the content of the file '%1'.", filename)); } } } else { RESET_CURSOR; rc = false; QMessageBox::critical(this, i18n("Failed to open project"), i18n("Failed to open the file '%1'.", filename)); } } #endif m_project->setChanged(false); if (!rc) { closeProject(); RESET_CURSOR; return; } m_currentFileName = filename; m_project->undoStack()->clear(); m_undoViewEmptyLabel = i18n("%1: opened", m_project->name()); m_recentProjectsAction->addUrl( QUrl(filename) ); setCaption(m_project->name()); updateGUIOnProjectChanges(); updateGUI(); //there are most probably worksheets or spreadsheets in the open project -> update the GUI m_saveAction->setEnabled(false); statusBar()->showMessage( i18n("Project successfully opened (in %1 seconds).", (float)timer.elapsed()/1000) ); if (m_autoSaveActive) m_autoSaveTimer.start(); RESET_CURSOR; } void MainWin::openRecentProject(const QUrl& url) { // if(dynamic_cast(centralWidget())) { // createMdiArea(); // setCentralWidget(m_mdiArea); // } if (url.isLocalFile()) // fix for Windows this->openProject(url.toLocalFile()); else this->openProject(url.path()); } /*! Closes the current project, if available. Return \c true, if the project was closed. */ bool MainWin::closeProject() { if (m_project == nullptr) return true; //nothing to close if (warnModified()) return false; if(!m_closing) { // if(dynamic_cast(centralWidget()) && m_showWelcomeScreen) { // m_welcomeWidget = createWelcomeScreen(); // setCentralWidget(m_welcomeWidget); // } } m_projectClosing = true; statusBar()->clearMessage(); delete m_aspectTreeModel; m_aspectTreeModel = nullptr; delete m_project; m_project = nullptr; m_currentFileName.clear(); m_projectClosing = false; //update the UI if we're just closing a project //and not closing(quitting) the application if (!m_closing) { m_projectExplorerDock->hide(); m_propertiesDock->hide(); m_currentAspect = nullptr; m_currentFolder = nullptr; updateGUIOnProjectChanges(); if (m_autoSaveActive) m_autoSaveTimer.stop(); } removeDockWidget(cursorDock); delete cursorDock; cursorDock = nullptr; cursorWidget = nullptr; // is deleted, because it's the cild of cursorDock return true; } bool MainWin::saveProject() { const QString& fileName = m_project->fileName(); if (fileName.isEmpty()) return saveProjectAs(); else return save(fileName); } bool MainWin::saveProjectAs() { KConfigGroup conf(KSharedConfig::openConfig(), "MainWin"); const QString& dir = conf.readEntry("LastOpenDir", ""); QString path = QFileDialog::getSaveFileName(this, i18n("Save Project As"), dir, i18n("LabPlot Projects (*.lml *.lml.gz *.lml.bz2 *.lml.xz *.LML *.LML.GZ *.LML.BZ2 *.LML.XZ)")); if (path.isEmpty())// "Cancel" was clicked return false; if (path.contains(QLatin1String(".lml"), Qt::CaseInsensitive) == false) path.append(QLatin1String(".lml")); //save new "last open directory" int pos = path.lastIndexOf(QDir::separator()); if (pos != -1) { const QString& newDir = path.left(pos); if (newDir != dir) conf.writeEntry("LastOpenDir", newDir); } return save(path); } /*! * auxiliary function that does the actual saving of the project */ bool MainWin::save(const QString& fileName) { QTemporaryFile tempFile(QDir::tempPath() + QLatin1Char('/') + QLatin1String("labplot_save_XXXXXX")); if (!tempFile.open()) { KMessageBox::error(this, i18n("Couldn't open the temporary file for writing.")); return false; } WAIT_CURSOR; const QString& tempFileName = tempFile.fileName(); DEBUG("Using temporary file " << STDSTRING(tempFileName)) tempFile.close(); // use file ending to find out how to compress file QIODevice* file; // if ending is .lml, do gzip compression anyway if (fileName.endsWith(QLatin1String(".lml"))) file = new KCompressionDevice(tempFileName, KCompressionDevice::GZip); else file = new KFilterDev(tempFileName); if (file == nullptr) file = new QFile(tempFileName); bool ok; if (file->open(QIODevice::WriteOnly)) { m_project->setFileName(fileName); QPixmap thumbnail = centralWidget()->grab(); QXmlStreamWriter writer(file); m_project->setFileName(fileName); m_project->save(thumbnail, &writer); m_project->undoStack()->clear(); m_project->setChanged(false); file->close(); // target file must not exist if (QFile::exists(fileName)) QFile::remove(fileName); // do not rename temp file. Qt still holds a handle (which fails renaming on Windows) and deletes it bool rc = QFile::copy(tempFileName, fileName); if (rc) { setCaption(m_project->name()); statusBar()->showMessage(i18n("Project saved")); m_saveAction->setEnabled(false); m_recentProjectsAction->addUrl( QUrl(fileName) ); ok = true; //if the project dock is visible, refresh the shown content //(version and modification time might have been changed) if (stackedWidget->currentWidget() == projectDock) projectDock->setProject(m_project); //we have a file name now // -> auto save can be activated now if not happened yet if (m_autoSaveActive && !m_autoSaveTimer.isActive()) m_autoSaveTimer.start(); } else { RESET_CURSOR; KMessageBox::error(this, i18n("Couldn't save the file '%1'.", fileName)); ok = false; } } else { RESET_CURSOR; KMessageBox::error(this, i18n("Couldn't open the file '%1' for writing.", fileName)); ok = false; } delete file; RESET_CURSOR; return ok; } /*! * automatically saves the project in the specified time interval. */ void MainWin::autoSaveProject() { //don't auto save when there are no changes or the file name //was not provided yet (the project was never explicitly saved yet). if ( !m_project->hasChanged() || m_project->fileName().isEmpty()) return; this->saveProject(); } /*! prints the current sheet (worksheet, spreadsheet or matrix) */ void MainWin::print() { QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return; AbstractPart* part = static_cast(win)->part(); statusBar()->showMessage(i18n("Preparing printing of %1", part->name())); if (part->printView()) statusBar()->showMessage(i18n("%1 printed", part->name())); else statusBar()->clearMessage(); } void MainWin::printPreview() { QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return; AbstractPart* part = static_cast(win)->part(); statusBar()->showMessage(i18n("Preparing printing of %1", part->name())); if (part->printPreview()) statusBar()->showMessage(i18n("%1 printed", part->name())); else statusBar()->clearMessage(); } /**************************************************************************************/ /*! adds a new Folder to the project. */ void MainWin::newFolder() { Folder* folder = new Folder(i18n("Folder")); this->addAspectToProject(folder); } /*! adds a new Workbook to the project. */ void MainWin::newWorkbook() { Workbook* workbook = new Workbook(i18n("Workbook")); this->addAspectToProject(workbook); } /*! adds a new Datapicker to the project. */ void MainWin::newDatapicker() { Datapicker* datapicker = new Datapicker(i18n("Datapicker")); this->addAspectToProject(datapicker); } /*! adds a new Spreadsheet to the project. */ void MainWin::newSpreadsheet() { Spreadsheet* spreadsheet = new Spreadsheet(i18n("Spreadsheet")); //if the current active window is a workbook and no folder/project is selected in the project explorer, //add the new spreadsheet to the workbook Workbook* workbook = dynamic_cast(m_currentAspect); if (workbook) { QModelIndex index = m_projectExplorer->currentIndex(); const auto* aspect = static_cast(index.internalPointer()); if (!aspect->inherits(AspectType::Folder)) { workbook->addChild(spreadsheet); return; } } this->addAspectToProject(spreadsheet); } /*! adds a new Matrix to the project. */ void MainWin::newMatrix() { Matrix* matrix = new Matrix(i18n("Matrix")); //if the current active window is a workbook and no folder/project is selected in the project explorer, //add the new matrix to the workbook Workbook* workbook = dynamic_cast(m_currentAspect); if (workbook) { QModelIndex index = m_projectExplorer->currentIndex(); const auto* aspect = static_cast(index.internalPointer()); if (!aspect->inherits(AspectType::Folder)) { workbook->addChild(matrix); return; } } this->addAspectToProject(matrix); } /*! adds a new Worksheet to the project. */ void MainWin::newWorksheet() { Worksheet* worksheet = new Worksheet(i18n("Worksheet")); this->addAspectToProject(worksheet); } /*! adds a new Note to the project. */ void MainWin::newNotes() { Note* notes = new Note(i18n("Note")); this->addAspectToProject(notes); } /*! returns a pointer to a \c Spreadsheet object, if the currently active Mdi-Subwindow or if the currently selected tab in a \c WorkbookView is a \c SpreadsheetView Otherwise returns \c 0. */ Spreadsheet* MainWin::activeSpreadsheet() const { // if(dynamic_cast(centralWidget())) // return nullptr; if (!m_currentAspect) return nullptr; Spreadsheet* spreadsheet = nullptr; if (m_currentAspect->type() == AspectType::Spreadsheet) spreadsheet = dynamic_cast(m_currentAspect); else { //check whether one of spreadsheet columns is selected and determine the spreadsheet auto* parent = m_currentAspect->parentAspect(); if (parent && parent->type() == AspectType::Spreadsheet) spreadsheet = dynamic_cast(parent); } return spreadsheet; } #ifdef HAVE_CANTOR_LIBS /* adds a new Cantor Spreadsheet to the project. */ void MainWin::newCantorWorksheet(QAction* action) { CantorWorksheet* cantorworksheet = new CantorWorksheet(action->data().toString()); this->addAspectToProject(cantorworksheet); } /********************************************************************************/ #endif /*! called if there were changes in the project. Adds "changed" to the window caption and activates the save-Action. */ void MainWin::projectChanged() { setCaption(i18n("%1 [Changed]", m_project->name())); m_saveAction->setEnabled(true); m_undoAction->setEnabled(true); return; } void MainWin::handleCurrentSubWindowChanged(QMdiSubWindow* win) { if (!win) { updateGUI(); return; } auto* view = static_cast(win); if (view == m_currentSubWindow) { //do nothing, if the current sub-window gets selected again. //This event happens, when labplot loses the focus (modal window is opened or the user switches to another application) //and gets it back (modal window is closed or the user switches back to labplot). return; } else m_currentSubWindow = view; updateGUI(); if (!m_suppressCurrentSubWindowChangedEvent) m_projectExplorer->setCurrentAspect(view->part()); } void MainWin::handleAspectAdded(const AbstractAspect* aspect) { const auto* part = dynamic_cast(aspect); if (part) { // connect(part, &AbstractPart::importFromFileRequested, this, &MainWin::importFileDialog); connect(part, &AbstractPart::importFromFileRequested, this, [=]() {importFileDialog();}); connect(part, &AbstractPart::importFromSQLDatabaseRequested, this, &MainWin::importSqlDialog); //TODO: export, print and print preview should be handled in the views and not in MainWin. connect(part, &AbstractPart::exportRequested, this, &MainWin::exportDialog); connect(part, &AbstractPart::printRequested, this, &MainWin::print); connect(part, &AbstractPart::printPreviewRequested, this, &MainWin::printPreview); connect(part, &AbstractPart::showRequested, this, &MainWin::handleShowSubWindowRequested); const auto* worksheet = dynamic_cast(aspect); if (worksheet) connect(worksheet, &Worksheet::cartesianPlotMouseModeChanged, this, &MainWin::cartesianPlotMouseModeChanged); } } void MainWin::handleAspectRemoved(const AbstractAspect* parent,const AbstractAspect* before,const AbstractAspect* aspect) { Q_UNUSED(before); Q_UNUSED(aspect); //no need to react on removal of // - AbstractSimpleFilter // - columns in the data spreadsheet of a datapicker curve, // this can only happen when changing the error type and is done on the level of DatapickerImage if (!dynamic_cast(aspect) && !(parent->parentAspect() && parent->parentAspect()->type() == AspectType::DatapickerCurve) ) m_projectExplorer->setCurrentAspect(parent); } void MainWin::handleAspectAboutToBeRemoved(const AbstractAspect *aspect) { const auto* part = dynamic_cast(aspect); if (!part) return; const auto* workbook = dynamic_cast(aspect->parentAspect()); auto* datapicker = dynamic_cast(aspect->parentAspect()); if (!datapicker) datapicker = dynamic_cast(aspect->parentAspect()->parentAspect()); if (!workbook && !datapicker) { PartMdiView* win = part->mdiSubWindow(); if (win) m_mdiArea->removeSubWindow(win); } } /*! called when the current aspect in the tree of the project explorer was changed. Selects the new aspect. */ void MainWin::handleCurrentAspectChanged(AbstractAspect *aspect) { if (!aspect) aspect = m_project; // should never happen, just in case m_suppressCurrentSubWindowChangedEvent = true; if (aspect->folder() != m_currentFolder) { m_currentFolder = aspect->folder(); updateMdiWindowVisibility(); } m_currentAspect = aspect; //activate the corresponding MDI sub window for the current aspect activateSubWindowForAspect(aspect); m_suppressCurrentSubWindowChangedEvent = false; updateGUI(); } void MainWin::activateSubWindowForAspect(const AbstractAspect* aspect) const { const auto* part = dynamic_cast(aspect); if (part) { - //for LiveDataSource we currently don't show any view - /*if (dynamic_cast(part)) - return;*/ - - PartMdiView* win; + PartMdiView* win{nullptr}; //for aspects being children of a Workbook, we show workbook's window, otherwise the window of the selected part const auto* workbook = dynamic_cast(aspect->parentAspect()); auto* datapicker = dynamic_cast(aspect->parentAspect()); if (!datapicker) datapicker = dynamic_cast(aspect->parentAspect()->parentAspect()); if (workbook) win = workbook->mdiSubWindow(); else if (datapicker) win = datapicker->mdiSubWindow(); else win = part->mdiSubWindow(); if (m_mdiArea && m_mdiArea->subWindowList().indexOf(win) == -1) { if (dynamic_cast(part)) m_mdiArea->addSubWindow(win, Qt::Tool); else m_mdiArea->addSubWindow(win); win->show(); //Qt provides its own "system menu" for every sub-window. The shortcut for the close-action //in this menu collides with our global m_closeAction. //remove the shortcuts in the system menu to avoid this collision. QMenu* menu = win->systemMenu(); if (menu) { for (QAction* action : menu->actions()) action->setShortcut(QKeySequence()); } } if (m_mdiArea) m_mdiArea->setActiveSubWindow(win); } else { //activate the mdiView of the parent, if a child was selected const AbstractAspect* parent = aspect->parentAspect(); if (parent) { activateSubWindowForAspect(parent); //if the parent's parent is a Workbook (a column of a spreadsheet in workbook was selected), //we need to select the corresponding tab in WorkbookView too if (parent->parentAspect()) { auto* workbook = dynamic_cast(parent->parentAspect()); auto* datapicker = dynamic_cast(parent->parentAspect()); if (!datapicker) datapicker = dynamic_cast(parent->parentAspect()->parentAspect()); if (workbook) workbook->childSelected(parent); else if (datapicker) datapicker->childSelected(parent); } } } return; } void MainWin::setMdiWindowVisibility(QAction* action) { m_project->setMdiWindowVisibility((Project::MdiWindowVisibility)(action->data().toInt())); } /*! shows the sub window of a worksheet, matrix or a spreadsheet. Used if the window was closed before and the user asks to show the window again via the context menu in the project explorer. */ void MainWin::handleShowSubWindowRequested() { activateSubWindowForAspect(m_currentAspect); } /*! this is called on a right click on the root folder in the project explorer */ void MainWin::createContextMenu(QMenu* menu) const { QAction* firstAction = nullptr; // if we're populating the context menu for the project explorer, then //there're already actions available there. Skip the first title-action //and insert the action at the beginning of the menu. if (menu->actions().size()>1) firstAction = menu->actions().at(1); menu->insertMenu(firstAction, m_newMenu); //The tabbed view collides with the visibility policy for the subwindows. //Hide the menus for the visibility policy if the tabbed view is used. if (m_mdiArea->viewMode() != QMdiArea::TabbedView) { menu->insertSeparator(firstAction); menu->insertMenu(firstAction, m_visibilityMenu); menu->insertSeparator(firstAction); } } /*! this is called on a right click on a non-root folder in the project explorer */ void MainWin::createFolderContextMenu(const Folder* folder, QMenu* menu) const { Q_UNUSED(folder); //Folder provides it's own context menu. Add a separator before adding additional actions. menu->addSeparator(); this->createContextMenu(menu); } void MainWin::undo() { WAIT_CURSOR; m_project->undoStack()->undo(); if (m_project->undoStack()->index() == 0) { setCaption(m_project->name()); m_saveAction->setEnabled(false); m_undoAction->setEnabled(false); m_project->setChanged(false); } m_redoAction->setEnabled(true); RESET_CURSOR; } void MainWin::redo() { WAIT_CURSOR; m_project->undoStack()->redo(); projectChanged(); if (m_project->undoStack()->index() == m_project->undoStack()->count()) m_redoAction->setEnabled(false); RESET_CURSOR; } /*! Shows/hides mdi sub-windows depending on the current visibility policy. */ void MainWin::updateMdiWindowVisibility() const { QList windows = m_mdiArea->subWindowList(); PartMdiView* part_view; switch (m_project->mdiWindowVisibility()) { case Project::allMdiWindows: for (auto* window : windows) window->show(); break; case Project::folderOnly: for (auto* window : windows) { part_view = qobject_cast(window); Q_ASSERT(part_view); if (part_view->part()->folder() == m_currentFolder) part_view->show(); else part_view->hide(); } break; case Project::folderAndSubfolders: for (auto* window : windows) { part_view = qobject_cast(window); if (part_view->part()->isDescendantOf(m_currentFolder)) part_view->show(); else part_view->hide(); } break; } } void MainWin::toggleDockWidget(QAction* action) { if (action->objectName() == "toggle_project_explorer_dock") { if (m_projectExplorerDock->isVisible()) m_projectExplorerDock->hide(); // toggleHideWidget(m_projectExplorerDock, true); else m_projectExplorerDock->show(); // toggleShowWidget(m_projectExplorerDock, true); } else if (action->objectName() == "toggle_properties_explorer_dock") { if (m_propertiesDock->isVisible()) m_propertiesDock->hide(); // toggleHideWidget(m_propertiesDock, false); else m_propertiesDock->show(); // toggleShowWidget(m_propertiesDock, false); } } /* void MainWin::toggleHideWidget(QWidget* widget, bool hideToLeft) { auto* timeline = new QTimeLine(800, this); timeline->setEasingCurve(QEasingCurve::InOutQuad); connect(timeline, &QTimeLine::valueChanged, [=] { const qreal value = timeline->currentValue(); const int widgetWidth = widget->width(); const int widgetPosY = widget->pos().y(); int moveX = 0; if (hideToLeft) { moveX = static_cast(value * widgetWidth) - widgetWidth; } else { const int frameRight = this->frameGeometry().right(); moveX = frameRight - static_cast(value * widgetWidth); } widget->move(moveX, widgetPosY); }); timeline->setDirection(QTimeLine::Backward); timeline->start(); connect(timeline, &QTimeLine::finished, [widget] {widget->hide();}); connect(timeline, &QTimeLine::finished, timeline, &QTimeLine::deleteLater); } void MainWin::toggleShowWidget(QWidget* widget, bool showToRight) { auto* timeline = new QTimeLine(800, this); timeline->setEasingCurve(QEasingCurve::InOutQuad); connect(timeline, &QTimeLine::valueChanged, [=]() { if (widget->isHidden()) { widget->show(); } const qreal value = timeline->currentValue(); const int widgetWidth = widget->width(); const int widgetPosY = widget->pos().y(); int moveX = 0; if (showToRight) { moveX = static_cast(value * widgetWidth) - widgetWidth; } else { const int frameRight = this->frameGeometry().right(); moveX = frameRight - static_cast(value * widgetWidth); } widget->move(moveX, widgetPosY); }); timeline->setDirection(QTimeLine::Forward); timeline->start(); connect(timeline, &QTimeLine::finished, timeline, &QTimeLine::deleteLater); } */ void MainWin::projectExplorerDockVisibilityChanged(bool visible) { m_toggleProjectExplorerDockAction->setChecked(visible); } void MainWin::propertiesDockVisibilityChanged(bool visible) { m_togglePropertiesDockAction->setChecked(visible); } void MainWin::cursorDockVisibilityChanged(bool visible) { //if the cursor dock was closed, switch to the "Select and Edit" mouse mode if (!visible) { // auto* worksheet = activeWorksheet(); //TODO: } } void MainWin::cartesianPlotMouseModeChanged(CartesianPlot::MouseMode mode) { if (mode != CartesianPlot::Cursor) { if (cursorDock) cursorDock->hide(); } else { if (!cursorDock) { cursorDock = new QDockWidget(i18n("Cursor"), this); cursorWidget = new CursorDock(cursorDock); cursorDock->setWidget(cursorWidget); connect(cursorDock, &QDockWidget::visibilityChanged, this, &MainWin::cursorDockVisibilityChanged); // cursorDock->setFloating(true); // does not work. Don't understand why // if (m_propertiesDock) // tabifyDockWidget(cursorDock, m_propertiesDock); // else addDockWidget(Qt::DockWidgetArea::RightDockWidgetArea, cursorDock); } auto* worksheet = static_cast(QObject::sender()); cursorWidget->setWorksheet(worksheet); cursorDock->show(); } } void MainWin::toggleFullScreen() { if (this->windowState() == Qt::WindowFullScreen) this->setWindowState(m_lastWindowState); else { m_lastWindowState = this->windowState(); this->showFullScreen(); } } void MainWin::closeEvent(QCloseEvent* event) { m_closing = true; if (!this->closeProject()) { m_closing = false; event->ignore(); } } void MainWin::dragEnterEvent(QDragEnterEvent* event) { event->accept(); } void MainWin::dropEvent(QDropEvent* event) { if (event->mimeData() && !event->mimeData()->urls().isEmpty()) { QUrl url = event->mimeData()->urls().at(0); const QString& f = url.toLocalFile(); bool open = Project::isLabPlotProject(f); #ifdef HAVE_LIBORIGIN if (!open) open = OriginProjectParser::isOriginProject(f); #endif #ifdef HAVE_CANTOR_LIBS if (!open) { QFileInfo fi(f); open = (fi.completeSuffix() == QLatin1String("cws")) || (fi.completeSuffix() == QLatin1String("ipynb")); } #endif if (open) openProject(f); else { if (!m_project) newProject(); importFileDialog(f); } event->accept(); } else event->ignore(); } void MainWin::handleSettingsChanges() { const KConfigGroup group = KSharedConfig::openConfig()->group( "Settings_General" ); // if(dynamic_cast(centralWidget()) == nullptr) { // QMdiArea::ViewMode viewMode = QMdiArea::ViewMode(group.readEntry("ViewMode", 0)); // if (m_mdiArea->viewMode() != viewMode) { // m_mdiArea->setViewMode(viewMode); // if (viewMode == QMdiArea::SubWindowView) // this->updateMdiWindowVisibility(); // } // // if (m_mdiArea->viewMode() == QMdiArea::TabbedView) { // m_tileWindowsAction->setVisible(false); // m_cascadeWindowsAction->setVisible(false); // QTabWidget::TabPosition tabPosition = QTabWidget::TabPosition(group.readEntry("TabPosition", 0)); // if (m_mdiArea->tabPosition() != tabPosition) // m_mdiArea->setTabPosition(tabPosition); // } else { // m_tileWindowsAction->setVisible(true); // m_cascadeWindowsAction->setVisible(true); // } // } //autosave bool autoSave = group.readEntry("AutoSave", 0); if (m_autoSaveActive != autoSave) { m_autoSaveActive = autoSave; if (autoSave) m_autoSaveTimer.start(); else m_autoSaveTimer.stop(); } int interval = group.readEntry("AutoSaveInterval", 1); interval *= 60*1000; if (interval != m_autoSaveTimer.interval()) m_autoSaveTimer.setInterval(interval); //show memory info bool showMemoryInfo = group.readEntry(QLatin1String("ShowMemoryInfo"), true); if (m_showMemoryInfo != showMemoryInfo) { m_showMemoryInfo = showMemoryInfo; if (showMemoryInfo) { m_memoryInfoWidget = new MemoryWidget(statusBar()); statusBar()->addPermanentWidget(m_memoryInfoWidget); } else { if (m_memoryInfoWidget) { statusBar()->removeWidget(m_memoryInfoWidget); delete m_memoryInfoWidget; m_memoryInfoWidget = nullptr; } } } //update the units if (stackedWidget) { for (int i = 0; i < stackedWidget->count(); ++i) { auto* widget = stackedWidget->widget(i); BaseDock* dock = dynamic_cast(widget); if (dock) dock->updateUnits(); else { auto* labelWidget = dynamic_cast(widget); if (labelWidget) labelWidget->updateUnits(); } } } bool showWelcomeScreen = group.readEntry(QLatin1String("ShowWelcomeScreen"), true); if(m_showWelcomeScreen != showWelcomeScreen) m_showWelcomeScreen = showWelcomeScreen; } void MainWin::openDatasetExample() { newProject(); // addAspectToProject(m_welcomeScreenHelper->releaseConfiguredSpreadsheet()); } /***************************************************************************************/ /************************************** dialogs ***************************************/ /***************************************************************************************/ /*! shows the dialog with the Undo-history. */ void MainWin::historyDialog() { if (!m_project->undoStack()) return; auto* dialog = new HistoryDialog(this, m_project->undoStack(), m_undoViewEmptyLabel); int index = m_project->undoStack()->index(); if (dialog->exec() != QDialog::Accepted) { if (m_project->undoStack()->count() != 0) m_project->undoStack()->setIndex(index); } //disable undo/redo-actions if the history was cleared //(in both cases, when accepted or rejected in the dialog) if (m_project->undoStack()->count() == 0) { m_undoAction->setEnabled(false); m_redoAction->setEnabled(false); } } /*! Opens the dialog to import data to the selected workbook, spreadsheet or matrix */ void MainWin::importFileDialog(const QString& fileName) { DEBUG("MainWin::importFileDialog()"); auto* dlg = new ImportFileDialog(this, false, fileName); // select existing container if (m_currentAspect->type() == AspectType::Spreadsheet || m_currentAspect->type() == AspectType::Matrix || m_currentAspect->type() == AspectType::Workbook) dlg->setCurrentIndex(m_projectExplorer->currentIndex()); else if (m_currentAspect->type() == AspectType::Column && m_currentAspect->parentAspect()->type() == AspectType::Spreadsheet) dlg->setCurrentIndex(m_aspectTreeModel->modelIndexOfAspect(m_currentAspect->parentAspect())); if (dlg->exec() == QDialog::Accepted) { dlg->importTo(statusBar()); m_project->setChanged(true); } delete dlg; DEBUG("MainWin::importFileDialog() DONE"); } void MainWin::importSqlDialog() { DEBUG("MainWin::importSqlDialog()"); auto* dlg = new ImportSQLDatabaseDialog(this); // select existing container if (m_currentAspect->type() == AspectType::Spreadsheet || m_currentAspect->type() == AspectType::Matrix || m_currentAspect->type() == AspectType::Workbook) dlg->setCurrentIndex(m_projectExplorer->currentIndex()); else if (m_currentAspect->type() == AspectType::Column && m_currentAspect->parentAspect()->type() == AspectType::Spreadsheet) dlg->setCurrentIndex(m_aspectTreeModel->modelIndexOfAspect(m_currentAspect->parentAspect())); if (dlg->exec() == QDialog::Accepted) { dlg->importTo(statusBar()); m_project->setChanged(true); } delete dlg; DEBUG("MainWin::importSqlDialog() DONE"); } void MainWin::importProjectDialog() { DEBUG("MainWin::importProjectDialog()"); ImportProjectDialog::ProjectType type; if (QObject::sender() == m_importOpjAction) type = ImportProjectDialog::ProjectOrigin; else type = ImportProjectDialog::ProjectLabPlot; auto* dlg = new ImportProjectDialog(this, type); // set current folder dlg->setCurrentFolder(m_currentFolder); if (dlg->exec() == QDialog::Accepted) { dlg->importTo(statusBar()); m_project->setChanged(true); } delete dlg; DEBUG("MainWin::importProjectDialog() DONE"); } /*! * \brief opens a dialog to import datasets */ void MainWin::importDatasetDialog() { ImportDatasetDialog* dlg = new ImportDatasetDialog(this); if (dlg->exec() == QDialog::Accepted) { Spreadsheet* spreadsheet = new Spreadsheet(i18n("Dataset%1", 1)); DatasetHandler* dataset = new DatasetHandler(spreadsheet); dlg->importToDataset(dataset, statusBar()); QTimer timer; timer.setSingleShot(true); QEventLoop loop; connect(dataset, &DatasetHandler::downloadCompleted, &loop, &QEventLoop::quit); connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); timer.start(1500); loop.exec(); if(timer.isActive()){ timer.stop(); addAspectToProject(spreadsheet); delete dataset; } else delete dataset; } delete dlg; } /*! opens the dialog for the export of the currently active worksheet, spreadsheet or matrix. */ void MainWin::exportDialog() { QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return; AbstractPart* part = static_cast(win)->part(); if (part->exportView()) statusBar()->showMessage(i18n("%1 exported", part->name())); } void MainWin::editFitsFileDialog() { auto* editDialog = new FITSHeaderEditDialog(this); if (editDialog->exec() == QDialog::Accepted) { if (editDialog->saved()) statusBar()->showMessage(i18n("FITS files saved")); } } /*! adds a new file data source to the current project. */ void MainWin::newLiveDataSourceActionTriggered() { ImportFileDialog* dlg = new ImportFileDialog(this, true); if (dlg->exec() == QDialog::Accepted) { if (static_cast(dlg->sourceType()) == LiveDataSource::MQTT) { #ifdef HAVE_MQTT MQTTClient* mqttClient = new MQTTClient(i18n("MQTT Client%1", 1)); dlg->importToMQTT(mqttClient); mqttClient->setName(mqttClient->clientHostName()); QVector existingClients = m_project->children(AbstractAspect::Recursive); //doesn't make sense to have more MQTTClients connected to the same broker bool found = false; for (const auto* client : existingClients) { if (client->clientHostName() == mqttClient->clientHostName() && client->clientPort() == mqttClient->clientPort()) { found = true; break; } } if (!found) addAspectToProject(mqttClient); else { delete mqttClient; QMessageBox::warning(this, "Warning", "There already is a MQTTClient with this host!"); } #endif } else { LiveDataSource* dataSource = new LiveDataSource(i18n("Live data source%1", 1), false); dlg->importToLiveDataSource(dataSource, statusBar()); addAspectToProject(dataSource); } } delete dlg; } void MainWin::addAspectToProject(AbstractAspect* aspect) { const QModelIndex& index = m_projectExplorer->currentIndex(); if (index.isValid()) { auto* parent = static_cast(index.internalPointer()); #ifdef HAVE_MQTT //doesn't make sense to add a new MQTTClient to an existing MQTTClient or to any of its successors QString className = parent->metaObject()->className(); MQTTClient* clientAncestor = parent->ancestor(); if (className == "MQTTClient") parent = parent->parentAspect(); else if (clientAncestor != nullptr) parent = clientAncestor->parentAspect(); #endif parent->folder()->addChild(aspect); } else m_project->addChild(aspect); } void MainWin::settingsDialog() { auto* dlg = new SettingsDialog(this); connect (dlg, &SettingsDialog::settingsChanged, this, &MainWin::handleSettingsChanges); // connect (dlg, &SettingsDialog::resetWelcomeScreen, this, &MainWin::resetWelcomeScreen); dlg->exec(); } #ifdef HAVE_CANTOR_LIBS void MainWin::cantorSettingsDialog() { static KCoreConfigSkeleton* emptyConfig = new KCoreConfigSkeleton(); KConfigDialog* cantorDialog = new KConfigDialog(this, QLatin1String("Cantor Settings"), emptyConfig); for (auto* backend : Cantor::Backend::availableBackends()) if (backend->config()) //It has something to configure, so add it to the dialog cantorDialog->addPage(backend->settingsWidget(cantorDialog), backend->config(), backend->name(), backend->icon()); cantorDialog->show(); } #endif