diff --git a/src/backends/maxima/maximavariablemodel.cpp b/src/backends/maxima/maximavariablemodel.cpp index f449741c..74d3408f 100644 --- a/src/backends/maxima/maximavariablemodel.cpp +++ b/src/backends/maxima/maximavariablemodel.cpp @@ -1,279 +1,230 @@ /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2012 Alexander Rieder */ #include "maximavariablemodel.h" #include "maximasession.h" #include "maximaexpression.h" #include "textresult.h" #include "latexresult.h" #include #include //command used to inspect a maxima variable. %1 is the name of that variable const QString MaximaVariableModel::inspectCommand=QLatin1String(":lisp($disp $%1)"); const QString MaximaVariableModel::variableInspectCommand=QLatin1String(":lisp(cantor-inspect $%1)"); MaximaVariableModel::MaximaVariableModel( MaximaSession* session) : Cantor::DefaultVariableModel(session) { } void MaximaVariableModel::clear() { emit functionsRemoved(functionNames()); - emit variablesRemoved(variableNames()); m_functions.clear(); - m_variables.clear(); DefaultVariableModel::clearVariables(); } void MaximaVariableModel::update() { qDebug()<<"checking for new variables"; const QString& cmd1=variableInspectCommand.arg(QLatin1String("values")); Cantor::Expression* expr1=session()->evaluateExpression(cmd1, Cantor::Expression::FinishingBehavior::DoNotDelete, true); connect(expr1, &Cantor::Expression::statusChanged, this, &MaximaVariableModel::parseNewVariables); qDebug()<<"checking for new functions"; const QString& cmd2=inspectCommand.arg(QLatin1String("functions")); Cantor::Expression* expr2=session()->evaluateExpression(cmd2, Cantor::Expression::FinishingBehavior::DoNotDelete, true); connect(expr2, &Cantor::Expression::statusChanged, this, &MaximaVariableModel::parseNewFunctions); } QList parse(MaximaExpression* expr) { if(!expr || expr->status()!=Cantor::Expression::Done || expr->results().isEmpty()) { return QList(); } //for parsing of names and values below (old code) we need to combine multiple results back to one string QString text; for (auto* result : expr->results()) { if(result->type()==Cantor::TextResult::Type) text += dynamic_cast(result)->plain(); else if(expr->result()->type()==Cantor::LatexResult::Type) text += dynamic_cast(result)->plain(); } const int nameIndex=text.indexOf(QLatin1Char(']')); QString namesString=text.left(nameIndex); //namesString.chop(1); namesString=namesString.mid(1); namesString=namesString.trimmed(); qDebug()<<"variable names: "<(); QStringList variableNames; QString valuesString; bool hasValues = false; QStringList variableValues; if ( namesString.contains(QLatin1Char(')')) ) { //function definition(s): e.g //text = "[f1(x),f2(x,y),f3(x,y,z)]\n$DONE" //nameString = f1(x),f2(x,y),f3(x,y,z) //variableString = "\n$DONE" variableNames = namesString.split(QLatin1String("),")); } else { //variable definition(s): e.g. //text = "[a,b]\n1\n\"-cantor-value-separator-\"\n2\n\"-cantor-value-separator-\"\n($A $B)" //nameString = "[a,b]" //variableString = "\n1\n\"-cantor-value-separator-\"\n2\n\"-cantor-value-separator-\"\n($A $B)" variableNames = namesString.split(QLatin1Char(',')); valuesString = text.mid(nameIndex+1).trimmed(); valuesString = valuesString.remove(QLatin1String("\n")); //lists with many elements have line breaks, remove them variableValues = valuesString.split(QLatin1String("\"-cantor-value-separator-\"")); hasValues = variableValues.isEmpty(); } qDebug()< variables; variables.reserve(variableNames.size()); for(int i=0;ii) { var.value=variableValues.at(i).trimmed(); var.value=var.value.remove(QLatin1String("\n")); //lists with many elements have line breaks, remove them } else var.value=QLatin1String("unknown"); variables<(sender()); QList newVars=parse(expr); - QStringList addedVars; - QStringList removedVars; - //remove the old variables - for (const Variable& var : m_variables) - { - //check if this var is present in the new variables - bool found=false; - for (const Variable& var2 : newVars) - { - if(var.name==var2.name) - { - found=true; - break; - } - } - - if(!found) - { - removeVariable(var); - removedVars<deleteLater(); - - emit variablesAdded(addedVars); - emit variablesRemoved(removedVars); } void MaximaVariableModel::parseNewFunctions(Cantor::Expression::Status status) { if (status != Cantor::Expression::Done && status != Cantor::Expression::Error) return; qDebug()<<"parsing functions"; MaximaExpression* expr=static_cast(sender()); QList newVars=parse(expr); QStringList addedVars; QStringList removedVars; //remove the old variables for (const Variable& var : m_functions) { //check if this var is present in the new variables bool found=false; for (const Variable& var2 : newVars) { if(var.name==var2.name) { found=true; break; } } if(!found) { removeVariable(var); removedVars<deleteLater(); emit functionsAdded(addedVars); emit functionsRemoved(removedVars); } bool MaximaVariableModel::isUpdateCommand(const QString& cmd) const { return cmd == variableInspectCommand.arg(QLatin1String("values")) || cmd == inspectCommand.arg(QLatin1String("functions")); } MaximaSession* MaximaVariableModel::maximaSession() { return static_cast (session()); } -QList MaximaVariableModel::variables() -{ - return m_variables; -} - QList MaximaVariableModel::functions() { return m_functions; } -QStringList MaximaVariableModel::variableNames() -{ - QStringList names; - for (const Cantor::DefaultVariableModel::Variable& var : m_variables) - names< */ #ifndef _MAXIMAVARIABLEMODEL_H #define _MAXIMAVARIABLEMODEL_H #include "defaultvariablemodel.h" #include class MaximaSession; class MaximaVariableModel : public Cantor::DefaultVariableModel { Q_OBJECT public: static const QString inspectCommand; static const QString variableInspectCommand; explicit MaximaVariableModel( MaximaSession* session); ~MaximaVariableModel() override = default; void clear(); - QList variables(); QList functions(); - QStringList variableNames(); QStringList functionNames(bool stripParameters=false); bool isUpdateCommand(const QString &cmd) const; public Q_SLOTS: void update(); private Q_SLOTS: void parseNewVariables(Cantor::Expression::Status status); void parseNewFunctions(Cantor::Expression::Status status); Q_SIGNALS: - void variablesAdded(const QStringList variables); - void variablesRemoved(const QStringList variables); - void functionsAdded(const QStringList variables); void functionsRemoved(const QStringList variables); private: MaximaSession* maximaSession(); private: - QList m_variables; QList m_functions; Cantor::Expression* m_variableExpression; Cantor::Expression* m_functionExpression; }; #endif /* _MAXIMAVARIABLEMODEL_H */ diff --git a/src/lib/defaultvariablemodel.cpp b/src/lib/defaultvariablemodel.cpp index 29c55bfe..c8844716 100644 --- a/src/lib/defaultvariablemodel.cpp +++ b/src/lib/defaultvariablemodel.cpp @@ -1,193 +1,274 @@ /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2010 Miha Čančula */ #include "defaultvariablemodel.h" #include #include #include "extension.h" #include "backend.h" namespace Cantor { class DefaultVariableModelPrivate { public: QList variables; Session* session; VariableManagementExtension* extension; }; DefaultVariableModel::DefaultVariableModel(Session* session): QAbstractTableModel(session), d_ptr(new DefaultVariableModelPrivate) { Q_D(DefaultVariableModel); d->session = session; if (session) { d->extension = dynamic_cast(session->backend()->extension(QLatin1String("VariableManagementExtension"))); } qDebug() << d->session << d->extension; } int DefaultVariableModel::columnCount(const QModelIndex& parent) const { Q_UNUSED(parent); return ColumnCount; } int DefaultVariableModel::rowCount(const QModelIndex& parent) const { if (parent.isValid()) { return 0; } else { Q_D(const DefaultVariableModel); return d->variables.size(); } } QVariant DefaultVariableModel::headerData(int section, Qt::Orientation orientation, int role) const { if(role==Qt::DisplayRole && orientation==Qt::Horizontal) { switch(section) { case NameColumn: return i18nc("@title:column", "Name"); case ValueColumn: return i18nc("@title:column", "Value"); break; } } return QVariant(); } Qt::ItemFlags DefaultVariableModel::flags(const QModelIndex& index) const { return QAbstractItemModel::flags(index) | Qt::ItemIsEditable; } QVariant DefaultVariableModel::data(const QModelIndex& index, int role) const { if (role != Qt::DisplayRole || !index.isValid()) { return QVariant(); } Q_D(const DefaultVariableModel); switch (index.column()) { case NameColumn: return QVariant(d->variables[index.row()].name); case ValueColumn: return QVariant(d->variables[index.row()].value); default: return QVariant(); } } bool DefaultVariableModel::setData(const QModelIndex& index, const QVariant& value, int role) { if(role!=Qt::EditRole || !value.isValid() || !index.isValid()) { return false; } Q_D(const DefaultVariableModel); if(index.column() == ValueColumn) { // Changing values QString name = data(index.sibling(index.row(), NameColumn)).toString(); d->session->evaluateExpression(d->extension->setValue(name, value.toString()), Expression::DeleteOnFinish); return true; } else if(index.column() == NameColumn) { // Renaming => copy it, then delete the old one QString oldName = data(index).toString(); QString variableValue = data(index.sibling(index.row(), ValueColumn)).toString(); d->session->evaluateExpression(d->extension->addVariable(value.toString(), variableValue), Expression::DeleteOnFinish); d->session->evaluateExpression(d->extension->removeVariable(oldName), Expression::DeleteOnFinish); return true; } return false; } void DefaultVariableModel::addVariable(const QString& name, const QString& value) { Variable v; v.name = name; v.value = value; addVariable(v); } void DefaultVariableModel::addVariable(const Cantor::DefaultVariableModel::Variable& variable) { Q_D(DefaultVariableModel); if ( d->variables.contains(variable) ) { + // TODO: Why we remove variable here? removeVariable(variable); } beginInsertRows(QModelIndex(), d->variables.size(), d->variables.size()); d->variables.append(variable); + emit variablesAdded(QStringList(variable.name)); endInsertRows(); } void DefaultVariableModel::removeVariable(const QString& name) { Variable v; v.name = name; removeVariable(v); } void DefaultVariableModel::removeVariable(const Cantor::DefaultVariableModel::Variable& variable) { Q_D(DefaultVariableModel); int row = d->variables.indexOf(variable); if(row==-1) return; + const QString& name = variable.name; beginRemoveRows(QModelIndex(), row, row); d->variables.removeAt(row); endRemoveRows(); + emit variablesRemoved(QStringList(name)); } void DefaultVariableModel::clearVariables() { Q_D(DefaultVariableModel); beginResetModel(); + + QStringList names; + for (const Variable var: d->variables) + names.append(var.name); + d->variables.clear(); endResetModel(); + + emit variablesRemoved(names); +} + +void DefaultVariableModel::setVariables(const QList& newVars) +{ + Q_D(DefaultVariableModel); + + QStringList addedVars; + QStringList removedVars; + + // Handle deleted vars + for (const Variable var : variables()) + { + bool found = false; + for (const Variable& newvar : newVars) + if(var.name == newvar.name) + { + found=true; + break; + } + + if (!found) + removedVars << var.name; + } + + // Handle added vars + const int size = d->variables.size(); + for (const Variable newvar : newVars) + { + bool found = false; + for (int i = 0; i < size; i++) + if(d->variables[i].name == newvar.name) + { + found=true; + break; + } + + if (!found) + { + addedVars << newvar.name; + beginInsertRows(QModelIndex(), d->variables.size(), d->variables.size()); + d->variables.append(newvar); + endInsertRows(); + } + } + + for (const QString& var: removedVars) + { + int row = d->variables.indexOf(Variable{var,QString()}); + beginRemoveRows(QModelIndex(), row, row); + d->variables.removeAt(row); + endRemoveRows(); + } + + emit variablesAdded(addedVars); + emit variablesRemoved(removedVars); } Session* DefaultVariableModel::session() const { Q_D(const DefaultVariableModel); return d->session; } +QList DefaultVariableModel::variables() const +{ + Q_D(const DefaultVariableModel); + return d->variables; +} + +QStringList DefaultVariableModel::variableNames() const +{ + Q_D(const DefaultVariableModel); + QStringList names; + for (const Variable var: d->variables) + names << var.name; + return names; +} + bool operator==(const Cantor::DefaultVariableModel::Variable& one, const Cantor::DefaultVariableModel::Variable& other) { return one.name == other.name; } } diff --git a/src/lib/defaultvariablemodel.h b/src/lib/defaultvariablemodel.h index cdbae980..d598f03d 100644 --- a/src/lib/defaultvariablemodel.h +++ b/src/lib/defaultvariablemodel.h @@ -1,143 +1,167 @@ /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2010 Miha Čančula */ #ifndef CANTOR_DEFAULTVARIABLEMODEL_H #define CANTOR_DEFAULTVARIABLEMODEL_H #include #include "session.h" #include "expression.h" namespace Cantor { class DefaultVariableModelPrivate; /** * @brief * This DefaultVariableModel class is an implementation of QAbstractItemModel * that can be used with the Variable Manager plugin. * * For most uses the addVariable(), removeVariable() and clearVariables() methods are sufficient. * They can be used from session (directly or by connecting signals to them), or called from * a subclass. * * DefaultVariableModel uses the session to run expressions for changing variables, and it * gets the commands from the backend's VariableManagementExtension. * If you do not want this behavior, you can subclass it and reimplement data() and/or setData(). * * @see Session::variableModel() */ class CANTOR_EXPORT DefaultVariableModel : public QAbstractTableModel { Q_OBJECT Q_PROPERTY(Session* session READ session) public: /** * A structure representing a variable. */ struct Variable { /** * The variable's name */ QString name; /** * The variable's value, represented as a string */ QString value; }; /** * Default constructor * If you are constructing a DefaultVariableModel without subclassing, the @p session must be valid * and its backends must support a VariableManagementExtension. * * This requirement can be avoided by reimplementing setData() in a subclass. * * @param session the session this Model belongs to, also becomes the Model's parent. */ explicit DefaultVariableModel(Session* session); ~DefaultVariableModel() override = default; /** * Get the session which created this Model and whose variables it contains * @return the session */ Session* session() const; + /** + * Returns variables, stored in this model, as @see Variable. + */ + QList variables() const; + + /** + * Returns names of stored variables + */ + QStringList variableNames() const; + public Q_SLOTS: /** * Adds a variable to the model. * If a variable with the same name already exists, it will be overwritten. * @param name the name of the variable * @param value the value of the variable */ void addVariable(const QString& name, const QString& value); /** * Convenience method, equivalent to addVariable(variable.name, variable.value) * @param variable the variable to add */ void addVariable(const Cantor::DefaultVariableModel::Variable& variable); /** * Remove the variable @p name from the model. * If a variable with the specified @p name doesn't exists, this method does nothing. * @param name the name of the variable to remove */ void removeVariable(const QString& name); /** * Convenience method, equivalent to removeVariable(variable.name) * @param variable the variable to remove */ void removeVariable(const Cantor::DefaultVariableModel::Variable& variable); /** * Clears all variables from the model */ void clearVariables(); +Q_SIGNALS: + /** + * Emitted after adding new variables + * @param variables list of new variables + */ + void variablesAdded(const QStringList& variables); + + /** + * Emitted after variables removing + * @param variables list of removed variables + */ + void variablesRemoved(const QStringList& variables); protected: QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; int columnCount(const QModelIndex& parent = QModelIndex()) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex& index) const override; + void setVariables(const QList& newVars); + enum Column { NameColumn = 0, ValueColumn = 1, ColumnCount = 2 }; private: DefaultVariableModelPrivate* const d_ptr; Q_DECLARE_PRIVATE(DefaultVariableModel) }; bool operator==(const Cantor::DefaultVariableModel::Variable& one, const Cantor::DefaultVariableModel::Variable& other); } #endif // CANTOR_DEFAULTVARIABLEMODEL_H