File indexing completed on 2024-05-12 15:27:36

0001 /***************************************************************************
0002     File                 : DatapickerImageView.cpp
0003     Project              : LabPlot
0004     Description          : DatapickerImage view for datapicker
0005     --------------------------------------------------------------------
0006     Copyright            : (C) 2015 by Ankit Wagadre (wagadre.ankit@gmail.com)
0007     Copyright            : (C) 2015-2016 by Alexander Semke (alexander.semke@web.de)
0008 
0009  ***************************************************************************/
0010 /***************************************************************************
0011  *                                                                         *
0012  *  This program is free software; you can redistribute it and/or modify   *
0013  *  it under the terms of the GNU General Public License as published by   *
0014  *  the Free Software Foundation; either version 2 of the License, or      *
0015  *  (at your option) any later version.                                    *
0016  *                                                                         *
0017  *  This program is distributed in the hope that it will be useful,        *
0018  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
0019  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
0020  *  GNU General Public License for more details.                           *
0021  *                                                                         *
0022  *   You should have received a copy of the GNU General Public License     *
0023  *   along with this program; if not, write to the Free Software           *
0024  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
0025  *   Boston, MA  02110-1301  USA                                           *
0026  *                                                                         *
0027  ***************************************************************************/
0028 
0029 #include "commonfrontend/datapicker/DatapickerImageView.h"
0030 #include "backend/worksheet/Worksheet.h"
0031 #include "backend/datapicker/DatapickerPoint.h"
0032 #include "backend/datapicker/Datapicker.h"
0033 #include "backend/datapicker/Transform.h"
0034 #include "backend/datapicker/DatapickerCurve.h"
0035 #include "backend/datapicker/DatapickerImage.h"
0036 
0037 #include <limits>
0038 
0039 #include <QMenu>
0040 #include <QMessageBox>
0041 #include <QToolBar>
0042 #include <QDesktopWidget>
0043 #include <QWheelEvent>
0044 #include <QPrinter>
0045 #include <QSvgGenerator>
0046 #include <QImage>
0047 #include <QToolButton>
0048 #include <QTimeLine>
0049 
0050 #include <KLocalizedString>
0051 
0052 /**
0053  * \class DatapickerImageView
0054  * \brief Datapicker/DatapickerImage view
0055  */
0056 
0057 /*!
0058   Constructur of the class.
0059   Creates a view for the DatapickerImage \c image and initializes the internal model.
0060 */
0061 DatapickerImageView::DatapickerImageView(DatapickerImage* image) : QGraphicsView(),
0062     m_image(image),
0063     m_datapicker(dynamic_cast<Datapicker*>(m_image->parentAspect())),
0064     m_transform(new Transform()) {
0065 
0066     setScene(m_image->scene());
0067 
0068     setRenderHint(QPainter::Antialiasing);
0069     setRubberBandSelectionMode(Qt::ContainsItemBoundingRect);
0070     setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
0071     setResizeAnchor(QGraphicsView::AnchorViewCenter);
0072     setMinimumSize(16, 16);
0073     setFocusPolicy(Qt::StrongFocus);
0074 
0075     viewport()->setAttribute(Qt::WA_OpaquePaintEvent);
0076     viewport()->setAttribute(Qt::WA_NoSystemBackground);
0077     setCacheMode(QGraphicsView::CacheBackground);
0078 
0079     initActions();
0080     initMenus();
0081     m_image->setSegmentsHoverEvent(true);
0082     setInteractive(true);
0083 
0084     changeZoom(zoomOriginAction);
0085     currentZoomAction = zoomInViewAction;
0086 
0087     if (m_image->plotPointsType() == DatapickerImage::PointsType::AxisPoints)
0088         setAxisPointsAction->setChecked(true);
0089     else if (m_image->plotPointsType() == DatapickerImage::PointsType::CurvePoints)
0090         setCurvePointsAction->setChecked(true);
0091     else
0092         selectSegmentAction->setChecked(true);
0093 
0094     handleImageActions();
0095     changeRotationAngle();
0096 
0097     //signal/slot connections
0098     //for general actions
0099     connect(m_image, &DatapickerImage::requestProjectContextMenu, this, &DatapickerImageView::createContextMenu);
0100     connect(m_image, &DatapickerImage::requestUpdate, this, &DatapickerImageView::updateBackground);
0101     connect(m_image, &DatapickerImage::requestUpdateActions, this, &DatapickerImageView::handleImageActions);
0102     connect(m_datapicker, &Datapicker::requestUpdateActions, this, &DatapickerImageView::handleImageActions);
0103     connect(m_image, &DatapickerImage::rotationAngleChanged, this, &DatapickerImageView::changeRotationAngle);
0104 
0105     //resize the view to make the complete scene visible.
0106     //no need to resize the view when the project is being opened,
0107     //all views will be resized to the stored values at the end
0108     if (!m_image->isLoading()) {
0109         float w = Worksheet::convertFromSceneUnits(sceneRect().width(), Worksheet::Unit::Inch);
0110         float h = Worksheet::convertFromSceneUnits(sceneRect().height(), Worksheet::Unit::Inch);
0111         w *= QApplication::desktop()->physicalDpiX();
0112         h *= QApplication::desktop()->physicalDpiY();
0113         resize(w*1.1, h*1.1);
0114     }
0115 
0116     //rescale to the original size
0117     static const float hscale = QApplication::desktop()->physicalDpiX()/(Worksheet::convertToSceneUnits(1, Worksheet::Unit::Inch));
0118     static const float vscale = QApplication::desktop()->physicalDpiY()/(Worksheet::convertToSceneUnits(1, Worksheet::Unit::Inch));
0119     setTransform(QTransform::fromScale(hscale, vscale));
0120 }
0121 
0122 DatapickerImageView::~DatapickerImageView() {
0123     delete m_transform;
0124 }
0125 
0126 void DatapickerImageView::initActions() {
0127     auto* zoomActionGroup = new QActionGroup(this);
0128     auto* mouseModeActionGroup = new QActionGroup(this);
0129     navigationActionGroup = new QActionGroup(this);
0130     magnificationActionGroup = new QActionGroup(this);
0131 
0132     //Zoom actions
0133     zoomInViewAction = new QAction(QIcon::fromTheme("zoom-in"), i18n("Zoom In"), zoomActionGroup);
0134     zoomInViewAction->setShortcut(Qt::CTRL+Qt::Key_Plus);
0135 
0136     zoomOutViewAction = new QAction(QIcon::fromTheme("zoom-out"), i18n("Zoom Out"), zoomActionGroup);
0137     zoomOutViewAction->setShortcut(Qt::CTRL+Qt::Key_Minus);
0138 
0139     zoomOriginAction = new QAction(QIcon::fromTheme("zoom-original"), i18n("Original Size"), zoomActionGroup);
0140     zoomOriginAction->setShortcut(Qt::CTRL+Qt::Key_1);
0141 
0142     zoomFitPageHeightAction = new QAction(QIcon::fromTheme("zoom-fit-height"), i18n("Fit to Height"), zoomActionGroup);
0143     zoomFitPageWidthAction = new QAction(QIcon::fromTheme("zoom-fit-width"), i18n("Fit to Width"), zoomActionGroup);
0144 
0145     // Mouse mode actions
0146     navigationModeAction = new QAction(QIcon::fromTheme("input-mouse"), i18n("Navigate"), mouseModeActionGroup);
0147     navigationModeAction->setCheckable(true);
0148     navigationModeAction->setData(static_cast<int>(MouseMode::Navigation));
0149 
0150     zoomSelectionModeAction = new QAction(QIcon::fromTheme("page-zoom"), i18n("Select and Zoom"), mouseModeActionGroup);
0151     zoomSelectionModeAction->setCheckable(true);
0152     zoomSelectionModeAction->setData(static_cast<int>(MouseMode::ZoomSelection));
0153 
0154     setAxisPointsAction = new QAction(QIcon::fromTheme("labplot-plot-axis-points"), i18n("Set Axis Points"), mouseModeActionGroup);
0155     setAxisPointsAction->setCheckable(true);
0156     setAxisPointsAction->setData(static_cast<int>(MouseMode::ReferencePointsEntry));
0157 
0158     setCurvePointsAction = new QAction(QIcon::fromTheme("labplot-xy-curve-points"), i18n("Set Curve Points"), mouseModeActionGroup);
0159     setCurvePointsAction->setCheckable(true);
0160     setCurvePointsAction->setData(static_cast<int>(MouseMode::CurvePointsEntry));
0161 
0162     selectSegmentAction = new QAction(QIcon::fromTheme("labplot-xy-curve-segments"), i18n("Select Curve Segments"), mouseModeActionGroup);
0163     selectSegmentAction->setCheckable(true);
0164     selectSegmentAction->setData(static_cast<int>(MouseMode::CurveSegmentsEntry));
0165 
0166     addCurveAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("New Curve"), this);
0167 
0168     shiftLeftAction = new QAction(QIcon::fromTheme("labplot-shift-left-x"), i18n("Shift Left"), navigationActionGroup);
0169     shiftLeftAction->setShortcut(Qt::Key_Right);
0170 
0171     shiftRightAction = new QAction(QIcon::fromTheme("labplot-shift-right-x"), i18n("Shift Right"), navigationActionGroup);
0172     shiftRightAction->setShortcut(Qt::Key_Left);
0173 
0174     shiftUpAction = new QAction(QIcon::fromTheme("labplot-shift-down-y"), i18n("Shift Up"), navigationActionGroup);
0175     shiftUpAction->setShortcut(Qt::Key_Up);
0176 
0177     shiftDownAction = new QAction(QIcon::fromTheme("labplot-shift-up-y"), i18n("Shift Down"), navigationActionGroup);
0178     shiftDownAction->setShortcut(Qt::Key_Down);
0179 
0180     noMagnificationAction = new QAction(QIcon::fromTheme("labplot-1x-zoom"), i18n("No Magnification"), magnificationActionGroup);
0181     noMagnificationAction->setCheckable(true);
0182     noMagnificationAction->setChecked(true);
0183 
0184     twoTimesMagnificationAction = new QAction(QIcon::fromTheme("labplot-2x-zoom"), i18n("2x Magnification"), magnificationActionGroup);
0185     twoTimesMagnificationAction->setCheckable(true);
0186 
0187     threeTimesMagnificationAction = new QAction(QIcon::fromTheme("labplot-3x-zoom"), i18n("3x Magnification"), magnificationActionGroup);
0188     threeTimesMagnificationAction->setCheckable(true);
0189 
0190     fourTimesMagnificationAction = new QAction(QIcon::fromTheme("labplot-4x-zoom"), i18n("4x Magnification"), magnificationActionGroup);
0191     fourTimesMagnificationAction->setCheckable(true);
0192 
0193     fiveTimesMagnificationAction = new QAction(QIcon::fromTheme("labplot-5x-zoom"), i18n("5x Magnification"), magnificationActionGroup);
0194     fiveTimesMagnificationAction->setCheckable(true);
0195 
0196     //set some default values
0197     currentZoomAction = zoomInViewAction;
0198     currentMagnificationAction = noMagnificationAction;
0199 
0200     switch(m_image->plotPointsType()) {
0201     case DatapickerImage::PointsType::AxisPoints:
0202         currentPlotPointsTypeAction = setAxisPointsAction;
0203         setAxisPointsAction->setChecked(true);
0204         mouseModeChanged(setAxisPointsAction);
0205         break;
0206     case DatapickerImage::PointsType::CurvePoints:
0207         currentPlotPointsTypeAction = setCurvePointsAction;
0208         setCurvePointsAction->setChecked(true);
0209         mouseModeChanged(setCurvePointsAction);
0210         break;
0211     case DatapickerImage::PointsType::SegmentPoints:
0212         currentPlotPointsTypeAction = selectSegmentAction;
0213         selectSegmentAction->setChecked(true);
0214         mouseModeChanged(selectSegmentAction);
0215     }
0216 
0217     //signal-slot connections
0218     connect(mouseModeActionGroup, &QActionGroup::triggered, this, &DatapickerImageView::mouseModeChanged);
0219     connect(zoomActionGroup, &QActionGroup::triggered, this, &DatapickerImageView::changeZoom);
0220     connect(addCurveAction, &QAction::triggered, this, &DatapickerImageView::addCurve);
0221     connect(navigationActionGroup, &QActionGroup::triggered, this, &DatapickerImageView::changeSelectedItemsPosition);
0222     connect(magnificationActionGroup, &QActionGroup::triggered, this, &DatapickerImageView::magnificationChanged);
0223 }
0224 
0225 void DatapickerImageView::initMenus() {
0226     m_viewMouseModeMenu = new QMenu(i18n("Mouse Mode"), this);
0227     m_viewMouseModeMenu->setIcon(QIcon::fromTheme("input-mouse"));
0228     m_viewMouseModeMenu->addAction(setAxisPointsAction);
0229     m_viewMouseModeMenu->addAction(setCurvePointsAction);
0230     m_viewMouseModeMenu->addAction(selectSegmentAction);
0231     m_viewMouseModeMenu->addSeparator();
0232     m_viewMouseModeMenu->addAction(navigationModeAction);
0233     m_viewMouseModeMenu->addAction(zoomSelectionModeAction);
0234 
0235     m_zoomMenu = new QMenu(i18n("Zoom View"), this);
0236     m_zoomMenu->setIcon(QIcon::fromTheme("zoom-draw"));
0237     m_zoomMenu->addAction(zoomInViewAction);
0238     m_zoomMenu->addAction(zoomOutViewAction);
0239     m_zoomMenu->addAction(zoomOriginAction);
0240     m_zoomMenu->addAction(zoomFitPageHeightAction);
0241     m_zoomMenu->addAction(zoomFitPageWidthAction);
0242 
0243     m_navigationMenu = new QMenu(i18n("Move Last Point"), this);
0244     m_navigationMenu->addAction(shiftLeftAction);
0245     m_navigationMenu->addAction(shiftRightAction);
0246     m_navigationMenu->addAction(shiftUpAction);
0247     m_navigationMenu->addAction(shiftDownAction);
0248 
0249     m_magnificationMenu = new QMenu(i18n("Magnification"), this);
0250     m_magnificationMenu->setIcon(QIcon::fromTheme("zoom-in"));
0251     m_magnificationMenu->addAction(noMagnificationAction);
0252     m_magnificationMenu->addAction(twoTimesMagnificationAction);
0253     m_magnificationMenu->addAction(threeTimesMagnificationAction);
0254     m_magnificationMenu->addAction(fourTimesMagnificationAction);
0255     m_magnificationMenu->addAction(fiveTimesMagnificationAction);
0256 }
0257 
0258 /*!
0259  * Populates the menu \c menu with the image and image-view relevant actions.
0260  * The menu is used
0261  *   - as the context menu in DatapickerImageView
0262  *   - as the "datapicker menu" in the main menu-bar (called form MainWin)
0263  *   - as a part of the image context menu in project explorer
0264  */
0265 void DatapickerImageView::createContextMenu(QMenu* menu) const {
0266     Q_ASSERT(menu);
0267 
0268     QAction* firstAction = nullptr;
0269     // if we're populating the context menu for the project explorer, then
0270     //there're already actions available there. Skip the first title-action
0271     //and insert the action at the beginning of the menu.
0272     if (menu->actions().size()>1)
0273         firstAction = menu->actions().at(1);
0274 
0275     menu->insertAction(firstAction, addCurveAction);
0276     menu->insertSeparator(firstAction);
0277     menu->insertMenu(firstAction, m_navigationMenu);
0278     menu->insertSeparator(firstAction);
0279     menu->insertMenu(firstAction, m_viewMouseModeMenu);
0280     menu->insertMenu(firstAction, m_zoomMenu);
0281     menu->insertMenu(firstAction, m_magnificationMenu);
0282     menu->insertSeparator(firstAction);
0283 }
0284 
0285 void DatapickerImageView::fillToolBar(QToolBar* toolBar) {
0286     toolBar->addSeparator();
0287     toolBar->addAction(setAxisPointsAction);
0288     toolBar->addAction(setCurvePointsAction);
0289     toolBar->addAction(selectSegmentAction);
0290     toolBar->addAction(navigationModeAction);
0291     toolBar->addAction(zoomSelectionModeAction);
0292 
0293     toolBar->addSeparator();
0294     toolBar->addAction(addCurveAction);
0295 
0296     toolBar->addSeparator();
0297     toolBar->addAction(shiftRightAction);
0298     toolBar->addAction(shiftLeftAction);
0299     toolBar->addAction(shiftUpAction);
0300     toolBar->addAction(shiftDownAction);
0301 
0302     tbZoom = new QToolButton(toolBar);
0303     tbZoom->setPopupMode(QToolButton::MenuButtonPopup);
0304     tbZoom->setMenu(m_zoomMenu);
0305     tbZoom->setDefaultAction(currentZoomAction);
0306     toolBar->addSeparator();
0307     toolBar->addWidget(tbZoom);
0308 
0309     tbMagnification = new QToolButton(toolBar);
0310     tbMagnification->setPopupMode(QToolButton::MenuButtonPopup);
0311     tbMagnification->setMenu(m_magnificationMenu);
0312     tbMagnification->setDefaultAction(currentMagnificationAction);
0313     toolBar->addWidget(tbMagnification);
0314 }
0315 
0316 void DatapickerImageView::setScene(QGraphicsScene* scene) {
0317     QGraphicsView::setScene(scene);
0318     setTransform(QTransform());
0319 }
0320 
0321 void DatapickerImageView::drawForeground(QPainter* painter, const QRectF& rect) {
0322     if (m_mouseMode == MouseMode::ZoomSelection && m_selectionBandIsShown) {
0323         painter->save();
0324         const QRectF& selRect = mapToScene(QRect(m_selectionStart, m_selectionEnd).normalized()).boundingRect();
0325         painter->setPen(QPen(Qt::black, 5/transform().m11()));
0326         painter->drawRect(selRect);
0327         painter->setBrush(Qt::blue);
0328         painter->setOpacity(0.2);
0329         painter->drawRect(selRect);
0330         painter->restore();
0331     }
0332     QGraphicsView::drawForeground(painter, rect);
0333 }
0334 
0335 void DatapickerImageView::drawBackground(QPainter* painter, const QRectF& rect) {
0336     painter->save();
0337 
0338     QRectF scene_rect = sceneRect();
0339     if (!scene_rect.contains(rect))
0340         painter->fillRect(rect, Qt::lightGray);
0341 
0342     // canvas
0343     if (m_image->isLoaded) {
0344         if (m_image->plotImageType() == DatapickerImage::PlotImageType::OriginalImage) {
0345             const QImage& todraw = m_image->originalPlotImage.scaled(scene_rect.width(), scene_rect.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
0346             painter->drawImage(scene_rect.topLeft(), todraw);
0347         } else if (m_image->plotImageType() == DatapickerImage::PlotImageType::ProcessedImage) {
0348             const QImage& todraw = m_image->processedPlotImage.scaled(scene_rect.width(), scene_rect.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
0349             painter->drawImage(scene_rect.topLeft(), todraw);
0350         } else {
0351             painter->fillRect(scene_rect, Qt::white);
0352         }
0353     } else {
0354         painter->setBrush(QBrush(Qt::gray));
0355         painter->drawRect(scene_rect);
0356     }
0357 
0358     invalidateScene(rect, QGraphicsScene::BackgroundLayer);
0359     painter->restore();
0360 }
0361 
0362 //##############################################################################
0363 //####################################  Events   ###############################
0364 //##############################################################################
0365 void DatapickerImageView::wheelEvent(QWheelEvent* event) {
0366     //https://wiki.qt.io/Smooth_Zoom_In_QGraphicsView
0367     if (m_mouseMode == MouseMode::ZoomSelection || (QApplication::keyboardModifiers() & Qt::ControlModifier)) {
0368         int numDegrees = event->delta() / 8;
0369         int numSteps = numDegrees / 15; // see QWheelEvent documentation
0370         zoom(numSteps);
0371     } else
0372         QGraphicsView::wheelEvent(event);
0373 }
0374 
0375 void DatapickerImageView::zoom(int numSteps) {
0376     m_numScheduledScalings += numSteps;
0377     if (m_numScheduledScalings * numSteps < 0) // if user moved the wheel in another direction, we reset previously scheduled scalings
0378         m_numScheduledScalings = numSteps;
0379 
0380     auto* anim = new QTimeLine(350, this);
0381     anim->setUpdateInterval(20);
0382 
0383     connect(anim, &QTimeLine::valueChanged, this, &DatapickerImageView::scalingTime);
0384     connect(anim, &QTimeLine::finished, this, &DatapickerImageView::animFinished);
0385     anim->start();
0386 }
0387 
0388 void DatapickerImageView::scalingTime() {
0389     qreal factor = 1.0 + qreal(m_numScheduledScalings) / 300.0;
0390     scale(factor, factor);
0391 }
0392 
0393 void DatapickerImageView::animFinished() {
0394     if (m_numScheduledScalings > 0)
0395         m_numScheduledScalings--;
0396     else
0397         m_numScheduledScalings++;
0398     sender()->~QObject();
0399 }
0400 
0401 void DatapickerImageView::mousePressEvent(QMouseEvent* event) {
0402     //prevent the deselection of items when context menu event
0403     //was triggered (right button click)
0404     if (event->button() == Qt::RightButton) {
0405         event->accept();
0406         return;
0407     }
0408 
0409     if (event->button() == Qt::LeftButton && m_mouseMode == MouseMode::ZoomSelection) {
0410         m_selectionStart = event->pos();
0411         m_selectionBandIsShown = true;
0412         return;
0413     }
0414 
0415     QPointF eventPos = mapToScene(event->pos());
0416     bool entryMode = (m_mouseMode == MouseMode::ReferencePointsEntry
0417                       || m_mouseMode == MouseMode::CurvePointsEntry || m_mouseMode == MouseMode::CurveSegmentsEntry);
0418 
0419     //check whether there is a point item under the cursor
0420     bool pointsUnderCursor = false;
0421     auto items = this->items(event->pos());
0422     for (auto* item : items) {
0423         if (item != m_image->m_magnificationWindow) {
0424             pointsUnderCursor = true;
0425             break;
0426         }
0427     }
0428 
0429     if (entryMode && !pointsUnderCursor && m_image->isLoaded && sceneRect().contains(eventPos)) {
0430         if ( m_image->plotPointsType() == DatapickerImage::PointsType::AxisPoints ) {
0431             int childCount = m_image->childCount<DatapickerPoint>(AbstractAspect::ChildIndexFlag::IncludeHidden);
0432             if (childCount < 3)
0433                 m_datapicker->addNewPoint(eventPos, m_image);
0434         } else if ( m_image->plotPointsType() == DatapickerImage::PointsType::CurvePoints && m_datapicker->activeCurve() ) {
0435             m_datapicker->addNewPoint(eventPos, m_datapicker->activeCurve());
0436         }
0437 
0438         if (m_image->m_magnificationWindow && m_image->m_magnificationWindow->isVisible())
0439             updateMagnificationWindow();
0440     }
0441 
0442     // make sure the datapicker (or its currently active curve) is selected in the project explorer if the view was clicked.
0443     // We need this for the case when we change from the project-node in the project explorer to the datapicker node by clicking the view.
0444     if (m_datapicker->activeCurve() && m_image->plotPointsType() != DatapickerImage::PointsType::AxisPoints) {
0445         m_datapicker->setSelectedInView(false);
0446         m_datapicker->activeCurve()->setSelectedInView(true);
0447     } else {
0448         if (m_datapicker->activeCurve())
0449             m_datapicker->activeCurve()->setSelectedInView(false);
0450         m_datapicker->setSelectedInView(true);
0451     }
0452 
0453     QGraphicsView::mousePressEvent(event);
0454 }
0455 
0456 void DatapickerImageView::mouseReleaseEvent(QMouseEvent* event) {
0457     if (event->button() == Qt::LeftButton && m_mouseMode == MouseMode::ZoomSelection) {
0458         m_selectionBandIsShown = false;
0459         viewport()->repaint(QRect(m_selectionStart, m_selectionEnd).normalized());
0460 
0461         //don't zoom if very small region was selected, avoid occasional/unwanted zooming
0462         m_selectionEnd = event->pos();
0463         if ( abs(m_selectionEnd.x()-m_selectionStart.x())>20 && abs(m_selectionEnd.y()-m_selectionStart.y())>20 )
0464             fitInView(mapToScene(QRect(m_selectionStart, m_selectionEnd).normalized()).boundingRect(), Qt::KeepAspectRatio);
0465     }
0466 
0467     QGraphicsView::mouseReleaseEvent(event);
0468 }
0469 
0470 void DatapickerImageView::mouseMoveEvent(QMouseEvent* event) {
0471     //show the selection band
0472     if (m_selectionBandIsShown) {
0473         QRect rect = QRect(m_selectionStart, m_selectionEnd).normalized();
0474         m_selectionEnd = event->pos();
0475         rect = rect.united(QRect(m_selectionStart, m_selectionEnd).normalized());
0476         int penWidth = 5/transform().m11();
0477         rect.setX(rect.x()-penWidth);
0478         rect.setY(rect.y()-penWidth);
0479         rect.setHeight(rect.height()+2*penWidth);
0480         rect.setWidth(rect.width()+2*penWidth);
0481         viewport()->repaint(rect);
0482         return;
0483     }
0484 
0485     QPointF pos = mapToScene(event->pos());
0486 
0487     //show the current coordinates under the mouse cursor in the status bar
0488     if (m_image->plotPointsType() == DatapickerImage::PointsType::CurvePoints) {
0489         QVector3D logicalPos = m_transform->mapSceneToLogical(pos, m_image->axisPoints());
0490         if (m_image->axisPoints().type == DatapickerImage::GraphType::Ternary) {
0491             emit statusInfo( "a =" + QString::number(logicalPos.x()) + ", b =" + QString::number(logicalPos.y()) + ", c =" + QString::number(logicalPos.z()));
0492         } else {
0493             QString xLabel('x');
0494             QString yLabel('y');
0495             if (m_image->axisPoints().type == DatapickerImage::GraphType::PolarInDegree) {
0496                 xLabel = 'r';
0497                 yLabel = "y(deg)";
0498             } else if (m_image->axisPoints().type == DatapickerImage::GraphType::PolarInRadians) {
0499                 xLabel = 'r';
0500                 yLabel = "y(rad)";
0501             }
0502 
0503             if (m_datapicker->activeCurve()) {
0504                 QString statusText = i18n("%1, active curve \"%2\": %3=%4, %5=%6", m_datapicker->name(), m_datapicker->activeCurve()->name(), xLabel, QString::number(logicalPos.x()), yLabel, QString::number(logicalPos.y()));
0505                 emit statusInfo(statusText);
0506             }
0507         }
0508     }
0509 
0510     //show the magnification window
0511     if ( magnificationFactor && m_image->isLoaded && sceneRect().contains(pos) ) {
0512 
0513         if (!m_image->m_magnificationWindow) {
0514             m_image->m_magnificationWindow = new QGraphicsPixmapItem;
0515             scene()->addItem(m_image->m_magnificationWindow);
0516             m_image->m_magnificationWindow->setZValue(std::numeric_limits<int>::max());
0517         }
0518 
0519         updateMagnificationWindow();
0520     } else if (m_image->m_magnificationWindow) {
0521         m_image->m_magnificationWindow->setVisible(false);
0522     }
0523 
0524     QGraphicsView::mouseMoveEvent(event);
0525 }
0526 
0527 void DatapickerImageView::updateMagnificationWindow() {
0528     m_image->m_magnificationWindow->setVisible(false);
0529     QPointF pos = mapToScene(mapFromGlobal(QCursor::pos()));
0530 
0531     //copy the part of the view to be shown magnified
0532     const int size = Worksheet::convertToSceneUnits(2.0, Worksheet::Unit::Centimeter)/transform().m11();
0533     const QRectF copyRect(pos.x() - size/(2*magnificationFactor), pos.y() - size/(2*magnificationFactor), size/magnificationFactor, size/magnificationFactor);
0534     QPixmap px = grab(mapFromScene(copyRect).boundingRect());
0535     px = px.scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
0536 
0537     //draw the bounding rect
0538     QPainter painter(&px);
0539     const QPen pen = QPen(Qt::lightGray, 2/transform().m11());
0540     painter.setPen(pen);
0541     QRect rect = px.rect();
0542     rect.setWidth(rect.width()-pen.widthF()/2);
0543     rect.setHeight(rect.height()-pen.widthF()/2);
0544     painter.drawRect(rect);
0545 
0546     //set the pixmap
0547     m_image->m_magnificationWindow->setPixmap(px);
0548     m_image->m_magnificationWindow->setPos(pos.x()- px.width()/2, pos.y()- px.height()/2);
0549 
0550     m_image->m_magnificationWindow->setVisible(true);
0551 }
0552 
0553 void DatapickerImageView::contextMenuEvent(QContextMenuEvent* e) {
0554     Q_UNUSED(e);
0555     //no need to propagate the event to the scene and graphics items
0556     QMenu *menu = new QMenu(this);
0557     this->createContextMenu(menu);
0558     menu->exec(QCursor::pos());
0559 }
0560 
0561 //##############################################################################
0562 //####################################  SLOTs   ###############################
0563 //##############################################################################
0564 void DatapickerImageView::mouseModeChanged(QAction* action) {
0565     m_mouseMode = (DatapickerImageView::MouseMode)action->data().toInt();
0566 
0567     if (action == navigationModeAction) {
0568         setInteractive(false);
0569         setDragMode(QGraphicsView::ScrollHandDrag);
0570         m_image->setSegmentsHoverEvent(false);
0571     } else if (action == zoomSelectionModeAction){
0572         setInteractive(false);
0573         setDragMode(QGraphicsView::NoDrag);
0574         m_image->setSegmentsHoverEvent(false);
0575         setCursor(Qt::ArrowCursor);
0576     } else {
0577         setInteractive(true);
0578         setDragMode(QGraphicsView::NoDrag);
0579         m_image->setSegmentsHoverEvent(true);
0580         setCursor(Qt::CrossCursor);
0581 
0582         if (currentPlotPointsTypeAction != action) {
0583             if (action == setAxisPointsAction) {
0584                 int count = m_image->childCount<DatapickerPoint>(AbstractAspect::ChildIndexFlag::IncludeHidden);
0585                 if (count) {
0586                     auto button = QMessageBox::question(this, i18n("Remove existing reference points?"),
0587                                 i18n("All available reference points will be removed. Do you want to continue?"));
0588                     if (button != QMessageBox::Yes) {
0589                         currentPlotPointsTypeAction->setChecked(true);
0590                         return;
0591                     }
0592                 }
0593 
0594                 m_image->setPlotPointsType(DatapickerImage::PointsType::AxisPoints);
0595             } else if (action == setCurvePointsAction)
0596                 m_image->setPlotPointsType(DatapickerImage::PointsType::CurvePoints);
0597             else if (action == selectSegmentAction)
0598                 m_image->setPlotPointsType(DatapickerImage::PointsType::SegmentPoints);
0599 
0600             currentPlotPointsTypeAction = action;
0601         }
0602     }
0603 }
0604 
0605 void DatapickerImageView::changeZoom(QAction* action) {
0606     if (action == zoomInViewAction)
0607         zoom(1);
0608     else if (action == zoomOutViewAction)
0609         zoom(-1);
0610     else if (action == zoomOriginAction) {
0611         static const float hscale = QApplication::desktop()->physicalDpiX()/(25.4*Worksheet::convertToSceneUnits(1, Worksheet::Unit::Millimeter));
0612         static const float vscale = QApplication::desktop()->physicalDpiY()/(25.4*Worksheet::convertToSceneUnits(1, Worksheet::Unit::Millimeter));
0613         setTransform(QTransform::fromScale(hscale, vscale));
0614         m_rotationAngle = 0;
0615     } else if (action == zoomFitPageWidthAction) {
0616         float scaleFactor = viewport()->width()/scene()->sceneRect().width();
0617         setTransform(QTransform::fromScale(scaleFactor, scaleFactor));
0618         m_rotationAngle = 0;
0619     } else if (action == zoomFitPageHeightAction) {
0620         float scaleFactor = viewport()->height()/scene()->sceneRect().height();
0621         setTransform(QTransform::fromScale(scaleFactor, scaleFactor));
0622         m_rotationAngle = 0;
0623     }
0624 
0625     currentZoomAction = action;
0626     if (tbZoom)
0627         tbZoom->setDefaultAction(action);
0628 
0629     //change and set angle if tranform reset
0630     changeRotationAngle();
0631 }
0632 
0633 void DatapickerImageView::changeSelectedItemsPosition(QAction* action) {
0634     if (scene()->selectedItems().isEmpty())
0635         return;
0636 
0637     QPointF shift(0, 0);
0638     if (action == shiftLeftAction)
0639         shift.setX(1);
0640     else if (action == shiftRightAction)
0641         shift.setX(-1);
0642     else if (action == shiftUpAction)
0643         shift.setY(-1);
0644     else if (action == shiftDownAction)
0645         shift.setY(1);
0646 
0647     m_image->beginMacro(i18n("%1: change position of selected DatapickerPoints.", m_image->name()));
0648     const QVector<DatapickerPoint*> axisPoints = m_image->children<DatapickerPoint>(AbstractAspect::ChildIndexFlag::IncludeHidden);
0649     for (auto* point : axisPoints) {
0650         if (!point->graphicsItem()->isSelected())
0651             continue;
0652 
0653         QPointF newPos = point->position();
0654         newPos = newPos + shift;
0655         point->setPosition(newPos);
0656 
0657         int pointIndex = m_image->indexOfChild<DatapickerPoint>(point, AbstractAspect::ChildIndexFlag::IncludeHidden);
0658         if (pointIndex == -1) continue;
0659         DatapickerImage::ReferencePoints points = m_image->axisPoints();
0660         points.scenePos[pointIndex].setX(point->position().x());
0661         points.scenePos[pointIndex].setY(point->position().y());
0662         m_image->setUndoAware(false);
0663         m_image->setAxisPoints(points);
0664         m_image->setUndoAware(true);
0665     }
0666 
0667     for (auto* curve : m_image->parentAspect()->children<DatapickerCurve>()) {
0668         for (auto* point : curve->children<DatapickerPoint>(AbstractAspect::ChildIndexFlag::IncludeHidden)) {
0669             if (!point->graphicsItem()->isSelected())
0670                 continue;
0671 
0672             QPointF newPos = point->position();
0673             newPos = newPos + shift;
0674             point->setPosition(newPos);
0675         }
0676     }
0677 
0678     m_image->endMacro();
0679     if (m_image->m_magnificationWindow && m_image->m_magnificationWindow->isVisible())
0680         updateMagnificationWindow();
0681 }
0682 
0683 void DatapickerImageView::magnificationChanged(QAction* action) {
0684     if (action == noMagnificationAction)
0685         magnificationFactor = 0;
0686     else if (action == twoTimesMagnificationAction)
0687         magnificationFactor = 2;
0688     else if (action == threeTimesMagnificationAction)
0689         magnificationFactor = 3;
0690     else if (action == fourTimesMagnificationAction)
0691         magnificationFactor = 4;
0692     else if (action == fiveTimesMagnificationAction)
0693         magnificationFactor = 5;
0694 }
0695 
0696 void DatapickerImageView::addCurve() {
0697     m_datapicker->beginMacro(i18n("%1: add new curve.", m_datapicker->name()));
0698     DatapickerCurve* curve = new DatapickerCurve(i18n("Curve"));
0699     curve->addDatasheet(m_image->axisPoints().type);
0700     m_datapicker->addChild(curve);
0701     m_datapicker->endMacro();
0702 
0703     setCurvePointsAction->setChecked(true);
0704     mouseModeChanged(setCurvePointsAction);
0705 }
0706 
0707 void DatapickerImageView::changeRotationAngle() {
0708     this->rotate(m_rotationAngle);
0709     this->rotate(-m_image->rotationAngle());
0710     m_rotationAngle = m_image->rotationAngle();
0711     updateBackground();
0712 }
0713 
0714 void DatapickerImageView::handleImageActions() {
0715     if (m_image->isLoaded) {
0716         magnificationActionGroup->setEnabled(true);
0717         setAxisPointsAction->setEnabled(true);
0718 
0719         int pointsCount = m_image->childCount<DatapickerPoint>(AbstractAspect::ChildIndexFlag::IncludeHidden);
0720         if (pointsCount > 0)
0721             navigationActionGroup->setEnabled(true);
0722         else
0723             navigationActionGroup->setEnabled(false);
0724 
0725         if (pointsCount > 2) {
0726             addCurveAction->setEnabled(true);
0727             if (m_datapicker->activeCurve()) {
0728                 setCurvePointsAction->setEnabled(true);
0729                 selectSegmentAction->setEnabled(true);
0730             } else {
0731                 setCurvePointsAction->setEnabled(false);
0732                 selectSegmentAction->setEnabled(false);
0733             }
0734         } else {
0735             addCurveAction->setEnabled(false);
0736             setCurvePointsAction->setEnabled(false);
0737             selectSegmentAction->setEnabled(false);
0738         }
0739     } else {
0740         navigationActionGroup->setEnabled(false);
0741         magnificationActionGroup->setEnabled(false);
0742         setAxisPointsAction->setEnabled(false);
0743         addCurveAction->setEnabled(false);
0744         setCurvePointsAction->setEnabled(false);
0745         selectSegmentAction->setEnabled(false);
0746     }
0747 }
0748 
0749 void DatapickerImageView::exportToFile(const QString& path, const WorksheetView::ExportFormat format, const int resolution) {
0750     QRectF sourceRect;
0751     sourceRect = scene()->sceneRect();
0752 
0753     //print
0754     if (format == WorksheetView::ExportFormat::PDF) {
0755         QPrinter printer(QPrinter::HighResolution);
0756         printer.setOutputFormat(QPrinter::PdfFormat);
0757         printer.setOutputFileName(path);
0758         int w = Worksheet::convertFromSceneUnits(sourceRect.width(), Worksheet::Unit::Millimeter);
0759         int h = Worksheet::convertFromSceneUnits(sourceRect.height(), Worksheet::Unit::Millimeter);
0760         printer.setPaperSize( QSizeF(w, h), QPrinter::Millimeter);
0761         printer.setPageMargins(0,0,0,0, QPrinter::Millimeter);
0762         printer.setPrintRange(QPrinter::PageRange);
0763         printer.setCreator( QLatin1String("LabPlot ") + LVERSION );
0764 
0765         QPainter painter(&printer);
0766         painter.setRenderHint(QPainter::Antialiasing);
0767         QRectF targetRect(0, 0, painter.device()->width(),painter.device()->height());
0768         painter.begin(&printer);
0769         exportPaint(&painter, targetRect, sourceRect);
0770         painter.end();
0771     } else if (format == WorksheetView::ExportFormat::SVG) {
0772         QSvgGenerator generator;
0773         generator.setFileName(path);
0774         int w = Worksheet::convertFromSceneUnits(sourceRect.width(), Worksheet::Unit::Millimeter);
0775         int h = Worksheet::convertFromSceneUnits(sourceRect.height(), Worksheet::Unit::Millimeter);
0776         w = w*QApplication::desktop()->physicalDpiX()/25.4;
0777         h = h*QApplication::desktop()->physicalDpiY()/25.4;
0778 
0779         generator.setSize(QSize(w, h));
0780         QRectF targetRect(0, 0, w, h);
0781         generator.setViewBox(targetRect);
0782 
0783         QPainter painter;
0784         painter.begin(&generator);
0785         exportPaint(&painter, targetRect, sourceRect);
0786         painter.end();
0787     } else {
0788         //PNG
0789         //TODO add all formats supported by Qt in QImage
0790         int w = Worksheet::convertFromSceneUnits(sourceRect.width(), Worksheet::Unit::Millimeter);
0791         int h = Worksheet::convertFromSceneUnits(sourceRect.height(), Worksheet::Unit::Millimeter);
0792         w = w*resolution/25.4;
0793         h = h*resolution/25.4;
0794         QImage image(QSize(w, h), QImage::Format_ARGB32_Premultiplied);
0795         image.fill(Qt::transparent);
0796         QRectF targetRect(0, 0, w, h);
0797 
0798         QPainter painter;
0799         painter.begin(&image);
0800         painter.setRenderHint(QPainter::Antialiasing);
0801         exportPaint(&painter, targetRect, sourceRect);
0802         painter.end();
0803 
0804         image.save(path, "png");
0805     }
0806 }
0807 
0808 void DatapickerImageView::exportPaint(QPainter* painter, const QRectF& targetRect, const QRectF& sourceRect) {
0809     painter->save();
0810     painter->scale(targetRect.width()/sourceRect.width(), targetRect.height()/sourceRect.height());
0811     drawBackground(painter, sourceRect);
0812     painter->restore();
0813     m_image->setPrinting(true);
0814     scene()->render(painter, QRectF(), sourceRect);
0815     m_image->setPrinting(false);
0816 }
0817 
0818 void DatapickerImageView::print(QPrinter* printer) {
0819     const QRectF scene_rect = sceneRect();
0820     int w = Worksheet::convertFromSceneUnits(scene_rect.width(), Worksheet::Unit::Millimeter);
0821     int h = Worksheet::convertFromSceneUnits(scene_rect.height(), Worksheet::Unit::Millimeter);
0822     printer->setPaperSize( QSizeF(w, h), QPrinter::Millimeter);
0823     printer->setPageMargins(0,0,0,0, QPrinter::Millimeter);
0824     printer->setPrintRange(QPrinter::PageRange);
0825     printer->setCreator( QString("LabPlot ") + LVERSION );
0826 
0827     QPainter painter(printer);
0828     QRectF targetRect(0, 0, painter.device()->width(),painter.device()->height());
0829     painter.setRenderHint(QPainter::Antialiasing);
0830     painter.begin(printer);
0831     painter.save();
0832     painter.scale(targetRect.width()/scene_rect.width(), targetRect.height()/scene_rect.height());
0833 
0834     // canvas
0835     if (m_image->isLoaded) {
0836         if (m_image->plotImageType() == DatapickerImage::PlotImageType::OriginalImage) {
0837             QImage todraw = m_image->originalPlotImage.scaled(scene_rect.width(), scene_rect.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
0838             painter.drawImage(scene_rect.topLeft(), todraw);
0839         } else if (m_image->plotImageType() == DatapickerImage::PlotImageType::ProcessedImage) {
0840             QImage todraw = m_image->processedPlotImage.scaled(scene_rect.width(), scene_rect.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
0841             painter.drawImage(scene_rect.topLeft(), todraw);
0842         } else {
0843             painter.fillRect(scene_rect, Qt::white);
0844         }
0845     } else {
0846         painter.setBrush(QBrush(Qt::gray));
0847         painter.drawRect(scene_rect);
0848     }
0849 
0850     painter.restore();
0851     m_image->setPrinting(true);
0852     scene()->render(&painter, QRectF(), scene_rect);
0853     m_image->setPrinting(false);
0854     painter.end();
0855 }
0856 
0857 void DatapickerImageView::updateBackground() {
0858     invalidateScene(sceneRect(), QGraphicsScene::BackgroundLayer);
0859 }