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 }