File indexing completed on 2024-04-21 03:42:06

0001 /*
0002     KmPlot - a math. function plotter for the KDE-Desktop
0003 
0004     SPDX-FileCopyrightText: 2006 David Saxton <david@bluehaze.org>
0005 
0006     This file is part of the KDE Project.
0007     KmPlot is part of the KDE-EDU Project.
0008 
0009     SPDX-License-Identifier: GPL-2.0-or-later
0010 
0011 */
0012 
0013 #include "initialconditionseditor.h"
0014 #include "equationedit.h"
0015 #include "parser.h"
0016 
0017 #include <QHeaderView>
0018 #include <assert.h>
0019 
0020 // BEGIN helper functions
0021 /**
0022  * \return a pointer to the differential state for the given function and row.
0023  */
0024 DifferentialState *differentialState(DifferentialStates *states, int row)
0025 {
0026     if (!states)
0027         return nullptr;
0028 
0029     if (row < 0 || row >= states->size())
0030         return nullptr;
0031 
0032     return &(*states)[row];
0033 }
0034 
0035 /**
0036  * \return a pointer to the Value for the given function, row and column
0037  */
0038 Value *value(DifferentialStates *states, int row, int column)
0039 {
0040     DifferentialState *state = differentialState(states, row);
0041     if (!state)
0042         return nullptr;
0043 
0044     if (column == 0)
0045         return &state->x0;
0046     else
0047         return &state->y0[column - 1];
0048 }
0049 // END helper functions
0050 
0051 // BEGIN class InitialConditionsModel
0052 InitialConditionsModel::InitialConditionsModel(InitialConditionsEditor *parent)
0053     : QAbstractTableModel(parent)
0054 {
0055     m_parent = parent;
0056 }
0057 
0058 int InitialConditionsModel::rowCount(const QModelIndex & /*parent*/) const
0059 {
0060     return m_parent->differentialStates()->size();
0061 }
0062 
0063 int InitialConditionsModel::columnCount(const QModelIndex & /*parent*/) const
0064 {
0065     return m_parent->differentialStates()->order() + 1;
0066 }
0067 
0068 QVariant InitialConditionsModel::data(const QModelIndex &index, int role) const
0069 {
0070     Value *v = value(m_parent->differentialStates(), index.row(), index.column());
0071     if (!v)
0072         return QVariant();
0073 
0074     switch ((Qt::ItemDataRole)role) {
0075     case Qt::DisplayRole:
0076     case Qt::EditRole:
0077     case Qt::AccessibleTextRole:
0078         return v->expression();
0079 
0080     case Qt::ToolTipRole:
0081         /// \todo return a description of the initial condition
0082         return QVariant();
0083 
0084     case Qt::DecorationRole:
0085     case Qt::StatusTipRole:
0086         return QVariant();
0087 
0088     case Qt::TextAlignmentRole:
0089         return Qt::AlignLeft;
0090 
0091     case Qt::ForegroundRole:
0092         return QColor(Qt::black);
0093 
0094     case Qt::WhatsThisRole:
0095     case Qt::AccessibleDescriptionRole:
0096     case Qt::CheckStateRole:
0097     case Qt::BackgroundRole:
0098     case Qt::SizeHintRole:
0099     case Qt::FontRole:
0100     case Qt::UserRole:
0101         return QVariant();
0102 
0103     default:
0104         return QVariant();
0105     }
0106 }
0107 
0108 bool InitialConditionsModel::setData(const QModelIndex &index, const QVariant &variant, int role)
0109 {
0110     if (role != Qt::EditRole)
0111         return false;
0112 
0113     Value *v = value(m_parent->differentialStates(), index.row(), index.column());
0114     if (!v)
0115         return false;
0116 
0117     v->updateExpression(variant.toString());
0118     emit dataChanged(index, index);
0119     return true;
0120 }
0121 
0122 QVariant InitialConditionsModel::headerData(int section, Qt::Orientation orientation, int role) const
0123 {
0124     Equation *eq = m_parent->equation();
0125 
0126     if (role != Qt::DisplayRole || !eq)
0127         return QAbstractTableModel::headerData(section, orientation, role);
0128 
0129     // Don't display row headers
0130     if (orientation == Qt::Vertical)
0131         return QVariant();
0132 
0133     QString param;
0134     QStringList variables = eq->variables();
0135     if (variables.isEmpty())
0136         param = 'x';
0137     else
0138         param = variables.first();
0139 
0140     param += SubscriptZeroSymbol;
0141 
0142     if (section == 0)
0143         return param;
0144     else
0145         return QStringLiteral("%1%2(%3)").arg(eq->name(true)).arg(QString(), section - 1, '\'').arg(param);
0146 }
0147 
0148 Qt::ItemFlags InitialConditionsModel::flags(const QModelIndex & /*index*/) const
0149 {
0150     return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
0151 }
0152 
0153 bool InitialConditionsModel::insertRows(int position, int rows, const QModelIndex & /*parent*/)
0154 {
0155     if (!m_parent->differentialStates())
0156         return false;
0157 
0158     beginInsertRows(QModelIndex(), position, position + rows - 1);
0159 
0160     for (int row = 0; row < rows; ++row)
0161         m_parent->differentialStates()->add();
0162 
0163     endInsertRows();
0164     return true;
0165 }
0166 
0167 bool InitialConditionsModel::removeRows(int position, int rows, const QModelIndex & /*parent*/)
0168 {
0169     beginRemoveRows(QModelIndex(), position, position + rows - 1);
0170     m_parent->differentialStates()->remove(position, rows);
0171     endRemoveRows();
0172     return true;
0173 }
0174 // END class InitialConditionsModel
0175 
0176 // BEGIN class InitialConditionsView
0177 InitialConditionsView::InitialConditionsView(QWidget *parent)
0178     : QTableView(parent)
0179 {
0180     setSelectionMode(QAbstractItemView::ExtendedSelection);
0181     setSelectionBehavior(QAbstractItemView::SelectRows);
0182     horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
0183     horizontalHeader()->setSectionsClickable(false);
0184     verticalHeader()->hide();
0185 }
0186 // END class InitialConditionsView
0187 
0188 // BEGIN class InitialConditionsDelegate
0189 InitialConditionsDelegate::InitialConditionsDelegate(InitialConditionsEditor *parent)
0190     : QItemDelegate(parent)
0191 {
0192     m_parent = parent;
0193     m_lastEditor = nullptr;
0194 }
0195 
0196 QWidget *InitialConditionsDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem & /*option*/, const QModelIndex &index) const
0197 {
0198     Value *v = value(m_parent->differentialStates(), index.row(), index.column());
0199     if (!v)
0200         return nullptr;
0201 
0202     m_lastEditor = new EquationEdit(parent);
0203     connect(m_lastEditor, &EquationEdit::returnPressed, this, &InitialConditionsDelegate::equationEditDone);
0204     m_lastEditor->setFocus();
0205     return m_lastEditor;
0206 }
0207 
0208 void InitialConditionsDelegate::equationEditDone()
0209 {
0210     emit commitData(m_lastEditor);
0211     emit closeEditor(m_lastEditor);
0212 }
0213 
0214 void InitialConditionsDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
0215 {
0216     QString expression = index.model()->data(index, Qt::DisplayRole).toString();
0217     static_cast<EquationEdit *>(editor)->setText(expression);
0218 }
0219 
0220 void InitialConditionsDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
0221 {
0222     EquationEdit *edit = static_cast<EquationEdit *>(editor);
0223     model->setData(index, edit->text());
0224 }
0225 
0226 void InitialConditionsDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex & /* index */) const
0227 {
0228     editor->setGeometry(option.rect);
0229 }
0230 // END class InitialConditionsDelegate
0231 
0232 // BEGIN class InitialConditionsEditor
0233 InitialConditionsEditor::InitialConditionsEditor(QWidget *parent)
0234     : QWidget(parent)
0235 {
0236     m_equation = nullptr;
0237 
0238     setupUi(this);
0239     layout()->setContentsMargins(0, 0, 0, 0);
0240     connect(addButton, &QPushButton::clicked, this, &InitialConditionsEditor::add);
0241     connect(removeButton, &QPushButton::clicked, this, &InitialConditionsEditor::remove);
0242 
0243     m_model = new InitialConditionsModel(this);
0244     view->setModel(m_model);
0245     view->setItemDelegate(new InitialConditionsDelegate(this));
0246 
0247     connect(m_model, &InitialConditionsModel::dataChanged, this, &InitialConditionsEditor::dataChanged);
0248 }
0249 
0250 void InitialConditionsEditor::setOrder(int order)
0251 {
0252     m_model->beginResetModel();
0253     m_states.setOrder(order);
0254     m_model->endResetModel();
0255 }
0256 
0257 void InitialConditionsEditor::init(Function *function)
0258 {
0259     m_model->beginResetModel();
0260 
0261     if (function) {
0262         m_equation = function->eq[0];
0263         m_states = m_equation->differentialStates;
0264     } else {
0265         m_equation = nullptr;
0266     }
0267 
0268     m_model->endResetModel();
0269 }
0270 
0271 void InitialConditionsEditor::add()
0272 {
0273     m_model->insertRows(0, 1, QModelIndex());
0274     emit dataChanged();
0275 }
0276 
0277 void InitialConditionsEditor::remove()
0278 {
0279     const QModelIndexList selected = view->selectionModel()->selectedIndexes();
0280 
0281     QMap<int, void *> sorted;
0282     for (const QModelIndex &index : selected)
0283         sorted.insert(-index.row(), nullptr);
0284     const QList<int> indexes = sorted.keys();
0285 
0286     for (int row : indexes)
0287         m_model->removeRows(-row, 1, QModelIndex());
0288 
0289     emit dataChanged();
0290 }
0291 // END class InitialConditionsEditor
0292 
0293 #include "moc_initialconditionseditor.cpp"