File indexing completed on 2024-05-12 03:48:32

0001 /*
0002     File                 : WorksheetView.cpp
0003     Project              : LabPlot
0004     Description          : Worksheet view
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2009-2023 Alexander Semke <alexander.semke@web.de>
0007     SPDX-FileCopyrightText: 2016-2018 Stefan-Gerlach <stefan.gerlach@uni.kn>
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include "commonfrontend/worksheet/WorksheetView.h"
0012 #include "backend/core/AbstractAspect.h"
0013 #include "backend/core/AbstractColumn.h"
0014 #include "backend/core/Project.h"
0015 #include "backend/core/Settings.h"
0016 #include "backend/lib/XmlStreamReader.h"
0017 #include "backend/lib/trace.h"
0018 #include "backend/worksheet/Background.h"
0019 #include "backend/worksheet/Image.h"
0020 #include "backend/worksheet/TextLabel.h"
0021 #include "backend/worksheet/plots/cartesian/Axis.h"
0022 #include "backend/worksheet/plots/cartesian/AxisPrivate.h"
0023 #include "backend/worksheet/plots/cartesian/BoxPlot.h" //TODO: needed for the icon only, remove later once we have a breeze icon
0024 #include "backend/worksheet/plots/cartesian/ReferenceLine.h"
0025 #include "backend/worksheet/plots/cartesian/ReferenceRange.h"
0026 #include "commonfrontend/core/ContentDockWidget.h"
0027 #include "kdefrontend/PlotTemplateDialog.h"
0028 #include "kdefrontend/widgets/ThemesWidget.h"
0029 #include "kdefrontend/worksheet/GridDialog.h"
0030 #include "kdefrontend/worksheet/PresenterWidget.h"
0031 
0032 #ifdef Q_OS_MAC
0033 #include "3rdparty/kdmactouchbar/src/kdmactouchbar.h"
0034 #endif
0035 
0036 #include <KColorScheme>
0037 #include <KConfigGroup>
0038 #include <KLocalizedString>
0039 #include <KMessageBox>
0040 #include <kcoreaddons_version.h>
0041 
0042 #include <QActionGroup>
0043 #include <QApplication>
0044 #include <QClipboard>
0045 #include <QGraphicsOpacityEffect>
0046 #include <QImage>
0047 #include <QMdiArea>
0048 #include <QMenu>
0049 #include <QMimeData>
0050 #include <QPrinter>
0051 #include <QScreen>
0052 #include <QSvgGenerator>
0053 #include <QTimeLine>
0054 #include <QToolBar>
0055 #include <QToolButton>
0056 #include <QWheelEvent>
0057 #include <QWidgetAction>
0058 
0059 #include <DockManager.h>
0060 
0061 #include <limits>
0062 
0063 /**
0064  * \class WorksheetView
0065  * \brief Worksheet view
0066  */
0067 
0068 /*!
0069   Constructor of the class.
0070   Creates a view for the Worksheet \c worksheet and initializes the internal model.
0071 */
0072 WorksheetView::WorksheetView(Worksheet* worksheet)
0073     : QGraphicsView()
0074     , m_worksheet(worksheet) {
0075     setScene(m_worksheet->scene());
0076 
0077     setRenderHint(QPainter::Antialiasing);
0078     setRubberBandSelectionMode(Qt::ContainsItemBoundingRect);
0079     setTransformationAnchor(QGraphicsView::AnchorViewCenter);
0080     setResizeAnchor(QGraphicsView::AnchorViewCenter);
0081     setMinimumSize(16, 16);
0082     setFocusPolicy(Qt::StrongFocus);
0083 
0084     updateScrollBarPolicy();
0085 
0086     viewport()->setAttribute(Qt::WA_OpaquePaintEvent);
0087     viewport()->setAttribute(Qt::WA_NoSystemBackground);
0088     setAcceptDrops(true);
0089     setCacheMode(QGraphicsView::CacheBackground);
0090 
0091     m_gridSettings.style = GridStyle::NoGrid;
0092 
0093     // signal/slot connections
0094     connect(m_worksheet, &Worksheet::requestProjectContextMenu, this, &WorksheetView::createContextMenu);
0095     connect(m_worksheet, &Worksheet::itemSelected, this, &WorksheetView::selectItem);
0096     connect(m_worksheet, &Worksheet::itemDeselected, this, &WorksheetView::deselectItem);
0097     connect(m_worksheet, &Worksheet::requestUpdate, this, &WorksheetView::updateBackground);
0098     connect(m_worksheet, &Worksheet::childAspectAboutToBeRemoved, this, &WorksheetView::aspectAboutToBeRemoved);
0099     connect(m_worksheet, &Worksheet::useViewSizeChanged, this, &WorksheetView::useViewSizeChanged);
0100     connect(m_worksheet, &Worksheet::layoutChanged, this, &WorksheetView::layoutChanged);
0101     connect(m_worksheet, &Worksheet::changed, this, [=] {
0102         if (m_magnificationWindow && m_magnificationWindow->isVisible())
0103             updateMagnificationWindow(mapToScene(mapFromGlobal(QCursor::pos())));
0104     });
0105     connect(scene(), &QGraphicsScene::selectionChanged, this, &WorksheetView::selectionChanged);
0106 
0107     // resize the view to make the complete scene visible.
0108     // no need to resize the view when the project is being opened,
0109     // all views will be resized to the stored values at the end
0110     if (!m_worksheet->isLoading()) {
0111         float w = Worksheet::convertFromSceneUnits(sceneRect().width(), Worksheet::Unit::Inch);
0112         float h = Worksheet::convertFromSceneUnits(sceneRect().height(), Worksheet::Unit::Inch);
0113         w *= QApplication::primaryScreen()->physicalDotsPerInchX();
0114         h *= QApplication::primaryScreen()->physicalDotsPerInchY();
0115         resize(w * 1.1, h * 1.1);
0116     }
0117 
0118     // rescale to the original size
0119     static const qreal hscale = QApplication::primaryScreen()->physicalDotsPerInchX() / (Worksheet::convertToSceneUnits(1, Worksheet::Unit::Inch));
0120     static const qreal vscale = QApplication::primaryScreen()->physicalDotsPerInchY() / (Worksheet::convertToSceneUnits(1, Worksheet::Unit::Inch));
0121     setTransform(QTransform::fromScale(hscale, vscale));
0122 
0123     initBasicActions();
0124     installEventFilter(this);
0125 }
0126 
0127 /*!
0128  * initializes couple of actions that have shortcuts assigned in the constructor as opposed
0129  * to other actions in initAction() that are create on demand only if the context menu is requested
0130  */
0131 void WorksheetView::initBasicActions() {
0132     selectAllAction = new QAction(QIcon::fromTheme(QStringLiteral("edit-select-all")), i18n("Select All"), this);
0133     this->addAction(selectAllAction);
0134     connect(selectAllAction, &QAction::triggered, this, &WorksheetView::selectAllElements);
0135 
0136     deleteAction = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18n("Delete"), this);
0137     this->addAction(deleteAction);
0138     connect(deleteAction, &QAction::triggered, this, &WorksheetView::deleteElement);
0139 
0140     backspaceAction = new QAction(this);
0141     this->addAction(backspaceAction);
0142     connect(backspaceAction, &QAction::triggered, this, &WorksheetView::deleteElement);
0143 
0144     // Zoom actions
0145     zoomInViewAction = new QAction(QIcon::fromTheme(QStringLiteral("zoom-in")), i18n("Zoom In"), this);
0146     zoomOutViewAction = new QAction(QIcon::fromTheme(QStringLiteral("zoom-out")), i18n("Zoom Out"), this);
0147     zoomOriginAction = new QAction(QIcon::fromTheme(QStringLiteral("zoom-original")), i18n("Original Size"), this);
0148 }
0149 
0150 void WorksheetView::initActions() {
0151     auto* addNewActionGroup = new QActionGroup(this);
0152     auto* zoomActionGroup = new QActionGroup(this);
0153     auto* mouseModeActionGroup = new QActionGroup(this);
0154     auto* layoutActionGroup = new QActionGroup(this);
0155     auto* gridActionGroup = new QActionGroup(this);
0156     gridActionGroup->setExclusive(true);
0157     auto* magnificationActionGroup = new QActionGroup(this);
0158 
0159     zoomActionGroup->addAction(zoomInViewAction);
0160     zoomActionGroup->addAction(zoomOutViewAction);
0161     zoomActionGroup->addAction(zoomOriginAction);
0162 
0163     auto* fitActionGroup = new QActionGroup(zoomActionGroup);
0164     fitActionGroup->setExclusive(true);
0165     zoomFitNoneAction = new QAction(QIcon::fromTheme(QStringLiteral("zoom-fit-none")), i18n("No fit"), fitActionGroup);
0166     zoomFitNoneAction->setCheckable(true);
0167     zoomFitNoneAction->setChecked(true);
0168     zoomFitNoneAction->setData((int)Worksheet::ZoomFit::None);
0169     zoomFitPageHeightAction = new QAction(QIcon::fromTheme(QStringLiteral("zoom-fit-height")), i18n("Fit to Height"), fitActionGroup);
0170     zoomFitPageHeightAction->setCheckable(true);
0171     zoomFitPageHeightAction->setData((int)Worksheet::ZoomFit::FitToHeight);
0172     zoomFitPageWidthAction = new QAction(QIcon::fromTheme(QStringLiteral("zoom-fit-width")), i18n("Fit to Width"), fitActionGroup);
0173     zoomFitPageWidthAction->setCheckable(true);
0174     zoomFitPageWidthAction->setData((int)Worksheet::ZoomFit::FitToWidth);
0175     zoomFitSelectionAction = new QAction(QIcon::fromTheme(QStringLiteral("zoom-fit-selection")), i18n("Fit to Selection"), fitActionGroup);
0176     zoomFitSelectionAction->setCheckable(true);
0177     zoomFitSelectionAction->setData((int)Worksheet::ZoomFit::FitToSelection);
0178     zoomFitAction = new QAction(QIcon::fromTheme(QStringLiteral("zoom-fit")), i18n("Fit"), fitActionGroup);
0179     zoomFitAction->setCheckable(true);
0180     zoomFitAction->setData((int)Worksheet::ZoomFit::Fit);
0181 
0182     // Mouse mode actions
0183     selectionModeAction = new QAction(QIcon::fromTheme(QStringLiteral("labplot-cursor-arrow")), i18n("Select and Edit"), mouseModeActionGroup);
0184     selectionModeAction->setCheckable(true);
0185 
0186     navigationModeAction = new QAction(QIcon::fromTheme(QStringLiteral("input-mouse")), i18n("Navigate"), mouseModeActionGroup);
0187     navigationModeAction->setCheckable(true);
0188 
0189     zoomSelectionModeAction = new QAction(QIcon::fromTheme(QStringLiteral("page-zoom")), i18n("Select and Zoom"), mouseModeActionGroup);
0190     zoomSelectionModeAction->setCheckable(true);
0191 
0192     // Magnification actions
0193     noMagnificationAction = new QAction(QIcon::fromTheme(QStringLiteral("labplot-1x-zoom")), i18n("No Magnification"), magnificationActionGroup);
0194     noMagnificationAction->setCheckable(true);
0195     noMagnificationAction->setChecked(true);
0196 
0197     twoTimesMagnificationAction = new QAction(QIcon::fromTheme(QStringLiteral("labplot-2x-zoom")), i18n("2x Magnification"), magnificationActionGroup);
0198     twoTimesMagnificationAction->setCheckable(true);
0199 
0200     threeTimesMagnificationAction = new QAction(QIcon::fromTheme(QStringLiteral("labplot-3x-zoom")), i18n("3x Magnification"), magnificationActionGroup);
0201     threeTimesMagnificationAction->setCheckable(true);
0202 
0203     fourTimesMagnificationAction = new QAction(QIcon::fromTheme(QStringLiteral("labplot-4x-zoom")), i18n("4x Magnification"), magnificationActionGroup);
0204     fourTimesMagnificationAction->setCheckable(true);
0205 
0206     fiveTimesMagnificationAction = new QAction(QIcon::fromTheme(QStringLiteral("labplot-5x-zoom")), i18n("5x Magnification"), magnificationActionGroup);
0207     fiveTimesMagnificationAction->setCheckable(true);
0208 
0209     // TODO implement later "group selection action" where multiple objects can be selected by drawing a rectangular
0210     //  selectionModeAction = new QAction(QIcon::fromTheme(QStringLiteral("select-rectangular")), i18n("Selection"), mouseModeActionGroup);
0211     //  selectionModeAction->setCheckable(true);
0212 
0213     //"Add new" related actions
0214     addCartesianPlot1Action = new QAction(QIcon::fromTheme(QStringLiteral("labplot-xy-plot-four-axes")), i18n("Four Axes"), addNewActionGroup);
0215     addCartesianPlot2Action = new QAction(QIcon::fromTheme(QStringLiteral("labplot-xy-plot-two-axes")), i18n("Two Axes"), addNewActionGroup);
0216     addCartesianPlot3Action = new QAction(QIcon::fromTheme(QStringLiteral("labplot-xy-plot-two-axes-centered")), i18n("Two Axes, Centered"), addNewActionGroup);
0217     addCartesianPlot4Action =
0218         new QAction(QIcon::fromTheme(QStringLiteral("labplot-xy-plot-two-axes-centered-origin")), i18n("Two Axes, Crossing at Origin"), addNewActionGroup);
0219     addCartesianPlotTemplateAction = new QAction(QIcon::fromTheme(QStringLiteral("document-new-from-template")), i18n("Load from Template"), addNewActionGroup);
0220     addTextLabelAction = new QAction(QIcon::fromTheme(QStringLiteral("draw-text")), i18n("Text"), addNewActionGroup);
0221     addImageAction = new QAction(QIcon::fromTheme(QStringLiteral("viewimage")), i18n("Image"), addNewActionGroup);
0222 
0223     // Layout actions
0224     // TODO: the icons labplot-editvlayout and labplot-edithlayout are confusing for the user.
0225     // the orientation is visualized as a horizontal or vertical line on the icon, but the user
0226     // percieves the two objects (resembles plots on the worksheet) separated by this line much stronger than the line itself.
0227     // with this, the two objects separated by a vertical line are perceived to be layed out in a _horizontal_ order and the
0228     // same for the vertical line. Because of this we change the icons here. We can rename the icons later in the breeze icon set.
0229     verticalLayoutAction = new QAction(QIcon::fromTheme(QStringLiteral("labplot-edithlayout")), i18n("Vertical Layout"), layoutActionGroup);
0230     verticalLayoutAction->setCheckable(true);
0231 
0232     horizontalLayoutAction = new QAction(QIcon::fromTheme(QStringLiteral("labplot-editvlayout")), i18n("Horizontal Layout"), layoutActionGroup);
0233     horizontalLayoutAction->setCheckable(true);
0234 
0235     gridLayoutAction = new QAction(QIcon::fromTheme(QStringLiteral("labplot-editgrid")), i18n("Grid Layout"), layoutActionGroup);
0236     gridLayoutAction->setCheckable(true);
0237 
0238     breakLayoutAction = new QAction(QIcon::fromTheme(QStringLiteral("labplot-editbreaklayout")), i18n("No Layout"), layoutActionGroup);
0239     breakLayoutAction->setEnabled(false);
0240 
0241     // Grid actions
0242     noGridAction = new QAction(i18n("No Grid"), gridActionGroup);
0243     noGridAction->setCheckable(true);
0244     noGridAction->setChecked(true);
0245     noGridAction->setData(static_cast<int>(GridStyle::NoGrid));
0246 
0247     denseLineGridAction = new QAction(i18n("Dense Line Grid"), gridActionGroup);
0248     denseLineGridAction->setCheckable(true);
0249 
0250     sparseLineGridAction = new QAction(i18n("Sparse Line Grid"), gridActionGroup);
0251     sparseLineGridAction->setCheckable(true);
0252 
0253     denseDotGridAction = new QAction(i18n("Dense Dot Grid"), gridActionGroup);
0254     denseDotGridAction->setCheckable(true);
0255 
0256     sparseDotGridAction = new QAction(i18n("Sparse Dot Grid"), gridActionGroup);
0257     sparseDotGridAction->setCheckable(true);
0258 
0259     customGridAction = new QAction(i18n("Custom Grid"), gridActionGroup);
0260     customGridAction->setCheckable(true);
0261 
0262     snapToGridAction = new QAction(i18n("Snap to Grid"), this);
0263     snapToGridAction->setCheckable(true);
0264 
0265     showPresenterMode = new QAction(QIcon::fromTheme(QStringLiteral("view-fullscreen")), i18n("Presenter Mode"), this);
0266     showPresenterMode->setShortcut(Qt::Key_F);
0267 
0268     // check the action corresponding to the currently active layout in worksheet
0269     this->layoutChanged(m_worksheet->layout());
0270 
0271     connect(addNewActionGroup, &QActionGroup::triggered, this, &WorksheetView::addNew);
0272     connect(mouseModeActionGroup, &QActionGroup::triggered, this, &WorksheetView::mouseModeChanged);
0273     connect(fitActionGroup, &QActionGroup::triggered, this, &WorksheetView::fitChanged);
0274     connect(zoomActionGroup, &QActionGroup::triggered, this, &WorksheetView::changeZoom);
0275     connect(magnificationActionGroup, &QActionGroup::triggered, this, &WorksheetView::magnificationChanged);
0276     connect(layoutActionGroup, &QActionGroup::triggered, this, &WorksheetView::changeLayout);
0277     connect(gridActionGroup, &QActionGroup::triggered, this, &WorksheetView::changeGrid);
0278     connect(snapToGridAction, &QAction::triggered, this, &WorksheetView::changeSnapToGrid);
0279     connect(showPresenterMode, &QAction::triggered, this, &WorksheetView::presenterMode);
0280 
0281     // worksheet control actions
0282     plotsInteractiveAction = new QAction(QIcon::fromTheme(QStringLiteral("hidemouse")), i18n("Interactive Plots"), this);
0283     plotsInteractiveAction->setToolTip(i18n("If not activated, plots on the worksheet don't react on drag and mouse wheel events."));
0284     plotsInteractiveAction->setCheckable(true);
0285     plotsInteractiveAction->setChecked(m_worksheet->plotsInteractive());
0286     connect(plotsInteractiveAction, &QAction::triggered, this, &WorksheetView::plotsInteractiveActionChanged);
0287 
0288     // actions for cartesian plots
0289 
0290     // "add new"-action shown in the context menu
0291     cartesianPlotAddNewAction = new QAction(QIcon::fromTheme(QStringLiteral("list-add")), i18n("Add New"), this);
0292 
0293     // "add new"- toolbutton shown in the toolbar
0294     tbCartesianPlotAddNew = new QToolButton(this);
0295     tbCartesianPlotAddNew->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
0296     tbCartesianPlotAddNew->setPopupMode(QToolButton::InstantPopup);
0297 
0298     auto* cartesianPlotActionModeActionGroup = new QActionGroup(this);
0299     cartesianPlotActionModeActionGroup->setExclusive(true);
0300     cartesianPlotApplyToSelectionAction = new QAction(i18n("Selected Plot Areas"), cartesianPlotActionModeActionGroup);
0301     cartesianPlotApplyToSelectionAction->setCheckable(true);
0302     cartesianPlotApplyToAllAction = new QAction(i18n("All Plot Areas"), cartesianPlotActionModeActionGroup);
0303     cartesianPlotApplyToAllAction->setCheckable(true);
0304     cartesianPlotApplyToAllXAction = new QAction(i18n("All Plot Areas X"), cartesianPlotActionModeActionGroup);
0305     cartesianPlotApplyToAllXAction->setCheckable(true);
0306     cartesianPlotApplyToAllYAction = new QAction(i18n("All Plot Areas Y"), cartesianPlotActionModeActionGroup);
0307     cartesianPlotApplyToAllYAction->setCheckable(true);
0308     setCartesianPlotActionMode(m_worksheet->cartesianPlotActionMode());
0309     connect(cartesianPlotActionModeActionGroup, &QActionGroup::triggered, this, &WorksheetView::cartesianPlotActionModeChanged);
0310 
0311     // cursor apply to all/selected
0312     auto* plotActionCursorGroup = new QActionGroup(this);
0313     plotActionCursorGroup->setExclusive(true);
0314     cartesianPlotApplyToSelectionCursor = new QAction(i18n("Selected Plot Areas"), plotActionCursorGroup);
0315     cartesianPlotApplyToSelectionCursor->setCheckable(true);
0316     cartesianPlotApplyToAllCursor = new QAction(i18n("All Plot Areas"), plotActionCursorGroup);
0317     cartesianPlotApplyToAllCursor->setCheckable(true);
0318     setCartesianPlotCursorMode(m_worksheet->cartesianPlotCursorMode());
0319     connect(plotActionCursorGroup, &QActionGroup::triggered, this, &WorksheetView::cartesianPlotCursorModeChanged);
0320 
0321     initPlotNavigationActions();
0322 
0323     // set some default values
0324     selectionModeAction->setChecked(true);
0325     currentZoomAction = zoomInViewAction;
0326     currentMagnificationAction = noMagnificationAction;
0327 
0328     m_actionsInitialized = true;
0329 }
0330 
0331 void WorksheetView::initPlotNavigationActions() {
0332     auto* plotMouseModeActionGroup = new QActionGroup(this);
0333     plotMouseModeActionGroup->setExclusive(true);
0334     cartesianPlotSelectionModeAction = new QAction(QIcon::fromTheme(QStringLiteral("labplot-cursor-arrow")), i18n("Select and Edit"), plotMouseModeActionGroup);
0335     cartesianPlotSelectionModeAction->setData(static_cast<int>(CartesianPlot::MouseMode::Selection));
0336     cartesianPlotSelectionModeAction->setCheckable(true);
0337     cartesianPlotSelectionModeAction->setChecked(true);
0338 
0339     cartesianPlotCrosshairModeAction = new QAction(QIcon::fromTheme(QStringLiteral("crosshairs")), i18n("Crosshair"), plotMouseModeActionGroup);
0340     cartesianPlotCrosshairModeAction->setData(static_cast<int>(CartesianPlot::MouseMode::Crosshair));
0341     cartesianPlotCrosshairModeAction->setCheckable(true);
0342 
0343     cartesianPlotZoomSelectionModeAction =
0344         new QAction(QIcon::fromTheme(QStringLiteral("labplot-zoom-select")), i18n("Select Region and Zoom In"), plotMouseModeActionGroup);
0345     cartesianPlotZoomSelectionModeAction->setData(static_cast<int>(CartesianPlot::MouseMode::ZoomSelection));
0346     cartesianPlotZoomSelectionModeAction->setCheckable(true);
0347 
0348     cartesianPlotZoomXSelectionModeAction =
0349         new QAction(QIcon::fromTheme(QStringLiteral("labplot-zoom-select-x")), i18n("Select X-Region and Zoom In"), plotMouseModeActionGroup);
0350     cartesianPlotZoomXSelectionModeAction->setData(static_cast<int>(CartesianPlot::MouseMode::ZoomXSelection));
0351     cartesianPlotZoomXSelectionModeAction->setCheckable(true);
0352 
0353     cartesianPlotZoomYSelectionModeAction =
0354         new QAction(QIcon::fromTheme(QStringLiteral("labplot-zoom-select-y")), i18n("Select Y-Region and Zoom In"), plotMouseModeActionGroup);
0355     cartesianPlotZoomYSelectionModeAction->setData(static_cast<int>(CartesianPlot::MouseMode::ZoomYSelection));
0356     cartesianPlotZoomYSelectionModeAction->setCheckable(true);
0357 
0358     // TODO: change ICON
0359     cartesianPlotCursorModeAction = new QAction(QIcon::fromTheme(QStringLiteral("debug-execute-from-cursor")), i18n("Cursor"), plotMouseModeActionGroup);
0360     cartesianPlotCursorModeAction->setData(static_cast<int>(CartesianPlot::MouseMode::Cursor));
0361     cartesianPlotCursorModeAction->setCheckable(true);
0362 
0363     connect(plotMouseModeActionGroup, &QActionGroup::triggered, this, &WorksheetView::cartesianPlotMouseModeChanged);
0364 
0365     auto* cartesianPlotNavigationGroup = new QActionGroup(this);
0366     scaleAutoAction = new QAction(QIcon::fromTheme(QStringLiteral("labplot-auto-scale-all")), i18n("Auto Scale"), cartesianPlotNavigationGroup);
0367     scaleAutoAction->setData(static_cast<int>(CartesianPlot::NavigationOperation::ScaleAuto));
0368     scaleAutoAction->setShortcut(Qt::Key_1);
0369 
0370     scaleAutoXAction = new QAction(QIcon::fromTheme(QStringLiteral("labplot-auto-scale-x")), i18n("Auto Scale X"), cartesianPlotNavigationGroup);
0371     scaleAutoXAction->setData(static_cast<int>(CartesianPlot::NavigationOperation::ScaleAutoX));
0372     scaleAutoXAction->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_X);
0373 
0374     scaleAutoYAction = new QAction(QIcon::fromTheme(QStringLiteral("labplot-auto-scale-y")), i18n("Auto Scale Y"), cartesianPlotNavigationGroup);
0375     scaleAutoYAction->setData(static_cast<int>(CartesianPlot::NavigationOperation::ScaleAutoY));
0376     scaleAutoYAction->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_Y);
0377 
0378     zoomInAction = new QAction(QIcon::fromTheme(QStringLiteral("zoom-in")), i18n("Zoom In"), cartesianPlotNavigationGroup);
0379     zoomInAction->setData(static_cast<int>(CartesianPlot::NavigationOperation::ZoomIn));
0380     zoomInAction->setShortcut(Qt::Key_Plus);
0381 
0382     zoomOutAction = new QAction(QIcon::fromTheme(QStringLiteral("zoom-out")), i18n("Zoom Out"), cartesianPlotNavigationGroup);
0383     zoomOutAction->setData(static_cast<int>(CartesianPlot::NavigationOperation::ZoomOut));
0384     zoomOutAction->setShortcut(Qt::Key_Minus);
0385 
0386     zoomInXAction = new QAction(QIcon::fromTheme(QStringLiteral("labplot-zoom-in-x")), i18n("Zoom In X"), cartesianPlotNavigationGroup);
0387     zoomInXAction->setData(static_cast<int>(CartesianPlot::NavigationOperation::ZoomInX));
0388     zoomInXAction->setShortcut(Qt::Key_X);
0389 
0390     zoomOutXAction = new QAction(QIcon::fromTheme(QStringLiteral("labplot-zoom-out-x")), i18n("Zoom Out X"), cartesianPlotNavigationGroup);
0391     zoomOutXAction->setData(static_cast<int>(CartesianPlot::NavigationOperation::ZoomOutX));
0392     zoomOutXAction->setShortcut(Qt::SHIFT | Qt::Key_X);
0393 
0394     zoomInYAction = new QAction(QIcon::fromTheme(QStringLiteral("labplot-zoom-in-y")), i18n("Zoom In Y"), cartesianPlotNavigationGroup);
0395     zoomInYAction->setData(static_cast<int>(CartesianPlot::NavigationOperation::ZoomInY));
0396     zoomInYAction->setShortcut(Qt::Key_Y);
0397 
0398     zoomOutYAction = new QAction(QIcon::fromTheme(QStringLiteral("labplot-zoom-out-y")), i18n("Zoom Out Y"), cartesianPlotNavigationGroup);
0399     zoomOutYAction->setData(static_cast<int>(CartesianPlot::NavigationOperation::ZoomOutY));
0400     zoomOutYAction->setShortcut(Qt::SHIFT | Qt::Key_Y);
0401 
0402     shiftLeftXAction = new QAction(QIcon::fromTheme(QStringLiteral("labplot-shift-left-x")), i18n("Shift Left X"), cartesianPlotNavigationGroup);
0403     shiftLeftXAction->setData(static_cast<int>(CartesianPlot::NavigationOperation::ShiftLeftX));
0404     shiftRightXAction = new QAction(QIcon::fromTheme(QStringLiteral("labplot-shift-right-x")), i18n("Shift Right X"), cartesianPlotNavigationGroup);
0405     shiftRightXAction->setData(static_cast<int>(CartesianPlot::NavigationOperation::ShiftRightX));
0406     shiftUpYAction = new QAction(QIcon::fromTheme(QStringLiteral("labplot-shift-up-y")), i18n("Shift Up Y"), cartesianPlotNavigationGroup);
0407     shiftUpYAction->setData(static_cast<int>(CartesianPlot::NavigationOperation::ShiftUpY));
0408     shiftDownYAction = new QAction(QIcon::fromTheme(QStringLiteral("labplot-shift-down-y")), i18n("Shift Down Y"), cartesianPlotNavigationGroup);
0409     shiftDownYAction->setData(static_cast<int>(CartesianPlot::NavigationOperation::ShiftDownY));
0410 
0411     connect(cartesianPlotNavigationGroup, &QActionGroup::triggered, this, &WorksheetView::cartesianPlotNavigationChanged);
0412 
0413     m_plotActionsInitialized = true;
0414 
0415     handleCartesianPlotActions();
0416 }
0417 
0418 void WorksheetView::initMenus() {
0419     if (!m_actionsInitialized)
0420         initActions();
0421 
0422     m_addNewCartesianPlotMenu = new QMenu(i18n("Plot Area"), this);
0423     m_addNewCartesianPlotMenu->addAction(addCartesianPlot1Action);
0424     m_addNewCartesianPlotMenu->addAction(addCartesianPlot2Action);
0425     m_addNewCartesianPlotMenu->addAction(addCartesianPlot3Action);
0426     m_addNewCartesianPlotMenu->addAction(addCartesianPlot4Action);
0427     m_addNewCartesianPlotMenu->addSeparator();
0428     m_addNewCartesianPlotMenu->addAction(addCartesianPlotTemplateAction);
0429 
0430     m_addNewMenu = new QMenu(i18n("Add New"), this);
0431     m_addNewMenu->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
0432     m_addNewMenu->addMenu(m_addNewCartesianPlotMenu)->setIcon(QIcon::fromTheme(QStringLiteral("office-chart-line")));
0433     m_addNewMenu->addSeparator();
0434     m_addNewMenu->addAction(addTextLabelAction);
0435     m_addNewMenu->addAction(addImageAction);
0436     m_addNewMenu->addAction(addGlobalInfoElementAction);
0437 
0438     m_viewMouseModeMenu = new QMenu(i18n("Mouse Mode"), this);
0439     m_viewMouseModeMenu->setIcon(QIcon::fromTheme(QStringLiteral("input-mouse")));
0440     m_viewMouseModeMenu->addAction(selectionModeAction);
0441     m_viewMouseModeMenu->addAction(navigationModeAction);
0442     m_viewMouseModeMenu->addAction(zoomSelectionModeAction);
0443 
0444     m_zoomMenu = new QMenu(i18n("Zoom"), this);
0445     m_zoomMenu->setIcon(QIcon::fromTheme(QStringLiteral("zoom-draw")));
0446     m_zoomMenu->addAction(zoomInViewAction);
0447     m_zoomMenu->addAction(zoomOutViewAction);
0448     m_zoomMenu->addAction(zoomOriginAction);
0449     m_zoomMenu->addAction(zoomFitNoneAction);
0450     m_zoomMenu->addAction(zoomFitAction);
0451     m_zoomMenu->addAction(zoomFitPageHeightAction);
0452     m_zoomMenu->addAction(zoomFitPageWidthAction);
0453     m_zoomMenu->addAction(zoomFitSelectionAction);
0454 
0455     m_magnificationMenu = new QMenu(i18n("Magnification"), this);
0456     m_magnificationMenu->setIcon(QIcon::fromTheme(QStringLiteral("zoom-in")));
0457     m_magnificationMenu->addAction(noMagnificationAction);
0458     m_magnificationMenu->addAction(twoTimesMagnificationAction);
0459     m_magnificationMenu->addAction(threeTimesMagnificationAction);
0460     m_magnificationMenu->addAction(fourTimesMagnificationAction);
0461     m_magnificationMenu->addAction(fiveTimesMagnificationAction);
0462 
0463     m_layoutMenu = new QMenu(i18n("Layout"), this);
0464     m_layoutMenu->setIcon(QIcon::fromTheme(QStringLiteral("labplot-editbreaklayout")));
0465     m_layoutMenu->addAction(verticalLayoutAction);
0466     m_layoutMenu->addAction(horizontalLayoutAction);
0467     m_layoutMenu->addAction(gridLayoutAction);
0468     m_layoutMenu->addSeparator();
0469     m_layoutMenu->addAction(breakLayoutAction);
0470 
0471     m_gridMenu = new QMenu(i18n("Grid"), this);
0472     m_gridMenu->setIcon(QIcon::fromTheme(QStringLiteral("view-grid")));
0473     m_gridMenu->addAction(noGridAction);
0474     m_gridMenu->addSeparator();
0475     m_gridMenu->addAction(sparseLineGridAction);
0476     m_gridMenu->addAction(denseLineGridAction);
0477     m_gridMenu->addSeparator();
0478     m_gridMenu->addAction(sparseDotGridAction);
0479     m_gridMenu->addAction(denseDotGridAction);
0480     m_gridMenu->addSeparator();
0481     m_gridMenu->addAction(customGridAction);
0482     // TODO: implement "snap to grid" and activate this action
0483     //  m_gridMenu->addSeparator();
0484     //  m_gridMenu->addAction(snapToGridAction);
0485 
0486     m_cartesianPlotMenu = new QMenu(i18n("Plot Area"), this);
0487     m_cartesianPlotMenu->setIcon(QIcon::fromTheme(QStringLiteral("office-chart-line")));
0488 
0489     m_cartesianPlotMouseModeMenu = new QMenu(i18n("Mouse Mode"), this);
0490     m_cartesianPlotMouseModeMenu->setIcon(QIcon::fromTheme(QStringLiteral("input-mouse")));
0491     m_cartesianPlotMouseModeMenu->addAction(cartesianPlotSelectionModeAction);
0492     m_cartesianPlotMouseModeMenu->addAction(cartesianPlotZoomSelectionModeAction);
0493     m_cartesianPlotMouseModeMenu->addAction(cartesianPlotZoomXSelectionModeAction);
0494     m_cartesianPlotMouseModeMenu->addAction(cartesianPlotZoomYSelectionModeAction);
0495     m_cartesianPlotMouseModeMenu->addSeparator();
0496     m_cartesianPlotMouseModeMenu->addAction(cartesianPlotCursorModeAction);
0497     m_cartesianPlotMouseModeMenu->addSeparator();
0498 
0499     m_cartesianPlotZoomMenu = new QMenu(i18n("Zoom/Navigate"), this);
0500     m_cartesianPlotZoomMenu->setIcon(QIcon::fromTheme(QStringLiteral("zoom-draw")));
0501     m_cartesianPlotZoomMenu->addAction(scaleAutoAction);
0502     m_cartesianPlotZoomMenu->addAction(scaleAutoXAction);
0503     m_cartesianPlotZoomMenu->addAction(scaleAutoYAction);
0504     m_cartesianPlotZoomMenu->addSeparator();
0505     m_cartesianPlotZoomMenu->addAction(zoomInAction);
0506     m_cartesianPlotZoomMenu->addAction(zoomOutAction);
0507     m_cartesianPlotZoomMenu->addSeparator();
0508     m_cartesianPlotZoomMenu->addAction(zoomInXAction);
0509     m_cartesianPlotZoomMenu->addAction(zoomOutXAction);
0510     m_cartesianPlotZoomMenu->addSeparator();
0511     m_cartesianPlotZoomMenu->addAction(zoomInYAction);
0512     m_cartesianPlotZoomMenu->addAction(zoomOutYAction);
0513     m_cartesianPlotZoomMenu->addSeparator();
0514     m_cartesianPlotZoomMenu->addAction(shiftLeftXAction);
0515     m_cartesianPlotZoomMenu->addAction(shiftRightXAction);
0516     m_cartesianPlotZoomMenu->addSeparator();
0517     m_cartesianPlotZoomMenu->addAction(shiftUpYAction);
0518     m_cartesianPlotZoomMenu->addAction(shiftDownYAction);
0519 
0520     m_cartesianPlotActionModeMenu = new QMenu(i18n("Apply Actions to"), this);
0521     m_cartesianPlotActionModeMenu->setIcon(QIcon::fromTheme(QStringLiteral("dialog-ok-apply")));
0522     m_cartesianPlotActionModeMenu->addAction(cartesianPlotApplyToSelectionAction);
0523     m_cartesianPlotActionModeMenu->addAction(cartesianPlotApplyToAllAction);
0524     m_cartesianPlotActionModeMenu->addAction(cartesianPlotApplyToAllXAction);
0525     m_cartesianPlotActionModeMenu->addAction(cartesianPlotApplyToAllYAction);
0526 
0527     m_cartesianPlotCursorModeMenu = new QMenu(i18n("Apply Cursor to"), this);
0528     m_cartesianPlotCursorModeMenu->setIcon(QIcon::fromTheme(QStringLiteral("debug-execute-from-cursor")));
0529     m_cartesianPlotCursorModeMenu->addAction(cartesianPlotApplyToSelectionCursor);
0530     m_cartesianPlotCursorModeMenu->addAction(cartesianPlotApplyToAllCursor);
0531 
0532     m_cartesianPlotMenu->addAction(cartesianPlotAddNewAction);
0533     m_cartesianPlotMenu->addSeparator();
0534     m_cartesianPlotMenu->addMenu(m_cartesianPlotMouseModeMenu);
0535     m_cartesianPlotMenu->addMenu(m_cartesianPlotZoomMenu);
0536     m_cartesianPlotMenu->addSeparator();
0537     m_cartesianPlotMenu->addMenu(m_cartesianPlotActionModeMenu);
0538     m_cartesianPlotMenu->addMenu(m_cartesianPlotCursorModeMenu);
0539 
0540     // themes menu
0541     m_themeMenu = new QMenu(i18n("Theme"), this);
0542     m_themeMenu->setIcon(QIcon::fromTheme(QStringLiteral("color-management")));
0543 #ifndef SDK
0544     connect(m_themeMenu, &QMenu::aboutToShow, this, [=]() {
0545         if (!m_themeMenu->isEmpty())
0546             return;
0547         auto* themeWidget = new ThemesWidget(nullptr);
0548         themeWidget->setFixedMode();
0549         connect(themeWidget, &ThemesWidget::themeSelected, m_worksheet, &Worksheet::setTheme);
0550         connect(themeWidget, &ThemesWidget::themeSelected, m_themeMenu, &QMenu::close);
0551 
0552         auto* widgetAction = new QWidgetAction(this);
0553         widgetAction->setDefaultWidget(themeWidget);
0554         m_themeMenu->addAction(widgetAction);
0555     });
0556 #endif
0557     m_menusInitialized = true;
0558 }
0559 
0560 /*!
0561  * Populates the menu \c menu with the worksheet and worksheet view relevant actions.
0562  * The menu is used
0563  *   - as the context menu in WorksheetView
0564  *   - as the "worksheet menu" in the main menu-bar (called form MainWin)
0565  *   - as a part of the worksheet context menu in project explorer
0566  */
0567 void WorksheetView::createContextMenu(QMenu* menu) {
0568     Q_ASSERT(menu != nullptr);
0569 
0570     if (!m_menusInitialized)
0571         initMenus();
0572 
0573     QAction* firstAction = nullptr;
0574     // if we're populating the context menu for the project explorer, then
0575     // there're already actions available there. Skip the first title-action
0576     // and insert the action at the beginning of the menu.
0577     if (menu->actions().size() > 1)
0578         firstAction = menu->actions().at(1);
0579 
0580     menu->insertMenu(firstAction, m_addNewMenu);
0581     menu->insertSeparator(firstAction);
0582     menu->insertMenu(firstAction, m_viewMouseModeMenu);
0583     menu->insertMenu(firstAction, m_zoomMenu);
0584     menu->insertMenu(firstAction, m_magnificationMenu);
0585     menu->insertSeparator(firstAction);
0586     menu->insertMenu(firstAction, m_layoutMenu);
0587     menu->insertMenu(firstAction, m_gridMenu);
0588     menu->insertSeparator(firstAction);
0589     menu->insertMenu(firstAction, m_themeMenu);
0590     menu->insertSeparator(firstAction);
0591     menu->insertAction(firstAction, plotsInteractiveAction);
0592     menu->insertSeparator(firstAction);
0593     menu->insertMenu(firstAction, m_cartesianPlotMenu);
0594     menu->insertSeparator(firstAction);
0595     menu->insertAction(firstAction, showPresenterMode);
0596     menu->insertSeparator(firstAction);
0597 }
0598 
0599 void WorksheetView::fillToolBar(QToolBar* toolBar) {
0600     toolBar->addSeparator();
0601     tbNewCartesianPlot = new QToolButton(toolBar);
0602     tbNewCartesianPlot->setPopupMode(QToolButton::MenuButtonPopup);
0603     tbNewCartesianPlot->setMenu(m_addNewCartesianPlotMenu);
0604     tbNewCartesianPlot->setDefaultAction(addCartesianPlot1Action);
0605     toolBar->addWidget(tbNewCartesianPlot);
0606     toolBar->addAction(addTextLabelAction);
0607     toolBar->addAction(addImageAction);
0608 
0609     toolBar->addSeparator();
0610     toolBar->addAction(verticalLayoutAction);
0611     toolBar->addAction(horizontalLayoutAction);
0612     toolBar->addAction(gridLayoutAction);
0613     toolBar->addAction(breakLayoutAction);
0614 
0615     toolBar->addSeparator();
0616     toolBar->addAction(selectionModeAction);
0617     toolBar->addAction(navigationModeAction);
0618     toolBar->addAction(zoomSelectionModeAction);
0619     toolBar->addSeparator();
0620     tbZoom = new QToolButton(toolBar);
0621     tbZoom->setPopupMode(QToolButton::MenuButtonPopup);
0622     tbZoom->setMenu(m_zoomMenu);
0623     tbZoom->setDefaultAction(currentZoomAction);
0624     toolBar->addWidget(tbZoom);
0625 
0626     tbMagnification = new QToolButton(toolBar);
0627     tbMagnification->setPopupMode(QToolButton::MenuButtonPopup);
0628     tbMagnification->setMenu(m_magnificationMenu);
0629     tbMagnification->setDefaultAction(currentMagnificationAction);
0630     toolBar->addWidget(tbMagnification);
0631 }
0632 
0633 #ifdef HAVE_TOUCHBAR
0634 void WorksheetView::fillTouchBar(KDMacTouchBar* touchBar) {
0635     // touchBar->addAction(addCartesianPlot1Action);
0636     touchBar->addAction(zoomInViewAction);
0637     touchBar->addAction(zoomOutViewAction);
0638     touchBar->addAction(showPresenterMode);
0639 }
0640 #endif
0641 
0642 void WorksheetView::fillCartesianPlotToolBar(QToolBar* toolBar) {
0643     toolBar->addWidget(tbCartesianPlotAddNew);
0644     toolBar->addSeparator();
0645     fillCartesianPlotNavigationToolBar(toolBar);
0646     toolBar->addSeparator();
0647 
0648     handleCartesianPlotActions();
0649 }
0650 
0651 void WorksheetView::fillCartesianPlotNavigationToolBar(QToolBar* toolBar, bool enableCursor) const {
0652     toolBar->addAction(cartesianPlotSelectionModeAction);
0653     toolBar->addAction(cartesianPlotCrosshairModeAction);
0654     toolBar->addAction(cartesianPlotZoomSelectionModeAction);
0655     toolBar->addAction(cartesianPlotZoomXSelectionModeAction);
0656     toolBar->addAction(cartesianPlotZoomYSelectionModeAction);
0657     if (enableCursor)
0658         toolBar->addAction(cartesianPlotCursorModeAction);
0659     toolBar->addSeparator();
0660     toolBar->addAction(scaleAutoAction);
0661     toolBar->addAction(scaleAutoXAction);
0662     toolBar->addAction(scaleAutoYAction);
0663     toolBar->addAction(zoomInAction);
0664     toolBar->addAction(zoomOutAction);
0665     toolBar->addAction(zoomInXAction);
0666     toolBar->addAction(zoomOutXAction);
0667     toolBar->addAction(zoomInYAction);
0668     toolBar->addAction(zoomOutYAction);
0669     toolBar->addAction(shiftLeftXAction);
0670     toolBar->addAction(shiftRightXAction);
0671     toolBar->addAction(shiftUpYAction);
0672     toolBar->addAction(shiftDownYAction);
0673 }
0674 
0675 void WorksheetView::setScene(QGraphicsScene* scene) {
0676     QGraphicsView::setScene(scene);
0677 }
0678 
0679 void WorksheetView::setIsClosing() {
0680     m_isClosing = true;
0681 }
0682 
0683 void WorksheetView::setCartesianPlotActionMode(Worksheet::CartesianPlotActionMode mode) {
0684     if (mode == Worksheet::CartesianPlotActionMode::ApplyActionToAll)
0685         cartesianPlotApplyToAllAction->setChecked(true);
0686     else if (mode == Worksheet::CartesianPlotActionMode::ApplyActionToAllX)
0687         cartesianPlotApplyToAllXAction->setChecked(true);
0688     else if (mode == Worksheet::CartesianPlotActionMode::ApplyActionToAllY)
0689         cartesianPlotApplyToAllYAction->setChecked(true);
0690     else
0691         cartesianPlotApplyToSelectionAction->setChecked(true);
0692 }
0693 
0694 void WorksheetView::setCartesianPlotCursorMode(Worksheet::CartesianPlotActionMode mode) {
0695     if (mode == Worksheet::CartesianPlotActionMode::ApplyActionToAll)
0696         cartesianPlotApplyToAllCursor->setChecked(true);
0697     else
0698         cartesianPlotApplyToSelectionCursor->setChecked(true);
0699 }
0700 
0701 void WorksheetView::setPlotInteractive(bool interactive) {
0702     plotsInteractiveAction->setChecked(interactive);
0703 }
0704 
0705 void WorksheetView::drawForeground(QPainter* painter, const QRectF& rect) {
0706     // QDEBUG(Q_FUNC_INFO << ", painter = " << painter << ", rect = " << rect)
0707     if (m_mouseMode == MouseMode::ZoomSelection && m_selectionBandIsShown) {
0708         painter->save();
0709         const QRectF& selRect = mapToScene(QRect(m_selectionStart, m_selectionEnd).normalized()).boundingRect();
0710         // TODO: don't hardcode for black here, use a a different color depending on the theme of the worksheet/plot under the mouse cursor?
0711         painter->setPen(QPen(Qt::black, 5 / transform().m11()));
0712         painter->drawRect(selRect);
0713         painter->setBrush(QApplication::palette().color(QPalette::Highlight));
0714         painter->setOpacity(0.2);
0715         painter->drawRect(selRect);
0716         painter->restore();
0717     }
0718     //  DEBUG(Q_FUNC_INFO << ", CALLING QGraphicsView::drawForeground. items = " << QGraphicsView::items().size()
0719     //      << ", scene items = " << scene()->items().count() )
0720     QGraphicsView::drawForeground(painter, rect);
0721 }
0722 
0723 void WorksheetView::drawBackgroundItems(QPainter* painter, const QRectF& scene_rect) {
0724     // canvas
0725     const auto* background = m_worksheet->background();
0726     painter->setOpacity(background->opacity());
0727     if (background->type() == Background::Type::Color) {
0728         switch (background->colorStyle()) {
0729         case Background::ColorStyle::SingleColor: {
0730             painter->setBrush(QBrush(background->firstColor()));
0731             break;
0732         }
0733         case Background::ColorStyle::HorizontalLinearGradient: {
0734             QLinearGradient linearGrad(scene_rect.topLeft(), scene_rect.topRight());
0735             linearGrad.setColorAt(0, background->firstColor());
0736             linearGrad.setColorAt(1, background->secondColor());
0737             painter->setBrush(QBrush(linearGrad));
0738             break;
0739         }
0740         case Background::ColorStyle::VerticalLinearGradient: {
0741             QLinearGradient linearGrad(scene_rect.topLeft(), scene_rect.bottomLeft());
0742             linearGrad.setColorAt(0, background->firstColor());
0743             linearGrad.setColorAt(1, background->secondColor());
0744             painter->setBrush(QBrush(linearGrad));
0745             break;
0746         }
0747         case Background::ColorStyle::TopLeftDiagonalLinearGradient: {
0748             QLinearGradient linearGrad(scene_rect.topLeft(), scene_rect.bottomRight());
0749             linearGrad.setColorAt(0, background->firstColor());
0750             linearGrad.setColorAt(1, background->secondColor());
0751             painter->setBrush(QBrush(linearGrad));
0752             break;
0753         }
0754         case Background::ColorStyle::BottomLeftDiagonalLinearGradient: {
0755             QLinearGradient linearGrad(scene_rect.bottomLeft(), scene_rect.topRight());
0756             linearGrad.setColorAt(0, background->firstColor());
0757             linearGrad.setColorAt(1, background->secondColor());
0758             painter->setBrush(QBrush(linearGrad));
0759             break;
0760         }
0761         case Background::ColorStyle::RadialGradient: {
0762             QRadialGradient radialGrad(scene_rect.center(), scene_rect.width() / 2);
0763             radialGrad.setColorAt(0, background->firstColor());
0764             radialGrad.setColorAt(1, background->secondColor());
0765             painter->setBrush(QBrush(radialGrad));
0766             break;
0767         }
0768             // default:
0769             //  painter->setBrush(QBrush(m_worksheet->backgroundFirstColor()));
0770         }
0771         painter->drawRect(scene_rect);
0772     } else if (background->type() == Background::Type::Image) { // background image
0773         const QString& backgroundFileName = background->fileName().trimmed();
0774         if (!backgroundFileName.isEmpty()) {
0775             QPixmap pix(backgroundFileName);
0776             switch (background->imageStyle()) {
0777             case Background::ImageStyle::ScaledCropped:
0778                 pix = pix.scaled(scene_rect.size().toSize(), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
0779                 painter->drawPixmap(scene_rect.topLeft(), pix);
0780                 break;
0781             case Background::ImageStyle::Scaled:
0782                 pix = pix.scaled(scene_rect.size().toSize(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
0783                 painter->drawPixmap(scene_rect.topLeft(), pix);
0784                 break;
0785             case Background::ImageStyle::ScaledAspectRatio:
0786                 pix = pix.scaled(scene_rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
0787                 painter->drawPixmap(scene_rect.topLeft(), pix);
0788                 break;
0789             case Background::ImageStyle::Centered:
0790                 painter->drawPixmap(QPointF(scene_rect.center().x() - pix.size().width() / 2, scene_rect.center().y() - pix.size().height() / 2), pix);
0791                 break;
0792             case Background::ImageStyle::Tiled:
0793                 painter->drawTiledPixmap(scene_rect, pix);
0794                 break;
0795             case Background::ImageStyle::CenterTiled:
0796                 painter->drawTiledPixmap(scene_rect, pix, QPoint(scene_rect.size().width() / 2, scene_rect.size().height() / 2));
0797                 break;
0798                 // default:
0799                 //  painter->drawPixmap(scene_rect.topLeft(),pix);
0800             }
0801         }
0802     } else if (background->type() == Background::Type::Pattern) { // background pattern
0803         painter->setBrush(QBrush(background->firstColor(), background->brushStyle()));
0804         painter->drawRect(scene_rect);
0805     }
0806 
0807     // grid
0808     if (m_gridSettings.style != GridStyle::NoGrid && !m_isPrinting) {
0809         QColor c = m_gridSettings.color;
0810         c.setAlphaF(m_gridSettings.opacity);
0811         painter->setPen(c);
0812 
0813         qreal x, y;
0814         qreal left = scene_rect.left();
0815         qreal right = scene_rect.right();
0816         qreal top = scene_rect.top();
0817         qreal bottom = scene_rect.bottom();
0818 
0819         if (m_gridSettings.style == GridStyle::Line) {
0820             QLineF line;
0821 
0822             // horizontal lines
0823             y = top + m_gridSettings.verticalSpacing;
0824             while (y < bottom) {
0825                 line.setLine(left, y, right, y);
0826                 painter->drawLine(line);
0827                 y += m_gridSettings.verticalSpacing;
0828             }
0829 
0830             // vertical lines
0831             x = left + m_gridSettings.horizontalSpacing;
0832             while (x < right) {
0833                 line.setLine(x, top, x, bottom);
0834                 painter->drawLine(line);
0835                 x += m_gridSettings.horizontalSpacing;
0836             }
0837         } else { // DotGrid
0838             y = top + m_gridSettings.verticalSpacing;
0839             while (y < bottom) {
0840                 x = left; // + m_gridSettings.horizontalSpacing;
0841                 while (x < right) {
0842                     x += m_gridSettings.horizontalSpacing;
0843                     painter->drawPoint(x, y);
0844                 }
0845                 y += m_gridSettings.verticalSpacing;
0846             }
0847         }
0848     }
0849 }
0850 
0851 void WorksheetView::drawBackground(QPainter* painter, const QRectF& rect) {
0852     painter->save();
0853 
0854     // painter->setRenderHint(QPainter::Antialiasing);
0855     QRectF scene_rect = sceneRect();
0856 
0857     if (!m_worksheet->useViewSize()) {
0858         // background
0859         KColorScheme scheme(QPalette::Active, KColorScheme::Window);
0860         const QColor color = scheme.background().color();
0861         if (!scene_rect.contains(rect))
0862             painter->fillRect(rect, color);
0863 
0864         // shadow
0865         //      int shadowSize = scene_rect.width()*0.02;
0866         //      QRectF rightShadowRect(scene_rect.right(), scene_rect.top() + shadowSize, shadowSize, scene_rect.height());
0867         //      QRectF bottomShadowRect(scene_rect.left() + shadowSize, scene_rect.bottom(), scene_rect.width(), shadowSize);
0868         //
0869         //      const QColor& shadeColor = scheme.shade(color, KColorScheme::MidShade);
0870         //      painter->fillRect(rightShadowRect.intersected(rect), shadeColor);
0871         //      painter->fillRect(bottomShadowRect.intersected(rect), shadeColor);
0872     }
0873 
0874     drawBackgroundItems(painter, scene_rect);
0875 
0876     invalidateScene(rect, QGraphicsScene::BackgroundLayer);
0877     painter->restore();
0878 }
0879 
0880 bool WorksheetView::isPlotAtPos(QPoint pos) const {
0881     QGraphicsItem* item = itemAt(pos);
0882     if (item) {
0883         // Not every element is a WorksheetElementPrivate, for example the
0884         // ScaledTextItem of the Textlabel. Therefore it must be checked
0885         // it it is a WorksheetElementPrivate or not
0886         const auto* w = dynamic_cast<WorksheetElementPrivate*>(item);
0887         if (w && ((w->q->type() == AspectType::CartesianPlot) || w->q->parent(AspectType::CartesianPlot)))
0888             return true;
0889     }
0890 
0891     return false;
0892 }
0893 
0894 CartesianPlot* WorksheetView::plotAt(QPoint pos) const {
0895     QGraphicsItem* item = itemAt(pos);
0896     if (!item)
0897         return nullptr;
0898 
0899     QGraphicsItem* plotItem = nullptr;
0900     if (item->data(0).toInt() == static_cast<int>(AspectType::CartesianPlot))
0901         plotItem = item;
0902     else {
0903         if (item->parentItem() && item->parentItem()->data(0).toInt() == static_cast<int>(AspectType::CartesianPlot))
0904             plotItem = item->parentItem();
0905     }
0906 
0907     if (plotItem == nullptr)
0908         return nullptr;
0909 
0910     CartesianPlot* plot = nullptr;
0911     for (auto* p : m_worksheet->children<CartesianPlot>()) {
0912         if (p->graphicsItem() == plotItem) {
0913             plot = p;
0914             break;
0915         }
0916     }
0917 
0918     return plot;
0919 }
0920 
0921 // ##############################################################################
0922 // ####################################  Events   ###############################
0923 // ##############################################################################
0924 void WorksheetView::resizeEvent(QResizeEvent* event) {
0925     if (m_isClosing)
0926         return;
0927 
0928     if (m_worksheet->useViewSize())
0929         this->processResize();
0930     else
0931         updateFit();
0932 
0933     QGraphicsView::resizeEvent(event);
0934 }
0935 
0936 void WorksheetView::wheelEvent(QWheelEvent* event) {
0937     if (isInteractive() && (m_mouseMode == MouseMode::ZoomSelection || (QApplication::keyboardModifiers() & Qt::ControlModifier))) {
0938         if (!zoomFitNoneAction)
0939             initActions();
0940         zoomFitNoneAction->setChecked(true);
0941         m_worksheet->setZoomFit(Worksheet::ZoomFit::None);
0942         updateScrollBarPolicy();
0943 
0944         // https://wiki.qt.io/Smooth_Zoom_In_QGraphicsView
0945         QPoint numDegrees = event->angleDelta() / 8;
0946         int numSteps = numDegrees.y() / 15; // see QWheelEvent documentation
0947         zoom(numSteps);
0948     } else
0949         QGraphicsView::wheelEvent(event);
0950 
0951     if (m_magnificationWindow && m_magnificationWindow->isVisible())
0952 #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
0953         updateMagnificationWindow(mapToScene(event->position().toPoint()));
0954 #else
0955         updateMagnificationWindow(mapToScene(event->pos()));
0956 #endif
0957 }
0958 
0959 void WorksheetView::zoom(int numSteps) {
0960     m_numScheduledScalings += numSteps;
0961     if (m_numScheduledScalings * numSteps < 0) // if user moved the wheel in another direction, we reset previously scheduled scalings
0962         m_numScheduledScalings = numSteps;
0963 
0964     auto* anim = new QTimeLine(350, this);
0965     anim->setUpdateInterval(20);
0966 
0967     connect(anim, &QTimeLine::valueChanged, this, &WorksheetView::scalingTime);
0968     connect(anim, &QTimeLine::finished, this, &WorksheetView::animFinished);
0969     anim->start();
0970 }
0971 
0972 void WorksheetView::scalingTime() {
0973     qreal factor = 1.0 + qreal(m_numScheduledScalings) / 300.0;
0974     scale(factor, factor);
0975 }
0976 
0977 void WorksheetView::animFinished() {
0978     if (m_numScheduledScalings > 0)
0979         m_numScheduledScalings--;
0980     else
0981         m_numScheduledScalings++;
0982     sender()->~QObject();
0983 
0984     updateLabelsZoom();
0985 }
0986 
0987 void WorksheetView::mousePressEvent(QMouseEvent* event) {
0988     // prevent the deselection of items when context menu event
0989     // was triggered (right button click)
0990     if (event->button() == Qt::RightButton) {
0991         event->accept();
0992         return;
0993     }
0994 
0995     if (event->button() == Qt::LeftButton && m_mouseMode == MouseMode::ZoomSelection) {
0996         m_selectionStart = event->pos();
0997         m_selectionEnd = m_selectionStart; // select&zoom'g starts -> reset the end point to the start point
0998         m_selectionBandIsShown = true;
0999         QGraphicsView::mousePressEvent(event);
1000         return;
1001     }
1002 
1003     // select the worksheet in the project explorer if the view was clicked
1004     // and there is no selection currently. We need this for the case when
1005     // there is a single worksheet in the project and we change from the project-node
1006     // in the project explorer to the worksheet-node by clicking the view.
1007     if (scene()->selectedItems().isEmpty())
1008         m_worksheet->setSelectedInView(true);
1009 
1010     QGraphicsView::mousePressEvent(event);
1011 }
1012 
1013 void WorksheetView::mouseReleaseEvent(QMouseEvent* event) {
1014     if (event->button() == Qt::LeftButton && m_mouseMode == MouseMode::ZoomSelection) {
1015         m_selectionBandIsShown = false;
1016         viewport()->repaint(QRect(m_selectionStart, m_selectionEnd).normalized());
1017 
1018         // don't zoom if very small region was selected, avoid occasional/unwanted zooming
1019         m_selectionEnd = event->pos();
1020         if (abs(m_selectionEnd.x() - m_selectionStart.x()) > 20 && abs(m_selectionEnd.y() - m_selectionStart.y()) > 20)
1021             fitInView(mapToScene(QRect(m_selectionStart, m_selectionEnd).normalized()).boundingRect(), Qt::KeepAspectRatio);
1022     }
1023 
1024     QGraphicsView::mouseReleaseEvent(event);
1025 }
1026 
1027 void WorksheetView::mouseDoubleClickEvent(QMouseEvent*) {
1028     Q_EMIT propertiesExplorerRequested();
1029 }
1030 
1031 void WorksheetView::mouseMoveEvent(QMouseEvent* event) {
1032     if (m_suppressSelectionChangedEvent)
1033         return QGraphicsView::mouseMoveEvent(event);
1034     if (m_mouseMode == MouseMode::Selection && m_cartesianPlotMouseMode != CartesianPlot::MouseMode::Selection) {
1035         // check whether there is a cartesian plot under the cursor
1036         // and set the cursor appearance according to the current mouse mode for the cartesian plots
1037         if (isPlotAtPos(event->pos())) {
1038             if (m_cartesianPlotMouseMode == CartesianPlot::MouseMode::ZoomSelection)
1039                 setCursor(Qt::CrossCursor);
1040             else if (m_cartesianPlotMouseMode == CartesianPlot::MouseMode::ZoomXSelection)
1041                 setCursor(Qt::SizeHorCursor);
1042             else if (m_cartesianPlotMouseMode == CartesianPlot::MouseMode::ZoomYSelection)
1043                 setCursor(Qt::SizeVerCursor);
1044         } else
1045             setCursor(Qt::ArrowCursor);
1046     } else if (m_mouseMode == MouseMode::Selection && m_cartesianPlotMouseMode == CartesianPlot::MouseMode::Selection)
1047         setCursor(Qt::ArrowCursor);
1048     else if (m_selectionBandIsShown) {
1049         QRect rect = QRect(m_selectionStart, m_selectionEnd).normalized();
1050         m_selectionEnd = event->pos();
1051         rect = rect.united(QRect(m_selectionStart, m_selectionEnd).normalized());
1052         qreal penWidth = 5 / transform().m11();
1053         rect.setX(rect.x() - penWidth);
1054         rect.setY(rect.y() - penWidth);
1055         rect.setHeight(rect.height() + 2 * penWidth);
1056         rect.setWidth(rect.width() + 2 * penWidth);
1057         viewport()->repaint(rect);
1058     }
1059 
1060     // show the magnification window
1061     if (magnificationFactor /*&& m_mouseMode == SelectAndEditMode*/) {
1062         if (!m_magnificationWindow) {
1063             m_magnificationWindow = new QGraphicsPixmapItem(nullptr);
1064             m_magnificationWindow->setZValue(std::numeric_limits<int>::max());
1065             scene()->addItem(m_magnificationWindow);
1066         }
1067 
1068         updateMagnificationWindow(mapToScene(event->pos()));
1069     } else if (m_magnificationWindow)
1070         m_magnificationWindow->setVisible(false);
1071 
1072     QGraphicsView::mouseMoveEvent(event);
1073 }
1074 
1075 /*!
1076  * Updates the magnified content of the scene under the cursor that is shown in the magnification window.
1077  * \pos is the current position of the cursor in scene coordinates which defines the middle of the magnification window.
1078  */
1079 void WorksheetView::updateMagnificationWindow(const QPointF& pos) {
1080     m_magnificationWindow->setVisible(false);
1081 
1082     // copy the part of the view to be shown magnified
1083     const int size = Worksheet::convertToSceneUnits(2.0, Worksheet::Unit::Centimeter) / transform().m11();
1084     const QRectF copyRect(pos.x() - size / (2 * magnificationFactor),
1085                           pos.y() - size / (2 * magnificationFactor),
1086                           size / magnificationFactor,
1087                           size / magnificationFactor);
1088     QPixmap px = grab(mapFromScene(copyRect).boundingRect());
1089     px = px.scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
1090 
1091     // draw the bounding rect
1092     QPainter painter(&px);
1093     const QPen pen = QPen(Qt::darkGray, 2 / transform().m11());
1094     painter.setPen(pen);
1095     QRect rect = px.rect();
1096     rect.setWidth(rect.width() - pen.widthF() / 2);
1097     rect.setHeight(rect.height() - pen.widthF() / 2);
1098     painter.drawRect(rect);
1099 
1100     // set the pixmap and show it again
1101     m_magnificationWindow->setPixmap(px);
1102     m_magnificationWindow->setPos(pos.x() - px.width() / 2, pos.y() - px.height() / 2);
1103     m_magnificationWindow->setVisible(true);
1104 }
1105 
1106 void WorksheetView::contextMenuEvent(QContextMenuEvent* e) {
1107     if ((m_magnificationWindow && m_magnificationWindow->isVisible() && items(e->pos()).size() == 1) || !itemAt(e->pos())) {
1108         // no item or only the magnification window under the cursor -> show the context menu for the worksheet
1109         m_cursorPos = mapToScene(e->pos());
1110         m_calledFromContextMenu = true;
1111         m_worksheet->createContextMenu()->exec(QCursor::pos());
1112     } else {
1113         // propagate the event to the scene and graphics items
1114         QGraphicsView::contextMenuEvent(e);
1115     }
1116 }
1117 
1118 void WorksheetView::keyPressEvent(QKeyEvent* event) {
1119     // handle delete
1120     if (event->matches(QKeySequence::Delete)) {
1121         deleteElement();
1122         QGraphicsView::keyPressEvent(event);
1123         return;
1124     }
1125 
1126     // handle copy/paste/duplicate
1127 
1128     // determine the currently selected aspect
1129     AbstractAspect* aspect = nullptr;
1130     if (m_selectedItems.count() == 1) {
1131         // at the moment we allow to copy/paste/duplicate one single selcted object only
1132         const auto children = m_worksheet->children<WorksheetElement>(AbstractAspect::ChildIndexFlag::Recursive);
1133         const auto* item = m_selectedItems.constFirst();
1134         for (auto* child : children) {
1135             if (child->graphicsItem() == item) {
1136                 aspect = child;
1137                 break;
1138             }
1139         }
1140     } else
1141         aspect = m_worksheet;
1142 
1143     if (!aspect) {
1144         QGraphicsView::keyPressEvent(event);
1145         return;
1146     }
1147 
1148     if (event->matches(QKeySequence::Copy)) {
1149         exportToClipboard(); // export the image to the clipboard
1150         if (aspect != m_worksheet)
1151             aspect->copy(); // copy the selected object itself
1152     } else if (event->matches(QKeySequence::Paste)) {
1153         // paste
1154         QString name;
1155         auto t = AbstractAspect::clipboardAspectType(name);
1156         if (t != AspectType::AbstractAspect && aspect->pasteTypes().indexOf(t) != -1)
1157             aspect->paste();
1158     } else if ((event->modifiers() & Qt::ControlModifier) && (event->key() == Qt::Key_D) && aspect != m_worksheet) {
1159         // duplicate
1160         aspect->copy();
1161         aspect->parentAspect()->paste(true);
1162 
1163         /* zooming related key events, handle them here so we can also use them in PresenterWidget without registering shortcuts */
1164     } else if ((event->modifiers() & Qt::ControlModifier) && (event->key() == Qt::Key_Plus)) {
1165         changeZoom(zoomInViewAction);
1166     } else if ((event->modifiers() & Qt::ControlModifier) && (event->key() == Qt::Key_Minus)) {
1167         changeZoom(zoomOutViewAction);
1168     } else if ((event->modifiers() & Qt::ControlModifier) && (event->key() == Qt::Key_1)) {
1169         changeZoom(zoomOriginAction);
1170     } else if (event->key() == 32) {
1171         // space key - hide/show the current object
1172         auto* we = dynamic_cast<WorksheetElement*>(aspect);
1173         if (we)
1174             we->setVisible(!we->isVisible());
1175     } else if (aspect->type() == AspectType::CartesianPlot && m_worksheet->layout() != Worksheet::Layout::NoLayout) {
1176         // use the arrow keys to navigate only if a layout is active in the worksheet.
1177         // without any layout the arrow keys are used to move the plot within the worksheet
1178         if (event->key() == Qt::Key_Left)
1179             cartesianPlotNavigationChanged(shiftRightXAction);
1180         else if (event->key() == Qt::Key_Right)
1181             cartesianPlotNavigationChanged(shiftLeftXAction);
1182         else if (event->key() == Qt::Key_Up)
1183             cartesianPlotNavigationChanged(shiftDownYAction);
1184         else if (event->key() == Qt::Key_Down)
1185             cartesianPlotNavigationChanged(shiftUpYAction);
1186     }
1187 
1188     QGraphicsView::keyPressEvent(event);
1189 }
1190 
1191 void WorksheetView::keyReleaseEvent(QKeyEvent* event) {
1192     QGraphicsView::keyReleaseEvent(event);
1193 }
1194 
1195 void WorksheetView::dragEnterEvent(QDragEnterEvent* event) {
1196     // ignore events not related to internal drags of columns etc., e.g. dropping of external files onto LabPlot
1197     const auto* mimeData = event->mimeData();
1198     if (!mimeData) {
1199         event->ignore();
1200         return;
1201     }
1202 
1203     if (mimeData->formats().at(0) != QLatin1String("labplot-dnd")) {
1204         event->ignore();
1205         return;
1206     }
1207 
1208     // select the worksheet in the project explorer and bring the view to the foreground
1209     m_worksheet->setSelectedInView(true);
1210     m_worksheet->dockWidget()->dockManager()->setDockWidgetFocused(m_worksheet->dockWidget());
1211 
1212     event->setAccepted(true);
1213 }
1214 
1215 void WorksheetView::dragMoveEvent(QDragMoveEvent* event) {
1216     // only accept drop events if we have a plot under the cursor where we can drop columns onto
1217 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
1218     bool plot = isPlotAtPos(event->position().toPoint());
1219 #else
1220     bool plot = isPlotAtPos(event->pos());
1221 #endif
1222     event->setAccepted(plot);
1223 }
1224 
1225 void WorksheetView::dropEvent(QDropEvent* event) {
1226 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
1227     auto* plot = plotAt(event->position().toPoint());
1228 #else
1229     auto* plot = plotAt(event->pos());
1230 #endif
1231     if (!plot)
1232         return;
1233 
1234     const auto* mimeData = event->mimeData();
1235     plot->processDropEvent(plot->project()->droppedAspects(mimeData));
1236 }
1237 
1238 // ##############################################################################
1239 // ####################################  SLOTs   ################################
1240 // ##############################################################################
1241 void WorksheetView::useViewSizeChanged(bool useViewSize) {
1242     if (!m_actionsInitialized)
1243         initActions();
1244 
1245     updateScrollBarPolicy();
1246 
1247     if (useViewSize) {
1248         zoomFitPageHeightAction->setVisible(false);
1249         zoomFitPageWidthAction->setVisible(false);
1250         currentZoomAction = zoomInViewAction;
1251         if (tbZoom)
1252             tbZoom->setDefaultAction(zoomInViewAction);
1253 
1254         // determine and set the current view size
1255         this->processResize();
1256     } else {
1257         zoomFitPageHeightAction->setVisible(true);
1258         zoomFitPageWidthAction->setVisible(true);
1259     }
1260 }
1261 
1262 void WorksheetView::processResize() {
1263     if (size() != sceneRect().size()) {
1264         static const float hscale = QApplication::primaryScreen()->physicalDotsPerInchX() / (Worksheet::convertToSceneUnits(1, Worksheet::Unit::Inch));
1265         static const float vscale = QApplication::primaryScreen()->physicalDotsPerInchY() / (Worksheet::convertToSceneUnits(1, Worksheet::Unit::Inch));
1266         m_worksheet->setUndoAware(false);
1267         m_worksheet->setPageRect(QRectF(0.0, 0.0, width() / hscale, height() / vscale));
1268         m_worksheet->setUndoAware(true);
1269     }
1270 }
1271 
1272 void WorksheetView::changeZoom(QAction* action) {
1273     zoomFitNoneAction->setChecked(true);
1274     m_worksheet->setZoomFit(Worksheet::ZoomFit::None);
1275     if (action == zoomInViewAction)
1276         zoom(1);
1277     else if (action == zoomOutViewAction)
1278         zoom(-1);
1279     else if (action == zoomOriginAction) {
1280         static const float hscale = QApplication::primaryScreen()->physicalDotsPerInchX() / (Worksheet::convertToSceneUnits(1, Worksheet::Unit::Inch));
1281         static const float vscale = QApplication::primaryScreen()->physicalDotsPerInchY() / (Worksheet::convertToSceneUnits(1, Worksheet::Unit::Inch));
1282         setTransform(QTransform::fromScale(hscale, vscale));
1283     }
1284 
1285     currentZoomAction = action;
1286     if (tbZoom)
1287         tbZoom->setDefaultAction(action);
1288 
1289     updateLabelsZoom();
1290 }
1291 
1292 void WorksheetView::updateFit() {
1293     switch (m_worksheet->zoomFit()) {
1294     case Worksheet::ZoomFit::None:
1295         break;
1296     case Worksheet::ZoomFit::FitToWidth: {
1297         const float scaleFactor = viewport()->width() / scene()->sceneRect().width();
1298         setTransform(QTransform::fromScale(scaleFactor, scaleFactor));
1299         break;
1300     }
1301     case Worksheet::ZoomFit::FitToHeight: {
1302         const float scaleFactor = viewport()->height() / scene()->sceneRect().height();
1303         setTransform(QTransform::fromScale(scaleFactor, scaleFactor));
1304         break;
1305     }
1306     case Worksheet::ZoomFit::FitToSelection: {
1307         fitInView(scene()->selectionArea().boundingRect(), Qt::KeepAspectRatio);
1308         break;
1309     }
1310     case Worksheet::ZoomFit::Fit: {
1311         const float scaleFactorVertical = viewport()->height() / scene()->sceneRect().height();
1312         const float scaleFactorHorizontal = viewport()->width() / scene()->sceneRect().width();
1313         if (scaleFactorVertical * scene()->sceneRect().width() < viewport()->width())
1314             setTransform(QTransform::fromScale(scaleFactorVertical, scaleFactorVertical));
1315         else
1316             setTransform(QTransform::fromScale(scaleFactorHorizontal, scaleFactorHorizontal));
1317         break;
1318     }
1319     }
1320 }
1321 
1322 void WorksheetView::updateScrollBarPolicy() {
1323     if (m_worksheet->useViewSize() || m_worksheet->zoomFit() != Worksheet::ZoomFit::None) {
1324         setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1325         setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1326     } else {
1327         setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
1328         setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
1329     }
1330 }
1331 
1332 void WorksheetView::fitChanged(QAction* action) {
1333     m_worksheet->setZoomFit(static_cast<Worksheet::ZoomFit>(action->data().toInt()));
1334     updateScrollBarPolicy();
1335     updateFit();
1336 }
1337 
1338 double WorksheetView::zoomFactor() const {
1339     double scale = transform().m11();
1340     scale *= Worksheet::convertToSceneUnits(1, Worksheet::Unit::Inch) / QApplication::primaryScreen()->physicalDotsPerInchX();
1341     return scale;
1342 }
1343 
1344 void WorksheetView::updateLabelsZoom() const {
1345     const double zoom = zoomFactor();
1346     const auto& labels = m_worksheet->children<TextLabel>(AbstractAspect::ChildIndexFlag::Recursive | AbstractAspect::ChildIndexFlag::IncludeHidden);
1347     for (auto* label : labels)
1348         label->setZoomFactor(zoom);
1349 }
1350 
1351 void WorksheetView::magnificationChanged(QAction* action) {
1352     if (action == noMagnificationAction) {
1353         magnificationFactor = 0;
1354         if (m_magnificationWindow)
1355             m_magnificationWindow->setVisible(false);
1356     } else if (action == twoTimesMagnificationAction)
1357         magnificationFactor = 2;
1358     else if (action == threeTimesMagnificationAction)
1359         magnificationFactor = 3;
1360     else if (action == fourTimesMagnificationAction)
1361         magnificationFactor = 4;
1362     else if (action == fiveTimesMagnificationAction)
1363         magnificationFactor = 5;
1364 
1365     currentMagnificationAction = action;
1366     if (tbMagnification)
1367         tbMagnification->setDefaultAction(action);
1368 }
1369 
1370 void WorksheetView::mouseModeChanged(QAction* action) {
1371     if (action == selectionModeAction) {
1372         m_mouseMode = MouseMode::Selection;
1373         setInteractive(true);
1374         setDragMode(QGraphicsView::NoDrag);
1375     } else if (action == navigationModeAction) {
1376         m_mouseMode = MouseMode::Navigation;
1377         setInteractive(false);
1378         setDragMode(QGraphicsView::ScrollHandDrag);
1379     } else {
1380         m_mouseMode = MouseMode::ZoomSelection;
1381         setInteractive(false);
1382         setDragMode(QGraphicsView::NoDrag);
1383     }
1384 }
1385 
1386 //"Add new" related slots
1387 void WorksheetView::addNew(QAction* action) {
1388     bool restorePointers = false;
1389     WorksheetElement* aspect = nullptr;
1390     if (action == addCartesianPlot1Action) {
1391         auto* plot = new CartesianPlot(i18n("Plot Area"));
1392         plot->setType(CartesianPlot::Type::FourAxes);
1393         plot->setMouseMode(m_cartesianPlotMouseMode);
1394         aspect = plot;
1395         if (tbNewCartesianPlot)
1396             tbNewCartesianPlot->setDefaultAction(addCartesianPlot1Action);
1397     } else if (action == addCartesianPlot2Action) {
1398         auto* plot = new CartesianPlot(i18n("Plot Area"));
1399         plot->setType(CartesianPlot::Type::TwoAxes);
1400         plot->setMouseMode(m_cartesianPlotMouseMode);
1401         aspect = plot;
1402         if (tbNewCartesianPlot)
1403             tbNewCartesianPlot->setDefaultAction(addCartesianPlot2Action);
1404     } else if (action == addCartesianPlot3Action) {
1405         auto* plot = new CartesianPlot(i18n("Plot Area"));
1406         plot->setType(CartesianPlot::Type::TwoAxesCentered);
1407         plot->setMouseMode(m_cartesianPlotMouseMode);
1408         aspect = plot;
1409         if (tbNewCartesianPlot)
1410             tbNewCartesianPlot->setDefaultAction(addCartesianPlot3Action);
1411     } else if (action == addCartesianPlot4Action) {
1412         auto* plot = new CartesianPlot(i18n("Plot Area"));
1413         plot->setType(CartesianPlot::Type::TwoAxesCenteredZero);
1414         plot->setMouseMode(m_cartesianPlotMouseMode);
1415         aspect = plot;
1416         if (tbNewCartesianPlot)
1417             tbNewCartesianPlot->setDefaultAction(addCartesianPlot4Action);
1418     } else if (action == addCartesianPlotTemplateAction) {
1419         // open dialog
1420         PlotTemplateDialog d;
1421         if (d.exec() != QDialog::Accepted)
1422             return;
1423 
1424         auto* plot = d.generatePlot();
1425         if (!plot)
1426             return;
1427 
1428         restorePointers = true;
1429         aspect = plot;
1430         if (tbNewCartesianPlot)
1431             tbNewCartesianPlot->setDefaultAction(addCartesianPlotTemplateAction);
1432     } else if (action == addTextLabelAction) {
1433         auto* l = new TextLabel(i18n("Text Label"));
1434         l->setText(i18n("Text Label"));
1435         aspect = l;
1436     } else if (action == addImageAction) {
1437         Image* image = new Image(i18n("Image"));
1438         aspect = image;
1439     }
1440     if (!aspect)
1441         return;
1442 
1443     m_worksheet->addChild(aspect);
1444 
1445     if (restorePointers) {
1446         m_worksheet->project()->restorePointers(m_worksheet);
1447         m_worksheet->project()->retransformElements(m_worksheet);
1448     }
1449 
1450     // labels and images with their initial positions need to be retransformed
1451     // after they have gotten a parent
1452     if (aspect->type() == AspectType::TextLabel || aspect->type() == AspectType::Image) {
1453         if (m_calledFromContextMenu) {
1454             // must be done after add Child, because otherwise the parentData rect is not available
1455             // and therefore aligning will not work
1456             auto position = aspect->position();
1457             position.point = aspect->parentPosToRelativePos(m_cursorPos, position);
1458             position.point =
1459                 aspect->align(position.point, aspect->graphicsItem()->boundingRect(), aspect->horizontalAlignment(), aspect->verticalAlignment(), false);
1460             aspect->setPosition(position);
1461             m_calledFromContextMenu = false;
1462         } else
1463             aspect->retransform();
1464     } else if (aspect->type() == AspectType::CartesianPlot)
1465         static_cast<CartesianPlot*>(aspect)->retransform();
1466 
1467     handleCartesianPlotActions();
1468 
1469     // fade-in the newly added element.
1470     // TODO: don't do any fade-in for text labels - when a text label is added
1471     // after new curves were created via PlotDataDialog, the undo or delete steps for this label
1472     // lead to a crash in the handling of graphics effects in Qt. The root cause for the crash
1473     // is not completely clear (maybe related ot its child ScaledTextItem) so we deactivate
1474     // this effect completely for text labels, s.a. BUG: 455096.
1475     if (aspect->type() == AspectType::TextLabel)
1476         return;
1477 
1478     if (!m_fadeInTimeLine) {
1479         m_fadeInTimeLine = new QTimeLine(1000, this);
1480         m_fadeInTimeLine->setFrameRange(0, 100);
1481         connect(m_fadeInTimeLine, &QTimeLine::valueChanged, this, &WorksheetView::fadeIn);
1482     }
1483 
1484     // if there is already an element fading in, stop the time line and show the element with the full opacity.
1485     if (m_fadeInTimeLine->state() == QTimeLine::Running) {
1486         m_fadeInTimeLine->stop();
1487         auto* effect = new QGraphicsOpacityEffect();
1488         effect->setOpacity(1);
1489         lastAddedWorksheetElement->graphicsItem()->setGraphicsEffect(effect);
1490     }
1491 
1492     // create the opacity effect and start the actual fade-in
1493     lastAddedWorksheetElement = aspect;
1494     auto* effect = new QGraphicsOpacityEffect();
1495     effect->setOpacity(0);
1496     lastAddedWorksheetElement->graphicsItem()->setGraphicsEffect(effect);
1497     m_fadeInTimeLine->start();
1498 }
1499 
1500 /*!
1501  * select all top-level items
1502  */
1503 void WorksheetView::selectAllElements() {
1504     // deselect all previously selected items since there can be some non top-level items belong them
1505     m_suppressSelectionChangedEvent = true;
1506     for (auto* item : m_selectedItems)
1507         m_worksheet->setItemSelectedInView(item, false);
1508 
1509     // select top-level items
1510     for (auto* item : scene()->items()) {
1511         if (!item->parentItem())
1512             item->setSelected(true);
1513     }
1514     m_suppressSelectionChangedEvent = false;
1515     this->selectionChanged();
1516 }
1517 
1518 /*!
1519  * deletes selected worksheet elements
1520  */
1521 void WorksheetView::deleteElement() {
1522     if (m_selectedItems.isEmpty())
1523         return;
1524 
1525 #if KCOREADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
1526     auto status = KMessageBox::warningTwoActions(
1527         this,
1528         i18np("Do you really want to delete the selected object?", "Do you really want to delete the selected %1 objects?", m_selectedItems.size()),
1529         i18np("Delete selected object", "Delete selected objects", m_selectedItems.size()),
1530         KStandardGuiItem::del(),
1531         KStandardGuiItem::cancel());
1532     if (status == KMessageBox::SecondaryAction)
1533         return;
1534 #else
1535     auto status = KMessageBox::warningYesNo(
1536         this,
1537         i18np("Do you really want to delete the selected object?", "Do you really want to delete the selected %1 objects?", m_selectedItems.size()),
1538         i18np("Delete selected object", "Delete selected objects", m_selectedItems.size()));
1539     if (status == KMessageBox::No)
1540         return;
1541 #endif
1542 
1543     m_suppressSelectionChangedEvent = true;
1544     m_worksheet->beginMacro(i18n("%1: Remove selected worksheet elements.", m_worksheet->name()));
1545     for (auto* item : m_selectedItems)
1546         m_worksheet->deleteAspectFromGraphicsItem(item);
1547     m_selectedElement = nullptr;
1548     m_worksheet->endMacro();
1549     m_suppressSelectionChangedEvent = false;
1550 }
1551 
1552 void WorksheetView::aspectAboutToBeRemoved(const AbstractAspect* /* aspect */) {
1553     /*
1554         lastAddedWorksheetElement = dynamic_cast<WorksheetElement*>(const_cast<AbstractAspect*>(aspect));
1555         if (!lastAddedWorksheetElement)
1556             return;
1557     */
1558     // FIXME: fading-out doesn't work
1559     // also, the following code collides with undo/redo of the deletion
1560     // of a worksheet element (after redoing the element is not shown with the full opacity
1561     /*
1562         if (!m_fadeOutTimeLine) {
1563             m_fadeOutTimeLine = new QTimeLine(1000, this);
1564             m_fadeOutTimeLine->setFrameRange(0, 100);
1565             connect(m_fadeOutTimeLine, SIGNAL(valueChanged(qreal)), this, SLOT(fadeOut(qreal)));
1566         }
1567 
1568         //if there is already an element fading out, stop the time line
1569         if (m_fadeOutTimeLine->state() == QTimeLine::Running)
1570             m_fadeOutTimeLine->stop();
1571 
1572         m_fadeOutTimeLine->start();
1573     */
1574 }
1575 
1576 void WorksheetView::fadeIn(qreal value) {
1577     auto* effect = static_cast<QGraphicsOpacityEffect*>(lastAddedWorksheetElement->graphicsItem()->graphicsEffect());
1578     effect->setOpacity(value);
1579 }
1580 
1581 void WorksheetView::fadeOut(qreal value) {
1582     auto* effect = new QGraphicsOpacityEffect();
1583     effect->setOpacity(1 - value);
1584     lastAddedWorksheetElement->graphicsItem()->setGraphicsEffect(effect);
1585 }
1586 
1587 /*!
1588  * called when one of the layout-actions in WorkseetView was triggered.
1589  * sets the layout in Worksheet and enables/disables the layout actions.
1590  */
1591 void WorksheetView::changeLayout(QAction* action) {
1592     if (action == breakLayoutAction) {
1593         verticalLayoutAction->setEnabled(true);
1594         verticalLayoutAction->setChecked(false);
1595 
1596         horizontalLayoutAction->setEnabled(true);
1597         horizontalLayoutAction->setChecked(false);
1598 
1599         gridLayoutAction->setEnabled(true);
1600         gridLayoutAction->setChecked(false);
1601 
1602         breakLayoutAction->setEnabled(false);
1603 
1604         m_worksheet->setLayout(Worksheet::Layout::NoLayout);
1605     } else {
1606         verticalLayoutAction->setEnabled(false);
1607         horizontalLayoutAction->setEnabled(false);
1608         gridLayoutAction->setEnabled(false);
1609         breakLayoutAction->setEnabled(true);
1610 
1611         if (action == verticalLayoutAction) {
1612             verticalLayoutAction->setChecked(true);
1613             m_worksheet->setLayout(Worksheet::Layout::VerticalLayout);
1614         } else if (action == horizontalLayoutAction) {
1615             horizontalLayoutAction->setChecked(true);
1616             m_worksheet->setLayout(Worksheet::Layout::HorizontalLayout);
1617         } else {
1618             gridLayoutAction->setChecked(true);
1619             m_worksheet->setLayout(Worksheet::Layout::GridLayout);
1620         }
1621     }
1622 }
1623 
1624 void WorksheetView::changeGrid(QAction* action) {
1625     if (action == noGridAction) {
1626         m_gridSettings.style = GridStyle::NoGrid;
1627         snapToGridAction->setEnabled(false);
1628     } else if (action == sparseLineGridAction) {
1629         m_gridSettings.style = GridStyle::Line;
1630         m_gridSettings.color = Qt::gray;
1631         m_gridSettings.opacity = 0.7;
1632         m_gridSettings.horizontalSpacing = 15;
1633         m_gridSettings.verticalSpacing = 15;
1634     } else if (action == denseLineGridAction) {
1635         m_gridSettings.style = GridStyle::Line;
1636         m_gridSettings.color = Qt::gray;
1637         m_gridSettings.opacity = 0.7;
1638         m_gridSettings.horizontalSpacing = 5;
1639         m_gridSettings.verticalSpacing = 5;
1640     } else if (action == denseDotGridAction) {
1641         m_gridSettings.style = GridStyle::Dot;
1642         m_gridSettings.color = Qt::black;
1643         m_gridSettings.opacity = 0.7;
1644         m_gridSettings.horizontalSpacing = 5;
1645         m_gridSettings.verticalSpacing = 5;
1646     } else if (action == sparseDotGridAction) {
1647         m_gridSettings.style = GridStyle::Dot;
1648         m_gridSettings.color = Qt::black;
1649         m_gridSettings.opacity = 0.7;
1650         m_gridSettings.horizontalSpacing = 15;
1651         m_gridSettings.verticalSpacing = 15;
1652     } else if (action == customGridAction) {
1653 #ifndef SDK
1654         auto* dlg = new GridDialog(this);
1655         if (dlg->exec() == QDialog::Accepted)
1656             dlg->save(m_gridSettings);
1657         else
1658 #endif
1659             return;
1660     }
1661 
1662     if (m_gridSettings.style == GridStyle::NoGrid)
1663         snapToGridAction->setEnabled(false);
1664     else
1665         snapToGridAction->setEnabled(true);
1666 
1667     invalidateScene(sceneRect(), QGraphicsScene::BackgroundLayer);
1668 }
1669 
1670 // TODO
1671 void WorksheetView::changeSnapToGrid() {
1672 }
1673 
1674 /*!
1675  *  Selects the QGraphicsItem \c item in \c WorksheetView.
1676  *  The selection in \c ProjectExplorer is forwarded to  \c Worksheet
1677  *  and is finally handled here.
1678  */
1679 void WorksheetView::selectItem(QGraphicsItem* item) {
1680     m_suppressSelectionChangedEvent = true;
1681     item->setSelected(true);
1682     m_selectedItems << item;
1683     handleCartesianPlotActions();
1684     m_suppressSelectionChangedEvent = false;
1685 }
1686 
1687 /*!
1688  *  Deselects the \c QGraphicsItem \c item in \c WorksheetView.
1689  *  The deselection in \c ProjectExplorer is forwarded to \c Worksheet
1690  *  and is finally handled here.
1691  */
1692 void WorksheetView::deselectItem(QGraphicsItem* item) {
1693     m_suppressSelectionChangedEvent = true;
1694     item->setSelected(false);
1695     m_selectedItems.removeOne(item);
1696     handleCartesianPlotActions();
1697     m_suppressSelectionChangedEvent = false;
1698 }
1699 
1700 /*!
1701  *  Called on selection changes in the view.
1702  *   Determines which items were selected and deselected
1703  *  and forwards these changes to \c Worksheet
1704  */
1705 void WorksheetView::selectionChanged() {
1706     // if the project is being closed, the scene items are being removed and the selection can change.
1707     // don't react on these changes since this can lead crashes (worksheet object is already in the destructor).
1708     if (m_isClosing)
1709         return;
1710 
1711     if (m_suppressSelectionChangedEvent)
1712         return;
1713 
1714     QList<QGraphicsItem*> items = scene()->selectedItems();
1715 
1716     // check, whether the previously selected items were deselected now.
1717     // Forward the deselection prior to the selection of new items
1718     // in order to avoid the unwanted multiple selection in project explorer
1719     for (auto* item : m_selectedItems) {
1720         if (items.indexOf(item) == -1)
1721             m_worksheet->setItemSelectedInView(item, false);
1722     }
1723 
1724     // select new items
1725     if (items.isEmpty()) {
1726         // no items selected -> select the worksheet again.
1727         m_worksheet->setSelectedInView(true);
1728 
1729         // if one of the "zoom&select" plot mouse modes was selected before, activate the default "selection mode" again
1730         // since no plots are selected now.
1731         if (m_mouseMode == MouseMode::Selection && m_cartesianPlotMouseMode != CartesianPlot::MouseMode::Selection) {
1732             cartesianPlotSelectionModeAction->setChecked(true);
1733             cartesianPlotMouseModeChanged(cartesianPlotSelectionModeAction);
1734         }
1735     } else {
1736         for (const auto* item : items)
1737             m_worksheet->setItemSelectedInView(item, true);
1738 
1739         // items selected -> deselect the worksheet in the project explorer
1740         // prevents unwanted multiple selection with worksheet (if it was selected before)
1741         m_worksheet->setSelectedInView(false);
1742     }
1743 
1744     m_selectedItems = items;
1745     handleCartesianPlotActions();
1746 }
1747 
1748 void WorksheetView::handleCartesianPlotSelected(CartesianPlot* plot) {
1749     if (tbCartesianPlotAddNew) { // not available in the presenter mode
1750         tbCartesianPlotAddNew->setMenu(plot->addNewMenu()); // update the tool button shown in the toolbar
1751         cartesianPlotAddNewAction->setMenu(plot->addNewMenu()); // update the action shown in the main menu
1752     }
1753 
1754     /* Action to All: action is applied to all ranges
1755      *  - Applied to all plots and all ranges
1756      * Action to X: action is applied to all x ranges
1757      *  - x zoom selection: zooming into all x ranges of all plots (Normaly all plots will have the same x ranges so it makes sense
1758      *  - y zoom selection: makes no sense. disable
1759      * Action to Y: action is applied to all y ranges
1760      *  - x zoom selection: makes no sense. disable
1761      *  - y zoom selection: zooming into all y ranges of all plots
1762      * Action to Selection
1763      * - x zoom selection: makes no sense, because the range is unknown, disable
1764      * - y zoom selection: makes no sense, because the range is unknown, disable
1765      *      - What happens when only one range is available?
1766      */
1767 
1768     switch (m_worksheet->cartesianPlotActionMode()) {
1769     case Worksheet::CartesianPlotActionMode::ApplyActionToAll: // Is there a usecase for this?
1770         cartesianPlotZoomSelectionModeAction->setEnabled(true);
1771         cartesianPlotZoomXSelectionModeAction->setEnabled(true);
1772         cartesianPlotZoomYSelectionModeAction->setEnabled(true);
1773         zoomInAction->setEnabled(true);
1774         zoomOutAction->setEnabled(true);
1775         zoomInXAction->setEnabled(true);
1776         zoomOutXAction->setEnabled(true);
1777         zoomInYAction->setEnabled(true);
1778         zoomOutYAction->setEnabled(true);
1779         shiftLeftXAction->setEnabled(true);
1780         shiftRightXAction->setEnabled(true);
1781         shiftUpYAction->setEnabled(true);
1782         shiftDownYAction->setEnabled(true);
1783         scaleAutoAction->setEnabled(true);
1784         scaleAutoXAction->setEnabled(true);
1785         scaleAutoYAction->setEnabled(true);
1786         break;
1787     case Worksheet::CartesianPlotActionMode::ApplyActionToSelection: {
1788         bool enableX = plot->rangeCount(Dimension::X) == 1;
1789         bool enableY = plot->rangeCount(Dimension::Y) == 1;
1790         // only when only one range available
1791         cartesianPlotZoomSelectionModeAction->setEnabled(enableX && enableY);
1792         cartesianPlotZoomXSelectionModeAction->setEnabled(enableX);
1793         cartesianPlotZoomYSelectionModeAction->setEnabled(enableY);
1794         zoomInAction->setEnabled(true);
1795         zoomOutAction->setEnabled(true);
1796         zoomInXAction->setEnabled(true);
1797         zoomOutXAction->setEnabled(true);
1798         zoomInYAction->setEnabled(true);
1799         zoomOutYAction->setEnabled(true);
1800         shiftLeftXAction->setEnabled(true);
1801         shiftRightXAction->setEnabled(true);
1802         shiftUpYAction->setEnabled(true);
1803         shiftDownYAction->setEnabled(true);
1804         scaleAutoAction->setEnabled(true);
1805         scaleAutoXAction->setEnabled(true);
1806         scaleAutoYAction->setEnabled(true);
1807         break;
1808     }
1809     case Worksheet::CartesianPlotActionMode::ApplyActionToAllX:
1810         cartesianPlotZoomSelectionModeAction->setEnabled(false);
1811         cartesianPlotZoomXSelectionModeAction->setEnabled(true);
1812         cartesianPlotZoomYSelectionModeAction->setEnabled(true);
1813         zoomInAction->setEnabled(false);
1814         zoomOutAction->setEnabled(false);
1815         zoomInXAction->setEnabled(true);
1816         zoomOutXAction->setEnabled(true);
1817         zoomInYAction->setEnabled(false);
1818         zoomOutYAction->setEnabled(false);
1819         shiftLeftXAction->setEnabled(true);
1820         shiftRightXAction->setEnabled(true);
1821         shiftUpYAction->setEnabled(false);
1822         shiftDownYAction->setEnabled(false);
1823         scaleAutoAction->setEnabled(false);
1824         scaleAutoXAction->setEnabled(true);
1825         scaleAutoYAction->setEnabled(true);
1826         break;
1827     case Worksheet::CartesianPlotActionMode::ApplyActionToAllY:
1828         cartesianPlotZoomSelectionModeAction->setEnabled(false);
1829         cartesianPlotZoomXSelectionModeAction->setEnabled(true);
1830         cartesianPlotZoomYSelectionModeAction->setEnabled(true);
1831         zoomInAction->setEnabled(false);
1832         zoomOutAction->setEnabled(false);
1833         zoomInXAction->setEnabled(false);
1834         zoomOutXAction->setEnabled(false);
1835         zoomInYAction->setEnabled(true);
1836         zoomOutYAction->setEnabled(true);
1837         shiftLeftXAction->setEnabled(false);
1838         shiftRightXAction->setEnabled(false);
1839         shiftUpYAction->setEnabled(true);
1840         shiftDownYAction->setEnabled(true);
1841         scaleAutoAction->setEnabled(false);
1842         scaleAutoXAction->setEnabled(false);
1843         scaleAutoYAction->setEnabled(true);
1844         break;
1845     }
1846 
1847     cartesianPlotSelectionModeAction->setEnabled(true);
1848     if (cartesianPlotCursorModeAction) // not available in the presenter mode
1849         cartesianPlotCursorModeAction->setEnabled(true);
1850 }
1851 
1852 void WorksheetView::handleReferenceRangeSelected() {
1853     auto l = static_cast<ReferenceRange*>(m_selectedElement);
1854     bool vert = (l->orientation() == ReferenceRange::Orientation::Vertical);
1855     handleReferences(vert);
1856 }
1857 
1858 void WorksheetView::handleReferenceLineSelected() {
1859     auto l = static_cast<ReferenceLine*>(m_selectedElement);
1860     bool vert = (l->orientation() == ReferenceLine::Orientation::Vertical);
1861     handleReferences(vert);
1862 }
1863 
1864 void WorksheetView::handleReferences(bool vertical) {
1865     /* Action to All: action is applied to all ranges
1866      *  - Disable
1867      * Action to X: action is applied to all x ranges
1868      *  - x zoom selection: if vertical:Zooming into all ranges of all plots (mostly the x ranges are the same for all plots --> usecase)
1869      *  - y zoom selection: if !vertical:zoom only into the range from the reference line
1870      * Action to Y: action is applied to all y ranges
1871      *  - x zoom selection: if vertical: zoom only into the range from the reference line
1872      *  - y zoom selection: if !vertical: Zooming into all ranges of all plots
1873      * Action to Selection
1874      * - x zoom selection: if vertical: zoom only into the range from the reference line
1875      * - y zoom selection: if !vertical:zoom only into the range from the reference line
1876      */
1877 
1878     zoomInAction->setEnabled(false);
1879     zoomOutAction->setEnabled(false);
1880     cartesianPlotZoomSelectionModeAction->setEnabled(false);
1881     scaleAutoAction->setEnabled(false);
1882 
1883     switch (m_worksheet->cartesianPlotActionMode()) {
1884     case Worksheet::CartesianPlotActionMode::ApplyActionToAll:
1885         cartesianPlotZoomXSelectionModeAction->setEnabled(false);
1886         cartesianPlotZoomYSelectionModeAction->setEnabled(false);
1887         zoomInXAction->setEnabled(false);
1888         zoomOutXAction->setEnabled(false);
1889         zoomInYAction->setEnabled(false);
1890         zoomOutYAction->setEnabled(false);
1891         shiftLeftXAction->setEnabled(false);
1892         shiftRightXAction->setEnabled(false);
1893         shiftUpYAction->setEnabled(false);
1894         shiftDownYAction->setEnabled(false);
1895         scaleAutoXAction->setEnabled(vertical);
1896         scaleAutoYAction->setEnabled(!vertical);
1897         break;
1898     case Worksheet::CartesianPlotActionMode::ApplyActionToSelection:
1899         cartesianPlotZoomXSelectionModeAction->setEnabled(vertical);
1900         cartesianPlotZoomYSelectionModeAction->setEnabled(!vertical);
1901         zoomInXAction->setEnabled(vertical);
1902         zoomOutXAction->setEnabled(vertical);
1903         zoomInYAction->setEnabled(!vertical);
1904         zoomOutYAction->setEnabled(!vertical);
1905         shiftLeftXAction->setEnabled(vertical);
1906         shiftRightXAction->setEnabled(vertical);
1907         shiftUpYAction->setEnabled(!vertical);
1908         shiftDownYAction->setEnabled(!vertical);
1909         scaleAutoXAction->setEnabled(vertical);
1910         scaleAutoYAction->setEnabled(!vertical);
1911         break;
1912     case Worksheet::CartesianPlotActionMode::ApplyActionToAllX:
1913         cartesianPlotZoomXSelectionModeAction->setEnabled(vertical);
1914         cartesianPlotZoomYSelectionModeAction->setEnabled(!vertical);
1915         zoomInXAction->setEnabled(vertical);
1916         zoomOutXAction->setEnabled(vertical);
1917         zoomInYAction->setEnabled(!vertical);
1918         zoomOutYAction->setEnabled(!vertical);
1919         shiftLeftXAction->setEnabled(vertical);
1920         shiftRightXAction->setEnabled(vertical);
1921         shiftUpYAction->setEnabled(!vertical);
1922         shiftDownYAction->setEnabled(!vertical);
1923         scaleAutoXAction->setEnabled(vertical);
1924         scaleAutoYAction->setEnabled(!vertical);
1925         break;
1926     case Worksheet::CartesianPlotActionMode::ApplyActionToAllY:
1927         cartesianPlotZoomXSelectionModeAction->setEnabled(vertical);
1928         cartesianPlotZoomYSelectionModeAction->setEnabled(!vertical);
1929         zoomInXAction->setEnabled(vertical);
1930         zoomOutXAction->setEnabled(vertical);
1931         zoomInYAction->setEnabled(!vertical);
1932         zoomOutYAction->setEnabled(!vertical);
1933         shiftLeftXAction->setEnabled(vertical);
1934         shiftRightXAction->setEnabled(vertical);
1935         shiftUpYAction->setEnabled(!vertical);
1936         shiftDownYAction->setEnabled(!vertical);
1937         scaleAutoXAction->setEnabled(vertical);
1938         scaleAutoYAction->setEnabled(!vertical);
1939         break;
1940     }
1941     cartesianPlotSelectionModeAction->setEnabled(true);
1942     cartesianPlotCursorModeAction->setEnabled(false);
1943 }
1944 
1945 void WorksheetView::handlePlotSelected() {
1946     /* Action to All: action is applied to all ranges
1947      *  - Disable
1948      * Action to X: action is applied to all x ranges
1949      *  - x zoom selection: Zooming into all ranges of all plots (mostly the x ranges are the same for all plots --> usecase)
1950      *  - y zoom selection: zoom only into the range from the curve
1951      * Action to Y: action is applied to all y ranges
1952      *  - x zoom selection: zoom only into the range from the curve
1953      *  - y zoom selection: Zooming into all ranges of all plots
1954      * Action to Selection
1955      * - x zoom selection: zoom only into the range from the curve
1956      * - y zoom selection: zoom only into the range from the curve
1957      */
1958 
1959     switch (m_worksheet->cartesianPlotActionMode()) {
1960     case Worksheet::CartesianPlotActionMode::ApplyActionToAll:
1961         cartesianPlotZoomSelectionModeAction->setEnabled(false);
1962         cartesianPlotZoomXSelectionModeAction->setEnabled(false);
1963         cartesianPlotZoomYSelectionModeAction->setEnabled(false);
1964         zoomInAction->setEnabled(false);
1965         zoomOutAction->setEnabled(false);
1966         zoomInXAction->setEnabled(false);
1967         zoomOutXAction->setEnabled(false);
1968         zoomInYAction->setEnabled(false);
1969         zoomOutYAction->setEnabled(false);
1970         shiftLeftXAction->setEnabled(false);
1971         shiftRightXAction->setEnabled(false);
1972         shiftUpYAction->setEnabled(false);
1973         shiftDownYAction->setEnabled(false);
1974         scaleAutoAction->setEnabled(true);
1975         scaleAutoXAction->setEnabled(true);
1976         scaleAutoYAction->setEnabled(true);
1977         break;
1978     case Worksheet::CartesianPlotActionMode::ApplyActionToSelection:
1979         cartesianPlotZoomSelectionModeAction->setEnabled(true);
1980         cartesianPlotZoomXSelectionModeAction->setEnabled(true);
1981         cartesianPlotZoomYSelectionModeAction->setEnabled(true);
1982         zoomInAction->setEnabled(true);
1983         zoomOutAction->setEnabled(true);
1984         zoomInXAction->setEnabled(true);
1985         zoomOutXAction->setEnabled(true);
1986         zoomInYAction->setEnabled(true);
1987         zoomOutYAction->setEnabled(true);
1988         shiftLeftXAction->setEnabled(true);
1989         shiftRightXAction->setEnabled(true);
1990         shiftUpYAction->setEnabled(true);
1991         shiftDownYAction->setEnabled(true);
1992         scaleAutoAction->setEnabled(true);
1993         scaleAutoXAction->setEnabled(true);
1994         scaleAutoYAction->setEnabled(true);
1995         break;
1996     case Worksheet::CartesianPlotActionMode::ApplyActionToAllX:
1997         cartesianPlotZoomSelectionModeAction->setEnabled(false);
1998         cartesianPlotZoomXSelectionModeAction->setEnabled(true);
1999         cartesianPlotZoomYSelectionModeAction->setEnabled(true);
2000         zoomInAction->setEnabled(false);
2001         zoomOutAction->setEnabled(false);
2002         zoomInXAction->setEnabled(true);
2003         zoomOutXAction->setEnabled(true);
2004         zoomInYAction->setEnabled(true);
2005         zoomOutYAction->setEnabled(true);
2006         shiftLeftXAction->setEnabled(true);
2007         shiftRightXAction->setEnabled(true);
2008         shiftUpYAction->setEnabled(true);
2009         shiftDownYAction->setEnabled(true);
2010         scaleAutoAction->setEnabled(true);
2011         scaleAutoXAction->setEnabled(true);
2012         scaleAutoYAction->setEnabled(true);
2013         break;
2014     case Worksheet::CartesianPlotActionMode::ApplyActionToAllY:
2015         cartesianPlotZoomSelectionModeAction->setEnabled(false);
2016         cartesianPlotZoomXSelectionModeAction->setEnabled(true);
2017         cartesianPlotZoomYSelectionModeAction->setEnabled(true);
2018         zoomInAction->setEnabled(false);
2019         zoomOutAction->setEnabled(false);
2020         zoomInXAction->setEnabled(true);
2021         zoomOutXAction->setEnabled(true);
2022         zoomInYAction->setEnabled(true);
2023         zoomOutYAction->setEnabled(true);
2024         shiftLeftXAction->setEnabled(true);
2025         shiftRightXAction->setEnabled(true);
2026         shiftUpYAction->setEnabled(true);
2027         shiftDownYAction->setEnabled(true);
2028         scaleAutoAction->setEnabled(true);
2029         scaleAutoXAction->setEnabled(true);
2030         scaleAutoYAction->setEnabled(true);
2031         break;
2032     }
2033     cartesianPlotSelectionModeAction->setEnabled(true);
2034     cartesianPlotCursorModeAction->setEnabled(false);
2035 }
2036 
2037 void WorksheetView::handleAxisSelected(const Axis* a) {
2038     if (a->orientation() == Axis::Orientation::Horizontal) {
2039         /* HORIZONTAL:
2040          * Action to All: action is applied to all ranges
2041          *  - Disable
2042          * Action to X: action is applied to all x ranges
2043          *  - x zoom selection: Zooming
2044          *  - y zoom selection: makes no sense. disable
2045          * Action to Y: action is applied to all y ranges
2046          *  - x zoom selection: zooming into the range of the axis
2047          *  - y zoom selection: makes no sense. disable
2048          * Action to Selection
2049          * - x zoom selection: apply to range assigned to the axis, but only for the plot where the axis is child
2050          * - y zoom selection: makes no sense. disable
2051          */
2052         cartesianPlotZoomYSelectionModeAction->setEnabled(false);
2053         cartesianPlotZoomSelectionModeAction->setEnabled(false);
2054         cartesianPlotZoomXSelectionModeAction->setEnabled(true);
2055         zoomInAction->setEnabled(false);
2056         zoomOutAction->setEnabled(false);
2057         zoomInXAction->setEnabled(true);
2058         zoomOutXAction->setEnabled(true);
2059         zoomInYAction->setEnabled(false);
2060         zoomOutYAction->setEnabled(false);
2061         shiftLeftXAction->setEnabled(true);
2062         shiftRightXAction->setEnabled(true);
2063         shiftUpYAction->setEnabled(false);
2064         shiftDownYAction->setEnabled(false);
2065         scaleAutoAction->setEnabled(false);
2066         scaleAutoXAction->setEnabled(true);
2067         scaleAutoYAction->setEnabled(false);
2068     } else {
2069         /* VERTICAL:
2070          * Action to All: action is applied to all ranges
2071          *  - Disable
2072          * Action to Y: action is applied to all y ranges
2073          *  - y zoom selection: Zooming
2074          *  - x zoom selection: makes no sense. disable
2075          * Action to X: action is applied to all x ranges
2076          *  - x zoom selection: makes no sense. disable
2077          *  - y zoom selection: makes no sense. disable
2078          * Action to Selection
2079          * - x zoom selection: apply to range assigned to the axis, but only for the plot where the axis is child
2080          * - y zoom selection: makes no sense. disable
2081          */
2082         cartesianPlotZoomYSelectionModeAction->setEnabled(true);
2083         cartesianPlotZoomSelectionModeAction->setEnabled(false);
2084         cartesianPlotZoomXSelectionModeAction->setEnabled(false);
2085         zoomInAction->setEnabled(false);
2086         zoomOutAction->setEnabled(false);
2087         zoomInXAction->setEnabled(false);
2088         zoomOutXAction->setEnabled(false);
2089         zoomInYAction->setEnabled(true);
2090         zoomOutYAction->setEnabled(true);
2091         shiftLeftXAction->setEnabled(false);
2092         shiftRightXAction->setEnabled(false);
2093         shiftUpYAction->setEnabled(true);
2094         shiftDownYAction->setEnabled(true);
2095         scaleAutoAction->setEnabled(false);
2096         scaleAutoXAction->setEnabled(false);
2097         scaleAutoYAction->setEnabled(true);
2098     }
2099 
2100     cartesianPlotSelectionModeAction->setEnabled(true);
2101     cartesianPlotCursorModeAction->setEnabled(false);
2102 }
2103 
2104 // check whether we have cartesian plots selected and activate/deactivate
2105 void WorksheetView::handleCartesianPlotActions() {
2106     if (!m_plotActionsInitialized)
2107         return;
2108 
2109     if (m_mouseMode != MouseMode::Selection)
2110         return; // Do not change selection when the mousemode is not selection!
2111 
2112     m_selectedElement = nullptr;
2113 
2114     bool handled = false, plot = false;
2115     for (auto* item : m_selectedItems) {
2116         // TODO: or if a children of a plot is selected
2117         auto* w = static_cast<WorksheetElementPrivate*>(item)->q;
2118         if (w->type() == AspectType::CartesianPlot) {
2119             handled = true;
2120             plot = true;
2121             m_selectedElement = w;
2122             handleCartesianPlotSelected(static_cast<CartesianPlot*>(m_selectedElement));
2123             break;
2124         } else if (w->type() == AspectType::ReferenceLine) {
2125             handled = true;
2126             m_selectedElement = w;
2127             handleReferenceLineSelected();
2128         } else if (w->type() == AspectType::ReferenceRange) {
2129             handled = true;
2130             m_selectedElement = w;
2131             handleReferenceRangeSelected();
2132         } else if (w->type() == AspectType::Axis) {
2133             handled = true;
2134             m_selectedElement = w;
2135             handleAxisSelected(static_cast<Axis*>(m_selectedElement));
2136             break;
2137         } else if (dynamic_cast<Plot*>(w) || w->coordinateBindingEnabled()) {
2138             // Plot and other WorksheetElements like custompoint, infoelement, textlabel
2139             handled = true;
2140             m_selectedElement = w;
2141             handlePlotSelected();
2142             break;
2143         }
2144     }
2145 
2146     if (!handled) {
2147         cartesianPlotZoomSelectionModeAction->setEnabled(false);
2148         cartesianPlotZoomSelectionModeAction->setChecked(false);
2149         cartesianPlotZoomXSelectionModeAction->setEnabled(false);
2150         cartesianPlotZoomXSelectionModeAction->setChecked(false);
2151         cartesianPlotZoomYSelectionModeAction->setEnabled(false);
2152         cartesianPlotZoomYSelectionModeAction->setChecked(false);
2153         for (auto* plot : m_worksheet->children<CartesianPlot>())
2154             plot->setMouseMode(CartesianPlot::MouseMode::Selection);
2155         zoomInAction->setEnabled(false);
2156         zoomOutAction->setEnabled(false);
2157         zoomInXAction->setEnabled(false);
2158         zoomOutXAction->setEnabled(false);
2159         zoomInYAction->setEnabled(false);
2160         zoomOutYAction->setEnabled(false);
2161         shiftLeftXAction->setEnabled(false);
2162         shiftRightXAction->setEnabled(false);
2163         shiftUpYAction->setEnabled(false);
2164         shiftDownYAction->setEnabled(false);
2165         scaleAutoAction->setEnabled(false);
2166         scaleAutoXAction->setEnabled(false);
2167         scaleAutoYAction->setEnabled(false);
2168     }
2169 
2170     if (cartesianPlotAddNewAction) // not available in the presenter mode
2171         cartesianPlotAddNewAction->setEnabled(plot);
2172 
2173     if (m_menusInitialized) { // not available in the presenter mode
2174         tbCartesianPlotAddNew->setEnabled(plot);
2175         m_cartesianPlotZoomMenu->setEnabled(m_selectedElement);
2176         m_cartesianPlotMouseModeMenu->setEnabled(plot);
2177     }
2178 }
2179 
2180 void WorksheetView::exportToFile(const QString& path, const ExportFormat format, const ExportArea area, const bool background, const int resolution) {
2181     QRectF sourceRect;
2182 
2183     // determine the rectangular to print
2184     if (area == ExportArea::BoundingBox)
2185         sourceRect = scene()->itemsBoundingRect();
2186     else if (area == ExportArea::Selection) {
2187         if (!m_selectedItems.isEmpty()) {
2188             // TODO doesn't work: rect = scene()->selectionArea().boundingRect();
2189             for (const auto* item : m_selectedItems)
2190                 sourceRect = sourceRect.united(item->mapToScene(item->boundingRect()).boundingRect());
2191         } else
2192             sourceRect = scene()->sceneRect(); // export everything if nothing is selected
2193     } else
2194         sourceRect = scene()->sceneRect();
2195 
2196     // save
2197     switch (format) {
2198     case ExportFormat::PDF: {
2199         QPrinter printer(QPrinter::HighResolution);
2200         printer.setOutputFormat(QPrinter::PdfFormat);
2201 
2202         printer.setOutputFileName(path);
2203         int w = Worksheet::convertFromSceneUnits(sourceRect.width(), Worksheet::Unit::Millimeter);
2204         int h = Worksheet::convertFromSceneUnits(sourceRect.height(), Worksheet::Unit::Millimeter);
2205         printer.setPageSize(QPageSize(QSizeF(w, h), QPageSize::Millimeter));
2206         printer.setPageMargins(QMarginsF(0, 0, 0, 0), QPageLayout::Millimeter);
2207         printer.setPrintRange(QPrinter::PageRange);
2208         printer.setCreator(QStringLiteral("LabPlot ") + QLatin1String(LVERSION));
2209 
2210         QPainter painter(&printer);
2211         painter.setRenderHint(QPainter::Antialiasing);
2212         QRectF targetRect(0, 0, painter.device()->width(), painter.device()->height());
2213         painter.begin(&printer);
2214         exportPaint(&painter, targetRect, sourceRect, background);
2215         painter.end();
2216         break;
2217     }
2218     case ExportFormat::SVG: {
2219         QSvgGenerator generator;
2220         generator.setFileName(path);
2221         //      if (!generator.isValid()) {
2222         //          RESET_CURSOR;
2223         //          QMessageBox::critical(nullptr, i18n("Failed to export"), i18n("Failed to write to '%1'. Please check the path.", path));
2224         //      }
2225         int w = Worksheet::convertFromSceneUnits(sourceRect.width(), Worksheet::Unit::Millimeter);
2226         int h = Worksheet::convertFromSceneUnits(sourceRect.height(), Worksheet::Unit::Millimeter);
2227         w = w * QApplication::primaryScreen()->physicalDotsPerInchX() / 25.4;
2228         h = h * QApplication::primaryScreen()->physicalDotsPerInchY() / 25.4;
2229 
2230         generator.setSize(QSize(w, h));
2231         QRectF targetRect(0, 0, w, h);
2232         generator.setViewBox(targetRect);
2233 
2234         QPainter painter;
2235         painter.begin(&generator);
2236         exportPaint(&painter, targetRect, sourceRect, background);
2237         painter.end();
2238         break;
2239     }
2240     case ExportFormat::PNG:
2241     case ExportFormat::JPG:
2242     case ExportFormat::BMP:
2243     case ExportFormat::PPM:
2244     case ExportFormat::XBM:
2245     case ExportFormat::XPM: {
2246         int w = Worksheet::convertFromSceneUnits(sourceRect.width(), Worksheet::Unit::Millimeter);
2247         int h = Worksheet::convertFromSceneUnits(sourceRect.height(), Worksheet::Unit::Millimeter);
2248         w = w * resolution / 25.4;
2249         h = h * resolution / 25.4;
2250         QImage image(QSize(w, h), QImage::Format_ARGB32_Premultiplied);
2251         image.fill(Qt::transparent);
2252         QRectF targetRect(0, 0, w, h);
2253 
2254         QPainter painter;
2255         painter.begin(&image);
2256         painter.setRenderHint(QPainter::Antialiasing);
2257         exportPaint(&painter, targetRect, sourceRect, background);
2258         painter.end();
2259 
2260         if (!path.isEmpty()) {
2261             bool rc{false};
2262             switch (format) {
2263             case ExportFormat::PNG:
2264                 rc = image.save(path, "PNG");
2265                 break;
2266             case ExportFormat::JPG:
2267                 rc = image.save(path, "JPG");
2268                 break;
2269             case ExportFormat::BMP:
2270                 rc = image.save(path, "BMP");
2271                 break;
2272             case ExportFormat::PPM:
2273                 rc = image.save(path, "PPM");
2274                 break;
2275             case ExportFormat::XBM:
2276                 rc = image.save(path, "XBM");
2277                 break;
2278             case ExportFormat::XPM:
2279                 rc = image.save(path, "XPM");
2280                 break;
2281             case ExportFormat::PDF:
2282             case ExportFormat::SVG:
2283                 break;
2284             }
2285             if (!rc) {
2286                 RESET_CURSOR;
2287                 QMessageBox::critical(nullptr, i18n("Failed to export"), i18n("Failed to write to '%1'. Please check the path.", path));
2288             }
2289         } else
2290             QApplication::clipboard()->setImage(image, QClipboard::Clipboard);
2291     }
2292     }
2293 }
2294 
2295 void WorksheetView::exportToPixmap(QPixmap& pixmap) {
2296     const auto& sourceRect = scene()->sceneRect();
2297     int w = Worksheet::convertFromSceneUnits(sourceRect.width(), Worksheet::Unit::Millimeter);
2298     int h = Worksheet::convertFromSceneUnits(sourceRect.height(), Worksheet::Unit::Millimeter);
2299     w = w * QApplication::primaryScreen()->physicalDotsPerInchX() / 25.4;
2300     h = h * QApplication::primaryScreen()->physicalDotsPerInchX() / 25.4;
2301     pixmap = pixmap.scaled(w, h);
2302     QRectF targetRect(0, 0, w, h);
2303 
2304     QPainter painter;
2305     painter.begin(&pixmap);
2306     painter.setRenderHint(QPainter::Antialiasing);
2307     exportPaint(&painter, targetRect, sourceRect, true /* export background */, true /* export selection */);
2308     painter.end();
2309 }
2310 
2311 bool WorksheetView::eventFilter(QObject* /*watched*/, QEvent* event) {
2312     if (event->type() == QEvent::KeyPress && m_actionsInitialized) {
2313         auto* keyEvent = static_cast<QKeyEvent*>(event);
2314         int key = keyEvent->key();
2315         switch (key) {
2316         case Qt::Key_S:
2317             if (cartesianPlotSelectionModeAction->isEnabled())
2318                 cartesianPlotSelectionModeAction->trigger();
2319             return true;
2320         case Qt::Key_Z:
2321             if (cartesianPlotZoomSelectionModeAction->isEnabled())
2322                 cartesianPlotZoomSelectionModeAction->trigger();
2323             return true;
2324         case Qt::Key_C:
2325             if (cartesianPlotCursorModeAction->isEnabled())
2326                 cartesianPlotCursorModeAction->trigger();
2327             return true;
2328         case Qt::Key_Escape:
2329             if (cartesianPlotSelectionModeAction->isEnabled())
2330                 cartesianPlotSelectionModeAction->trigger();
2331             return false; // so the plot can handle the event too
2332         default:
2333             return false;
2334         }
2335     }
2336     return false;
2337 }
2338 
2339 void WorksheetView::exportToClipboard() {
2340     QRectF sourceRect;
2341 
2342     if (m_selectedItems.size() == 0)
2343         sourceRect = scene()->itemsBoundingRect();
2344     else {
2345         // export selection
2346         for (const auto* item : m_selectedItems)
2347             sourceRect = sourceRect.united(item->mapToScene(item->boundingRect()).boundingRect());
2348     }
2349 
2350     int w = Worksheet::convertFromSceneUnits(sourceRect.width(), Worksheet::Unit::Millimeter);
2351     int h = Worksheet::convertFromSceneUnits(sourceRect.height(), Worksheet::Unit::Millimeter);
2352     w = w * QApplication::primaryScreen()->physicalDotsPerInchX() / 25.4;
2353     h = h * QApplication::primaryScreen()->physicalDotsPerInchY() / 25.4;
2354     QImage image(QSize(w, h), QImage::Format_ARGB32_Premultiplied);
2355     image.fill(Qt::transparent);
2356     QRectF targetRect(0, 0, w, h);
2357 
2358     QPainter painter;
2359     painter.begin(&image);
2360     painter.setRenderHint(QPainter::Antialiasing);
2361     exportPaint(&painter, targetRect, sourceRect, true);
2362     painter.end();
2363 
2364     QApplication::clipboard()->setImage(image, QClipboard::Clipboard);
2365 }
2366 
2367 void WorksheetView::exportPaint(QPainter* painter, const QRectF& targetRect, const QRectF& sourceRect, const bool background, const bool selection) {
2368     // hide the magnification window, shouldn't be exported
2369     bool magnificationActive = false;
2370     if (m_magnificationWindow && m_magnificationWindow->isVisible()) {
2371         magnificationActive = true;
2372         m_magnificationWindow->setVisible(false);
2373     }
2374 
2375     // draw the background
2376     m_isPrinting = true;
2377     if (background) {
2378         painter->save();
2379         painter->scale(targetRect.width() / sourceRect.width(), targetRect.height() / sourceRect.height());
2380         drawBackground(painter, sourceRect);
2381         painter->restore();
2382     }
2383 
2384     // draw the scene items
2385     if (!selection) // if no selection effects have to be exported, set the printing flag to suppress it in the paint()'s of the children
2386         m_worksheet->setPrinting(true);
2387     scene()->render(painter, QRectF(), sourceRect);
2388     if (!selection)
2389         m_worksheet->setPrinting(false);
2390     m_isPrinting = false;
2391 
2392     // show the magnification window if it was active before
2393     if (magnificationActive)
2394         m_magnificationWindow->setVisible(true);
2395 }
2396 
2397 void WorksheetView::print(QPrinter* printer) {
2398     m_isPrinting = true;
2399     m_worksheet->setPrinting(true);
2400     bool magnificationActive = false;
2401     if (m_magnificationWindow && m_magnificationWindow->isVisible()) {
2402         magnificationActive = true;
2403         m_magnificationWindow->setVisible(false);
2404     }
2405 
2406     QPainter painter(printer);
2407     painter.setRenderHint(QPainter::Antialiasing);
2408 
2409     // draw background
2410     const auto& page_rect = printer->pageLayout().paintRectPixels(printer->resolution());
2411     const auto& scene_rect = scene()->sceneRect();
2412     float scale = std::max(scene_rect.width() / page_rect.width(), scene_rect.height() / page_rect.height());
2413     drawBackgroundItems(&painter, QRectF(0, 0, scene_rect.width() / scale, scene_rect.height() / scale));
2414 
2415     // draw scene
2416     scene()->render(&painter);
2417     m_worksheet->setPrinting(false);
2418     m_isPrinting = false;
2419 
2420     if (magnificationActive)
2421         m_magnificationWindow->setVisible(true);
2422 }
2423 
2424 void WorksheetView::updateBackground() {
2425     invalidateScene(sceneRect(), QGraphicsScene::BackgroundLayer);
2426 }
2427 
2428 /*!
2429  * called when the layout was changed in Worksheet,
2430  * enables the corresponding action
2431  */
2432 void WorksheetView::layoutChanged(Worksheet::Layout layout) {
2433     if (layout == Worksheet::Layout::NoLayout) {
2434         verticalLayoutAction->setEnabled(true);
2435         verticalLayoutAction->setChecked(false);
2436 
2437         horizontalLayoutAction->setEnabled(true);
2438         horizontalLayoutAction->setChecked(false);
2439 
2440         gridLayoutAction->setEnabled(true);
2441         gridLayoutAction->setChecked(false);
2442 
2443         breakLayoutAction->setEnabled(false);
2444     } else {
2445         verticalLayoutAction->setEnabled(false);
2446         horizontalLayoutAction->setEnabled(false);
2447         gridLayoutAction->setEnabled(false);
2448         breakLayoutAction->setEnabled(true);
2449 
2450         if (layout == Worksheet::Layout::VerticalLayout)
2451             verticalLayoutAction->setChecked(true);
2452         else if (layout == Worksheet::Layout::HorizontalLayout)
2453             horizontalLayoutAction->setChecked(true);
2454         else
2455             gridLayoutAction->setChecked(true);
2456     }
2457 }
2458 
2459 void WorksheetView::suppressSelectionChangedEvent(bool value) {
2460     m_suppressSelectionChangedEvent = value;
2461 }
2462 
2463 WorksheetElement* WorksheetView::selectedElement() const {
2464     return m_selectedElement;
2465 }
2466 QList<QGraphicsItem*> WorksheetView::selectedItems() const {
2467     return m_selectedItems;
2468 }
2469 
2470 void WorksheetView::registerShortcuts() {
2471     selectAllAction->setShortcut(Qt::CTRL | Qt::Key_A);
2472     deleteAction->setShortcut(Qt::Key_Delete);
2473     backspaceAction->setShortcut(Qt::Key_Backspace);
2474     zoomInViewAction->setShortcut(Qt::CTRL | Qt::Key_Plus);
2475     zoomOutViewAction->setShortcut(Qt::CTRL | Qt::Key_Minus);
2476     zoomOriginAction->setShortcut(Qt::CTRL | Qt::Key_1);
2477 }
2478 
2479 void WorksheetView::unregisterShortcuts() {
2480     selectAllAction->setShortcut(QKeySequence());
2481     deleteAction->setShortcut(QKeySequence());
2482     backspaceAction->setShortcut(QKeySequence());
2483     zoomInViewAction->setShortcut(QKeySequence());
2484     zoomOutViewAction->setShortcut(QKeySequence());
2485     zoomOriginAction->setShortcut(QKeySequence());
2486 }
2487 
2488 // ##############################################################################
2489 // ########################  SLOTs for cartesian plots   ########################
2490 // ##############################################################################
2491 void WorksheetView::cartesianPlotActionModeChanged(QAction* action) {
2492     if (action == cartesianPlotApplyToSelectionAction)
2493         m_worksheet->setCartesianPlotActionMode(Worksheet::CartesianPlotActionMode::ApplyActionToSelection);
2494     else if (action == cartesianPlotApplyToAllXAction)
2495         m_worksheet->setCartesianPlotActionMode(Worksheet::CartesianPlotActionMode::ApplyActionToAllX);
2496     else if (action == cartesianPlotApplyToAllYAction)
2497         m_worksheet->setCartesianPlotActionMode(Worksheet::CartesianPlotActionMode::ApplyActionToAllY);
2498     else
2499         m_worksheet->setCartesianPlotActionMode(Worksheet::CartesianPlotActionMode::ApplyActionToAll);
2500 
2501     handleCartesianPlotActions();
2502 }
2503 
2504 void WorksheetView::cartesianPlotCursorModeChanged(QAction* action) {
2505     if (action == cartesianPlotApplyToSelectionCursor)
2506         m_worksheet->setCartesianPlotCursorMode(Worksheet::CartesianPlotActionMode::ApplyActionToSelection);
2507     else
2508         m_worksheet->setCartesianPlotCursorMode(Worksheet::CartesianPlotActionMode::ApplyActionToAll);
2509 
2510     handleCartesianPlotActions();
2511 }
2512 
2513 void WorksheetView::plotsInteractiveActionChanged(bool checked) {
2514     m_worksheet->setPlotsInteractive(checked);
2515 }
2516 
2517 void WorksheetView::cartesianPlotMouseModeChanged(QAction* action) {
2518     if (m_suppressMouseModeChange)
2519         return;
2520 
2521     m_cartesianPlotMouseMode = static_cast<CartesianPlot::MouseMode>(action->data().toInt());
2522     // TODO: find out, which element is selected. So the corresponding range can be modified
2523 
2524     for (auto* plot : m_worksheet->children<CartesianPlot>())
2525         plot->setMouseMode(m_cartesianPlotMouseMode);
2526 }
2527 
2528 void WorksheetView::cartesianPlotMouseModeChangedSlot(CartesianPlot::MouseMode mouseMode) {
2529     if (!m_menusInitialized)
2530         return;
2531 
2532     m_suppressMouseModeChange = true;
2533     if (mouseMode == CartesianPlot::MouseMode::Selection)
2534         cartesianPlotSelectionModeAction->setChecked(true);
2535     else if (mouseMode == CartesianPlot::MouseMode::ZoomSelection)
2536         cartesianPlotZoomSelectionModeAction->setChecked(true);
2537     else if (mouseMode == CartesianPlot::MouseMode::ZoomXSelection)
2538         cartesianPlotZoomXSelectionModeAction->setChecked(true);
2539     else if (mouseMode == CartesianPlot::MouseMode::ZoomYSelection)
2540         cartesianPlotZoomYSelectionModeAction->setChecked(true);
2541     else if (mouseMode == CartesianPlot::MouseMode::Cursor)
2542         cartesianPlotCursorModeAction->setChecked(true);
2543     m_suppressMouseModeChange = false;
2544 }
2545 
2546 void WorksheetView::childContextMenuRequested(AspectType t, QMenu* menu) {
2547     if (!menu)
2548         return;
2549     if (t == AspectType::CartesianPlot) {
2550         // actions.at(0) is the menu title
2551         // actions.at(1) is the "new" menu
2552         menu->insertMenu(menu->actions().at(2), m_cartesianPlotZoomMenu);
2553     }
2554     menu->exec(QCursor::pos());
2555 }
2556 
2557 void WorksheetView::cartesianPlotNavigationChanged(QAction* action) {
2558     // TODO: find out, which element was selected to find out which range should be changed
2559     // Project().projectExplorer().currentAspect()
2560 
2561     auto op = (CartesianPlot::NavigationOperation)action->data().toInt();
2562     auto plotActionMode = m_worksheet->cartesianPlotActionMode();
2563     if (plotActionMode == Worksheet::CartesianPlotActionMode::ApplyActionToSelection) {
2564         int cSystemIndex = CartesianPlot::cSystemIndex(m_selectedElement);
2565         const auto& plots = m_worksheet->children<CartesianPlot>();
2566         for (auto* plot : plots) {
2567             if (m_selectedItems.indexOf(plot->graphicsItem()) != -1)
2568                 plot->navigate(cSystemIndex, op);
2569             else {
2570                 // check if one of the plots childrend is selected. Do the operation there too.
2571                 for (auto* child : plot->children<WorksheetElement>()) {
2572                     if (m_selectedItems.indexOf(child->graphicsItem()) != -1) {
2573                         plot->navigate(cSystemIndex, op);
2574                         break;
2575                     }
2576                 }
2577             }
2578         }
2579     } else if ((plotActionMode == Worksheet::CartesianPlotActionMode::ApplyActionToAllY
2580                 && (op == CartesianPlot::NavigationOperation::ScaleAutoX || op == CartesianPlot::NavigationOperation::ShiftLeftX
2581                     || op == CartesianPlot::NavigationOperation::ShiftRightX || op == CartesianPlot::NavigationOperation::ZoomInX
2582                     || op == CartesianPlot::NavigationOperation::ZoomOutX))
2583                || (plotActionMode == Worksheet::CartesianPlotActionMode::ApplyActionToAllX
2584                    && (op == CartesianPlot::NavigationOperation::ScaleAutoY || op == CartesianPlot::NavigationOperation::ShiftUpY
2585                        || op == CartesianPlot::NavigationOperation::ShiftDownY || op == CartesianPlot::NavigationOperation::ZoomInY
2586                        || op == CartesianPlot::NavigationOperation::ZoomOutY))) {
2587         int cSystemIndex = CartesianPlot::cSystemIndex(m_selectedElement);
2588         if (m_selectedElement->type() == AspectType::CartesianPlot)
2589             static_cast<CartesianPlot*>(m_selectedElement)->navigate(-1, op);
2590         else {
2591             auto parentPlot = static_cast<CartesianPlot*>(m_selectedElement->parent(AspectType::CartesianPlot));
2592             if (parentPlot) // really needed?
2593                 parentPlot->navigate(cSystemIndex, op);
2594         }
2595     } else {
2596         const auto& plots = m_worksheet->children<CartesianPlot>();
2597         for (auto* plot : plots)
2598             plot->navigate(-1, op);
2599     }
2600 }
2601 
2602 Worksheet::CartesianPlotActionMode WorksheetView::getCartesianPlotActionMode() const {
2603     return m_worksheet->cartesianPlotActionMode();
2604 }
2605 
2606 void WorksheetView::presenterMode() {
2607 #ifndef SDK
2608     const auto& group = Settings::group(QStringLiteral("Settings_Worksheet"));
2609     const bool interactive = group.readEntry("PresenterModeInteractive", false);
2610     auto* presenterWidget = new PresenterWidget(m_worksheet, screen(), interactive);
2611     presenterWidget->showFullScreen();
2612 #endif
2613 }