File indexing completed on 2022-09-20 12:13:56

0001 /*************************************************************************************
0002  *  Copyright (C) 2007-2009 by Aleix Pol <aleixpol@kde.org>                          *
0003  *  Copyright (C) 2010-2012 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 "plotsmodel.h"
0021 
0022 #include "plotsfactory.h"
0023 #include "plotitem.h"
0024 
0025 #include "analitza/analitzautils.h"
0026 #include <analitza/variables.h>
0027 #include <analitzaplot/functiongraph.h>
0028 
0029 #include <QDebug>
0030 #include <QIcon>
0031 #include <QPixmap>
0032 #include <QCoreApplication>
0033 #include <QRandomGenerator>
0034 
0035 using namespace Analitza;
0036 
0037 PlotsModel::PlotsModel(QObject* parent)
0038     : QAbstractListModel(parent)
0039     , m_resolution(500)
0040     , m_namingCount(0)
0041 {}
0042 
0043 PlotsModel::~PlotsModel()
0044 {
0045     clear();
0046 }
0047 
0048 QHash<int, QByteArray> PlotsModel::roleNames() const
0049 {
0050     auto ret = QAbstractListModel::roleNames();
0051     ret.insert(DescriptionRole, "description");
0052     return ret;
0053 }
0054 
0055 void PlotsModel::clear()
0056 {
0057     if (!m_items.isEmpty()) {
0058         beginRemoveRows(QModelIndex(), 0, m_items.count()-1);
0059         qDeleteAll(m_items);
0060         m_items.clear();
0061         endRemoveRows();
0062     }
0063 }
0064 
0065 Qt::ItemFlags PlotsModel::flags(const QModelIndex & index) const
0066 {
0067     if(index.isValid())
0068         return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEditable;
0069     else
0070         return Qt::NoItemFlags;
0071 }
0072 
0073 QVariant PlotsModel::headerData(int section, Qt::Orientation orientation, int role) const
0074 {
0075     if(role==Qt::DisplayRole && orientation==Qt::Horizontal) {
0076         switch(section) 
0077         {
0078             case 0: return QCoreApplication::translate("@title:column", "Name");
0079             case 1: return QCoreApplication::translate("@title:column", "Plot");
0080         }
0081     }
0082     
0083     return QAbstractListModel::headerData(section, orientation, role);
0084 }
0085 
0086 QVariant PlotsModel::data(const QModelIndex & index, int role) const
0087 {
0088     if(!index.isValid() || index.row()>=m_items.count())
0089         return QVariant();
0090 
0091     PlotItem *tmpcurve = m_items.at(index.row());
0092     
0093     switch(role)
0094     {
0095         case Qt::DisplayRole:
0096         case Qt::EditRole:
0097             switch(index.column()) 
0098             {
0099                 case 0: return tmpcurve->name();
0100                 case 1: return tmpcurve->display();
0101             }
0102             break;
0103         case Qt::DecorationRole:
0104             if(index.column()==0)
0105             {
0106                 QPixmap ico(16, 16);
0107                 ico.fill(tmpcurve->color());
0108                 return QIcon(ico);
0109             } 
0110             else 
0111                 return QIcon::fromTheme(tmpcurve->iconName());
0112         case Qt::ToolTipRole:
0113             return tmpcurve->name();
0114         case Qt::StatusTipRole:
0115             return tmpcurve->typeName();
0116         case Qt::CheckStateRole:
0117             if(index.column()==0) 
0118                 return tmpcurve->isVisible() ? Qt::Checked : Qt::Unchecked;
0119             break;
0120         case DimensionRole:
0121             return int(tmpcurve->spaceDimension());
0122         case PlotRole:
0123             return QVariant::fromValue<PlotItem*>(tmpcurve);
0124         case DescriptionRole:
0125             return tmpcurve->display();
0126     }
0127 
0128     
0129     return QVariant();
0130 }
0131 
0132 bool PlotsModel::setData(const QModelIndex& index, const QVariant& value, int role)
0133 {
0134     if (!index.isValid())
0135         return false;
0136 
0137     switch(role)
0138     {
0139         case Qt::EditRole:
0140             switch(index.column()) 
0141             {
0142                 case 0: {
0143                     //FIXME: actually I think the name should be stored in the model instead of plotItem
0144                     //if there was another plot with that name, it shouldn't be accepted
0145                     QString newName = value.toString();
0146                     if(!newName.isEmpty()) {
0147                         m_items[index.row()]->setName(newName);
0148                         emit dataChanged(index, index);
0149                     }
0150                     return !newName.isEmpty();
0151                 }
0152                 case 1: {
0153                     Analitza::Expression valexp = AnalitzaUtils::variantToExpression(value);
0154                     PlotItem* it = m_items[index.row()];
0155 
0156                     PlotBuilder plot = PlotsFactory::self()->requestPlot(valexp, it->spaceDimension());
0157                     if (plot.canDraw()) {
0158                         if (m_items[index.row()]->expression() != valexp) {
0159                             QColor color=it->color();
0160                             QString name=it->name();
0161                             delete m_items[index.row()];
0162                             m_items[index.row()] = plot.create(color, name);
0163                         }
0164                         
0165                         emit dataChanged(index, index);
0166                         
0167                         return true;
0168                     }
0169                     return false;
0170                 }
0171             } //fallthrough
0172         case Qt::CheckStateRole:
0173             m_items[index.row()]->setVisible(value.toBool());
0174             return true;
0175         case Qt::DecorationRole:
0176             m_items[index.row()]->setColor(value.value<QColor>());
0177             return true;
0178     }
0179 
0180     return false;
0181 }
0182 
0183 int PlotsModel::rowCount(const QModelIndex & parent) const
0184 {
0185     if(parent.isValid())
0186         return 0;
0187     else
0188         return m_items.size();
0189 }
0190 
0191 int PlotsModel::columnCount(const QModelIndex& parent) const
0192 {
0193     Q_UNUSED(parent);
0194     return 2;
0195 }
0196 
0197 
0198 bool PlotsModel::removeRows(int row, int count, const QModelIndex& parent)
0199 {
0200     Q_ASSERT(row<m_items.size());
0201     if(parent.isValid())
0202         return false;
0203     
0204     beginRemoveRows(QModelIndex(), row, row+count-1);
0205     for (int i = 0; i < count; ++i) {
0206         delete m_items.takeAt(row);
0207     }
0208     endRemoveRows();
0209     
0210     return true;
0211 }
0212 
0213 void PlotsModel::addPlot(PlotItem* it)
0214 {
0215     Q_ASSERT(it);
0216 
0217     beginInsertRows(QModelIndex(), m_items.count(), m_items.count());
0218     it->setModel(this);
0219     m_items.append(it);
0220 
0221     if(FunctionGraph* g=dynamic_cast<FunctionGraph*>(it))
0222         g->setResolution(m_resolution);
0223     endInsertRows();
0224 
0225     m_namingCount++;
0226 }
0227 
0228 void PlotsModel::updatePlot(int row, PlotItem* it)
0229 {
0230     it->setModel(this);
0231     delete m_items[row];
0232     m_items[row]=it;
0233     
0234     QModelIndex idx = index(row);
0235     emit dataChanged(idx, idx);
0236 }
0237 
0238 void PlotsModel::emitChanged(PlotItem* it)
0239 {
0240     int row = m_items.indexOf(it);
0241     QModelIndex idx = index(row);
0242     emit dataChanged(idx, idx);
0243 }
0244 
0245 QModelIndex PlotsModel::indexForName(const QString& name)
0246 {
0247     const int rows = rowCount();
0248     for(int i=0; i<rows; i++) {
0249         QModelIndex idx = index(i);
0250         if(idx.data().toString()==name)
0251             return idx;
0252     }
0253     return QModelIndex();
0254 }
0255 
0256 QString PlotsModel::freeId() const
0257 {
0258     return 'f'+QString::number(m_namingCount);
0259 }
0260 
0261 void PlotsModel::setResolution(int res)
0262 {
0263     m_resolution = res;
0264     for(int i=0; i<rowCount(); i++) {
0265         FunctionGraph* g = dynamic_cast<FunctionGraph*>(m_items[i]);
0266         if(g)
0267             g->setResolution(res);
0268     }
0269 }
0270 
0271 static QColor randomFunctionColor() { return QColor::fromHsv(QRandomGenerator::global()->bounded(255), 255, 225); }
0272 
0273 QStringList PlotsModel::addFunction(const QString& expression, Dimension dim, const QSharedPointer<Analitza::Variables>& vars)
0274 {
0275     Analitza::Expression e(expression, Analitza::Expression::isMathML(expression));
0276 
0277     QString fname;
0278     do {
0279         fname = freeId();
0280     } while(vars && vars->contains(fname));
0281     QColor fcolor = randomFunctionColor();
0282 
0283     QStringList err;
0284     PlotBuilder req = PlotsFactory::self()->requestPlot(e, dim, vars);
0285     if(req.canDraw()) {
0286         auto it = req.create(fcolor, fname);
0287 
0288         if(it->isCorrect())
0289             addPlot(it);
0290         else {
0291             err = it->errors();
0292             delete it;
0293         }
0294     }
0295 
0296     return err;
0297 }
0298 
0299 bool Analitza::PlotsModel::canAddFunction(const QString& expression, int _dim, const QSharedPointer<Analitza::Variables>& vars)
0300 {
0301     const Analitza::Dimension dim = static_cast<Analitza::Dimension>(_dim);
0302     Analitza::Expression e(expression, Analitza::Expression::isMathML(expression));
0303     PlotBuilder req = PlotsFactory::self()->requestPlot(e, dim, vars);
0304     return req.canDraw();
0305 }