File indexing completed on 2022-12-06 18:49:19

0001 /*************************************************************************************
0002  *  Copyright (C) 2007-2008 by Aleix Pol <aleixpol@kde.org>                          *
0003  *  Copyright (C) 2012-2013 by Percy Camilo T. Aucahuasi <percy.camilo.ta@gmail.com> *
0004  *                                                                                   *
0005  *  This program is free software; you can redistribute it and/or                    *
0006  *  modify it under the terms of the GNU General Public License                      *
0007  *  as published by the Free Software Foundation; either version 2                   *
0008  *  of the License, or (at your option) any later version.                           *
0009  *                                                                                   *
0010  *  This program is distributed in the hope that it will be useful,                  *
0011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of                   *
0012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                    *
0013  *  GNU General Public License for more details.                                     *
0014  *                                                                                   *
0015  *  You should have received a copy of the GNU General Public License                *
0016  *  along with this program; if not, write to the Free Software                      *
0017  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA   *
0018  *************************************************************************************/
0019 
0020 #include "plotsview2d.h"
0021 
0022 #include <QSvgGenerator>
0023 #include <QList>
0024 #include <QFile>
0025 #include <QDebug>
0026 #include <QTimer>
0027 #include <QPropertyAnimation>
0028 #include <QItemSelectionModel>
0029 #include <QCoreApplication>
0030 #include <QApplication>
0031 #include <QClipboard>
0032 
0033 #include <analitza/analyzer.h>
0034 #include <analitzaplot/plotsmodel.h>
0035 #include <cmath>
0036 
0037 using namespace Analitza;
0038 
0039 PlotsView2D::PlotsView2D(QWidget *parent)
0040     : QWidget(parent)
0041     , Plotter2D(size())
0042     , valid(false)
0043     , mode(None)
0044     , m_framed(false)
0045     , m_readonly(false)
0046     , m_selection(nullptr)
0047 {
0048     this->setFocusPolicy(Qt::ClickFocus);
0049     this->setCursor(Qt::CrossCursor);
0050     
0051     this->setMouseTracking(!m_readonly);
0052     this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
0053     this->setMinimumSize(128, 128);
0054     
0055     //BEGIN setup plotter2d grid
0056     defViewport = QRectF(QPointF(-10.0, 10.0), QSizeF(20.0, -20.0));
0057     resetViewport();
0058     
0059     //colors
0060     QColor bgcolor = palette().color(QPalette::Base);
0061     setBackgroundColor(bgcolor);
0062     
0063     QColor gridcolor = palette().color(QPalette::Midlight);
0064     setGridColor(gridcolor);
0065     //END
0066     
0067     this->setAutoFillBackground(false);
0068 }
0069 
0070 PlotsView2D::~PlotsView2D() {}
0071 
0072 void PlotsView2D::paintEvent(QPaintEvent * )
0073 {
0074     if (!valid)
0075     {
0076         if (buffer.isNull() || buffer.size()!=size())
0077             buffer = QPixmap(size());
0078         buffer.fill(backgroundColor());
0079     
0080         drawFunctions(&buffer);
0081 
0082         valid=true;
0083     }
0084     QPainter p(this);
0085     p.drawPixmap(QRect(QPoint(0,0), size()), buffer);
0086     
0087     QPen ccursor;
0088     
0089 //  finestra.setRenderHint(QPainter::Antialiasing, true);
0090     
0091     //NOTE GSOC 2012 better guards : model()->rowCount() > 0 && !mark.isNull() ... si no se cumplen que no dibuje las lineas rojas
0092     if(!m_readonly && mode==None) {
0093         //QPointF ultim = toWidget(mark);
0094         QPointF ultim(cursorPos);
0095         
0096         //Draw derivative
0097         if (!mark.isNull()) 
0098         {
0099             ultim = toWidget(mark); // si no es nulo apunto al lugar del punto de pendiente
0100             
0101             ccursor.setColor(palette().color(QPalette::Active, QPalette::Link));
0102             ccursor.setStyle(Qt::SolidLine);
0103             QLineF sl=slope(mark);
0104             sl.translate(mark);
0105             
0106             p.setPen(ccursor);
0107             p.setRenderHint(QPainter::Antialiasing, true);
0108     #ifndef Q_CC_MSVC
0109             if(!sl.isNull() && !std::isnan(sl.length()))
0110     #else
0111             if(!sl.isNull() && !_isnan(sl.length()))
0112     #endif
0113                 p.drawLine(toWidget(sl));
0114             p.setRenderHint(QPainter::Antialiasing, false);
0115         }
0116         //EOderivative
0117         
0118         ccursor.setColor(QColor(0xc0,0,0));
0119         ccursor.setStyle(Qt::SolidLine);
0120         
0121         p.setPen(ccursor);
0122         p.drawLine(QPointF(0.,ultim.y()), QPointF(size().width(), ultim.y()));
0123         p.drawLine(QPointF(ultim.x(),0.), QPointF(ultim.x(), size().height()));
0124         
0125         int w=p.fontMetrics().boundingRect(m_posText).width()+15, h=p.fontMetrics().height();
0126         
0127         if(ultim.x()+w > size().width())
0128             ultim.setX(size().width()-w);
0129         if(ultim.y()+h > size().height())
0130             ultim.setY(size().height()-h);
0131         if(ultim.y() < 0.)
0132             ultim.setY(0.);
0133         
0134         p.setPen(QPen(Qt::black));
0135         p.drawText(QPointF(ultim.x()+15., ultim.y()+15.), m_posText);
0136     } else if(!m_readonly && mode==Selection) {
0137            //NOTE GSOC 2012 accessibility code, the selection follow system rules :)
0138 
0139 //         ccursor.setColor(QColor(0xc0,0,0));
0140         QColor selcol = QPalette().highlight().color();
0141         ccursor.setColor(QPalette().highlightedText().color());
0142         ccursor.setStyle(Qt::SolidLine);
0143         p.setPen(ccursor);
0144 //         p.setBrush(QColor(0xff,0xff, 0,0x90));
0145         selcol.setAlpha(0x90);
0146         p.setBrush(selcol);
0147         p.drawRect(QRect(press,last));
0148     }
0149     
0150     if(m_framed) {
0151         QPen bord(Qt::black);
0152         p.setPen(bord);
0153         p.drawRect(QRect(QPoint(0,0), size()-QSize(2,2)));
0154     }
0155     p.end();
0156     
0157 //  qDebug() << "xxx2 " << viewport;
0158 }
0159 
0160 void PlotsView2D::wheelEvent(QWheelEvent *e)
0161 {
0162     QRectF viewport=currentViewport();
0163     int steps = e->angleDelta().y()/(8*15);
0164     qreal d = (-0.03*steps) + 1;
0165     
0166     if(d>0 || (viewport.width()+d > 2 && viewport.height()+d < 2)) {
0167         scaleViewport(d, e->position().toPoint());
0168     }
0169 }
0170 
0171 void PlotsView2D::mousePressEvent(QMouseEvent *e)
0172 {
0173     if(!m_readonly && (e->button()==Qt::LeftButton || e->button()==Qt::MiddleButton)) {
0174         last = press = e->pos();
0175         ant = toViewport(e->pos());
0176         this->setCursor(QCursor(Qt::PointingHandCursor));
0177         if(e->button()==Qt::MiddleButton || (e->button()==Qt::LeftButton && e->modifiers()&Qt::ControlModifier))
0178             mode=Pan;
0179         else if(e->button()==Qt::LeftButton)
0180             mode=Selection;
0181     }
0182 }
0183 
0184 void PlotsView2D::mouseReleaseEvent(QMouseEvent *e)
0185 {
0186     if(m_readonly)
0187         this->setCursor(Qt::ArrowCursor);
0188     else
0189         this->setCursor(Qt::CrossCursor);
0190     
0191     if(!m_readonly && mode==Selection) {
0192         QPointF pd = toViewport(e->pos())-toViewport(press);
0193         
0194         QPoint pd2 = e->pos()-press;
0195         QRect rr(press, QSize(pd2.x(), pd2.y()));
0196         
0197         if(rr.width()>20 && rr.height()>20) { //if selection is not very small
0198             QRectF r(fromWidget(press), QSizeF(pd.x(), pd.y()));
0199             
0200             if(r.top()<r.bottom()) {
0201                 double aux = r.bottom();
0202                 r.setBottom(r.top());
0203                 r.setTop(aux);
0204             }
0205             
0206             if(r.right()<r.left()) {
0207                 double aux = r.left();
0208                 r.setLeft(r.right());
0209                 r.setRight(aux);
0210             }
0211             
0212             setViewport(r);
0213         } else
0214             sendStatus(QCoreApplication::tr("Selected viewport too small"));
0215     }
0216     
0217     mode = None;
0218     this->repaint();
0219 }
0220 
0221 void PlotsView2D::mouseMoveEvent(QMouseEvent *e)
0222 {
0223     cursorPos = e->pos();
0224     QPair<QPointF, QString> img=calcImage(fromWidget(e->pos()));
0225     mark=img.first;
0226     
0227     if(!m_readonly && mode==Pan && ant != toViewport(e->pos())){
0228         moveViewport(e->pos() - press);
0229         
0230         press = e->pos();
0231     } else if(e->buttons()&Qt::LeftButton) {
0232         last = e->pos();
0233     } else if(e->buttons()==0) {
0234         if(img.second.isEmpty()) {
0235             mark = fromWidget(e->pos());
0236             sendStatus(QCoreApplication::tr("x=%1 y=%2").arg(mark.x()).arg(mark.y()));
0237         } else
0238             sendStatus(img.second);
0239     }
0240     
0241     this->repaint();
0242 }
0243 
0244 void PlotsView2D::keyPressEvent(QKeyEvent * e)
0245 {
0246     //TODO use grid info (inc) here, made a public method to return current scale increment
0247     const double xstep=currentViewport().width()/12., ystep=currentViewport().height()/10.;
0248     
0249     switch(e->key()) {
0250         case Qt::Key_Right:
0251             setViewport(lastUserViewport().translated(xstep, 0));
0252             break;
0253         case Qt::Key_Left:
0254             setViewport(lastUserViewport().translated(-xstep, 0));
0255             break;
0256         case Qt::Key_Down:
0257             setViewport(lastUserViewport().translated(0, -ystep));
0258             break;
0259         case Qt::Key_Up:
0260             setViewport(lastUserViewport().translated(0, ystep));
0261             break;
0262         case Qt::Key_Minus:
0263             zoomOut();
0264             break;
0265         case Qt::Key_Plus:
0266             zoomIn();
0267             break;
0268         default:
0269             return;
0270     }
0271 }
0272 
0273 void PlotsView2D::resizeEvent(QResizeEvent * ev)
0274 {
0275     if (ev->size() != buffer.size()) {
0276         buffer = QPixmap(ev->size());
0277         setPaintedSize(ev->size());
0278     }
0279 }
0280 
0281 bool PlotsView2D::toImage(const QString &path, Format f)
0282 {
0283     Q_ASSERT(!path.isEmpty());
0284     bool b=false;
0285     
0286     switch(f) {
0287         case SVG: {
0288             QFile f(path);
0289             QSvgGenerator gen;
0290             gen.setOutputDevice(&f);
0291             gen.setSize(this->size());
0292             drawFunctions(&gen);
0293             b=true;
0294             forceRepaint();
0295         }   break;
0296         case PNG:
0297             this->repaint();
0298             b=buffer.save(path, "PNG");
0299             break;
0300     }
0301     
0302     return b;
0303 }
0304 
0305 void PlotsView2D::snapshotToClipboard()
0306 {
0307     QClipboard *cb = QApplication::clipboard();
0308 
0309     cb->setImage(buffer.toImage());
0310 }
0311 
0312 void Analitza::PlotsView2D::modelChanged()
0313 {
0314 }
0315 
0316 void PlotsView2D::setReadOnly(bool ro)
0317 {
0318     m_readonly=ro;
0319     setCursor(ro ? Qt::ArrowCursor : Qt::CrossCursor);
0320     setMouseTracking(!ro);
0321 }
0322 
0323 QRectF PlotsView2D::definedViewport() const
0324 {
0325     return lastUserViewport();
0326 }
0327 
0328 void PlotsView2D::viewportChanged()
0329 {
0330     QRectF userViewport=lastUserViewport(), viewport=currentViewport();
0331     
0332     sendStatus(QStringLiteral("(%1, %2)-(%3, %4)")
0333             .arg(viewport.left()).arg(viewport.top()).arg(viewport.right()).arg(viewport.bottom()));
0334     emit viewportChanged(userViewport);
0335 }
0336 
0337 int PlotsView2D::currentFunction() const
0338 {
0339     if (!model())
0340         return -1;
0341     
0342     int ret=-1;
0343     if(m_selection) {
0344         ret=m_selection->currentIndex().row();
0345     }
0346     
0347     return ret;
0348 }
0349 
0350 void PlotsView2D::setSelectionModel(QItemSelectionModel* selection)
0351 {
0352     Q_ASSERT(selection->model() == model());
0353 
0354     if (m_selection)
0355         disconnect(m_selection,SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(forceRepaint()));
0356 
0357     m_selection = selection;
0358 
0359     connect(m_selection,SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(forceRepaint()));
0360 }