diff --git a/src/Mod/Spreadsheet/App/Cell.cpp b/src/Mod/Spreadsheet/App/Cell.cpp index 4cc3f7c6318e..7101d6037789 100644 --- a/src/Mod/Spreadsheet/App/Cell.cpp +++ b/src/Mod/Spreadsheet/App/Cell.cpp @@ -102,7 +102,6 @@ Cell::Cell(const CellAddress &_address, PropertySheet *_owner) : address(_address) , owner(_owner) , used(0) - , expression(0) , alignment(ALIGNMENT_HIMPLIED | ALIGNMENT_LEFT | ALIGNMENT_VIMPLIED | ALIGNMENT_VCENTER) , style() , foregroundColor(0, 0, 0, 1) @@ -142,7 +141,7 @@ Cell &Cell::operator =(const Cell &rhs) address = rhs.address; - setExpression(rhs.expression ? rhs.expression->copy() : 0); + setExpression(App::ExpressionPtr(rhs.expression ? rhs.expression->copy() : nullptr)); setAlignment(rhs.alignment); setStyle(rhs.style); setBackground(rhs.backgroundColor); @@ -166,8 +165,6 @@ Cell &Cell::operator =(const Cell &rhs) Cell::~Cell() { - if (expression) - delete expression; } /** @@ -175,7 +172,7 @@ Cell::~Cell() * */ -void Cell::setExpression(App::Expression *expr) +void Cell::setExpression(App::ExpressionPtr &&expr) { PropertySheet::AtomicPropertyChange signaller(*owner); @@ -204,9 +201,7 @@ void Cell::setExpression(App::Expression *expr) expr->comment.clear(); } - if (expression) - delete expression; - expression = expr; + expression = std::move(expr); setUsed(EXPRESSION_SET, !!expression); /* Update dependencies */ @@ -247,8 +242,8 @@ const App::Expression *Cell::getExpression(bool withFormat) const bool Cell::getStringContent(std::string & s, bool persistent) const { if (expression) { - if (freecad_dynamic_cast(expression)) { - s = static_cast(expression)->getText(); + if (freecad_dynamic_cast(expression.get())) { + s = static_cast(expression.get())->getText(); char * end; errno = 0; double d = strtod(s.c_str(), &end); @@ -256,12 +251,12 @@ bool Cell::getStringContent(std::string & s, bool persistent) const if (!*end && errno == 0) s = "'" + s; } - else if (freecad_dynamic_cast(expression)) + else if (freecad_dynamic_cast(expression.get())) s = "=" + expression->toString(); - else if (freecad_dynamic_cast(expression)) + else if (freecad_dynamic_cast(expression.get())) s = expression->toString(); else - s = "=" + expression->toString(); + s = "=" + expression->toString(persistent); return true; } @@ -272,7 +267,7 @@ bool Cell::getStringContent(std::string & s, bool persistent) const } void Cell::afterRestore() { - auto expr = freecad_dynamic_cast(expression); + auto expr = freecad_dynamic_cast(expression.get()); if(expr) setContent(expr->getText().c_str()); } @@ -285,7 +280,7 @@ void Cell::setContent(const char * value) clearException(); if (value != 0) { if(owner->sheet()->isRestoring()) { - expression = new App::StringExpression(owner->sheet(),value); + expression.reset(new App::StringExpression(owner->sheet(),value)); setUsed(EXPRESSION_SET, true); return; } @@ -320,7 +315,7 @@ void Cell::setContent(const char * value) } try { - setExpression(expr); + setExpression(App::ExpressionPtr(expr)); signaller.tryInvoke(); } catch (Base::Exception &e) { if(value) { @@ -330,7 +325,7 @@ void Cell::setContent(const char * value) _value += value; value = _value.c_str(); } - setExpression(new App::StringExpression(owner->sheet(), value)); + setExpression(App::ExpressionPtr(new App::StringExpression(owner->sheet(), value))); setParseException(e.what()); } } @@ -770,9 +765,6 @@ void Cell::save(std::ostream &os, const char *indent, bool noContent) const { os << "colSpan=\"" << colSpan << "\" "; } - if(editMode) - os << "editMode=\"" << editMode << "\" "; - os << "/>"; if(!noContent) os << std::endl; diff --git a/src/Mod/Spreadsheet/App/Cell.h b/src/Mod/Spreadsheet/App/Cell.h index b7f088a48bf1..0a5dee810926 100644 --- a/src/Mod/Spreadsheet/App/Cell.h +++ b/src/Mod/Spreadsheet/App/Cell.h @@ -37,11 +37,6 @@ class XMLReader; class Writer; } -namespace App { -class Expression; -class ExpressionVisitor; -} - namespace Spreadsheet { class PropertySheet; @@ -152,9 +147,7 @@ class SpreadsheetExport Cell { void setParseException(const std::string & e); - //void setExpression(const Expression * expr); - - void setExpression(App::Expression *expr); + void setExpression(App::ExpressionPtr &&expr); void setUsed(int mask, bool state = true); @@ -184,7 +177,7 @@ class SpreadsheetExport Cell { PropertySheet * owner; int used; - App::Expression * expression; + mutable App::ExpressionPtr expression; int alignment; std::set style; App::Color foregroundColor; diff --git a/src/Mod/Spreadsheet/App/PropertyColumnWidths.cpp b/src/Mod/Spreadsheet/App/PropertyColumnWidths.cpp index 71264498d334..f9335dbd763d 100644 --- a/src/Mod/Spreadsheet/App/PropertyColumnWidths.cpp +++ b/src/Mod/Spreadsheet/App/PropertyColumnWidths.cpp @@ -63,8 +63,11 @@ App::Property *PropertyColumnWidths::Copy() const void PropertyColumnWidths::Paste(const App::Property &from) { + setValues(static_cast(from).getValues()); +} + +void PropertyColumnWidths::setValues(const std::map &values) { aboutToSetValue(); - const PropertyColumnWidths * frompcw = static_cast(&from); std::map::const_iterator i; @@ -79,8 +82,8 @@ void PropertyColumnWidths::Paste(const App::Property &from) clear(); /* Copy new map from from */ - i = frompcw->begin(); - while (i != frompcw->end()) { + i = values.begin(); + while (i != values.end()) { insert(*i); dirty.insert(i->first); ++i; diff --git a/src/Mod/Spreadsheet/App/PropertyColumnWidths.h b/src/Mod/Spreadsheet/App/PropertyColumnWidths.h index d7a282c371aa..9b0954d3ace6 100644 --- a/src/Mod/Spreadsheet/App/PropertyColumnWidths.h +++ b/src/Mod/Spreadsheet/App/PropertyColumnWidths.h @@ -39,6 +39,8 @@ class SpreadsheetExport PropertyColumnWidths : public App::Property, std::map &); + std::map getValues() const { return *this; } diff --git a/src/Mod/Spreadsheet/App/PropertyRowHeights.cpp b/src/Mod/Spreadsheet/App/PropertyRowHeights.cpp index 61692edd9b75..cbe985a6e84f 100644 --- a/src/Mod/Spreadsheet/App/PropertyRowHeights.cpp +++ b/src/Mod/Spreadsheet/App/PropertyRowHeights.cpp @@ -34,7 +34,7 @@ using namespace Spreadsheet; -const int PropertyRowHeights::defaultHeight = 20; +const int PropertyRowHeights::defaultHeight = 30; TYPESYSTEM_SOURCE(Spreadsheet::PropertyRowHeights , App::Property); @@ -56,8 +56,11 @@ App::Property *PropertyRowHeights::Copy() const void PropertyRowHeights::Paste(const Property &from) { + setValues(static_cast(from).getValues()); +} + +void PropertyRowHeights::setValues(const std::map &values) { aboutToSetValue(); - const PropertyRowHeights * fromprh = static_cast(&from); std::map::const_iterator i; @@ -72,8 +75,8 @@ void PropertyRowHeights::Paste(const Property &from) clear(); /* Copy new map from from */ - i = fromprh->begin(); - while (i != fromprh->end()) { + i = values.begin(); + while (i != values.end()) { insert(*i); dirty.insert(i->first); ++i; diff --git a/src/Mod/Spreadsheet/App/PropertyRowHeights.h b/src/Mod/Spreadsheet/App/PropertyRowHeights.h index b44d5d2fce42..c131859650f7 100644 --- a/src/Mod/Spreadsheet/App/PropertyRowHeights.h +++ b/src/Mod/Spreadsheet/App/PropertyRowHeights.h @@ -39,6 +39,8 @@ class SpreadsheetExport PropertyRowHeights : public App::Property, std::map &); + int getValue(int row) const { std::map::const_iterator i = find(row); return i != end() ? i->second : defaultHeight; diff --git a/src/Mod/Spreadsheet/App/Sheet.cpp b/src/Mod/Spreadsheet/App/Sheet.cpp index d7deb552c5dc..d99119f80041 100644 --- a/src/Mod/Spreadsheet/App/Sheet.cpp +++ b/src/Mod/Spreadsheet/App/Sheet.cpp @@ -254,6 +254,8 @@ bool Sheet::exportToFile(const std::string &filename, char delimiter, char quote field << static_cast(prop)->getValue(); else if (prop->isDerivedFrom((PropertyFloat::getClassTypeId()))) field << static_cast(prop)->getValue(); + else if (prop->isDerivedFrom((PropertyInteger::getClassTypeId()))) + field << static_cast(prop)->getValue(); else if (prop->isDerivedFrom((PropertyString::getClassTypeId()))) field << static_cast(prop)->getValue(); else @@ -498,6 +500,30 @@ Property * Sheet::setFloatProperty(CellAddress key, double value) return floatProp; } +Property * Sheet::setIntegerProperty(CellAddress key, long value) +{ + Property * prop = props.getDynamicPropertyByName(key.toString().c_str()); + PropertyInteger * intProp; + + if (!prop || prop->getTypeId() != PropertyInteger::getClassTypeId()) { + if (prop) { + this->removeDynamicProperty(key.toString().c_str()); + propAddress.erase(prop); + } + intProp = freecad_dynamic_cast(addDynamicProperty( + "App::PropertyInteger", key.toString().c_str(), 0, 0, + Prop_ReadOnly | Prop_Hidden | Prop_NoPersist)); + } + else + intProp = static_cast(prop); + + propAddress[intProp] = key; + intProp->setValue(value); + + return intProp; +} + + /** * Set the property for cell \p key to a PropertyQuantity with \a value and \a unit. * If the Property exists, but of wrong type, the previous Property is destroyed and recreated as the correct type. @@ -561,6 +587,25 @@ Property * Sheet::setStringProperty(CellAddress key, const std::string & value) return stringProp; } +Property * Sheet::setObjectProperty(CellAddress key, Py::Object object) +{ + Property * prop = props.getDynamicPropertyByName(key.toString().c_str()); + PropertyPythonObject * pyProp = freecad_dynamic_cast(prop); + + if (!pyProp) { + if (prop) { + this->removeDynamicProperty(key.toString().c_str()); + propAddress.erase(prop); + } + pyProp = freecad_dynamic_cast(addDynamicProperty("App::PropertyPythonObject", key.toString().c_str(), 0, 0, Prop_ReadOnly | Prop_Hidden | Prop_NoPersist)); + } + + propAddress[pyProp] = key; + pyProp->setValue(object); + + return pyProp; +} + /** * @brief Update the alias for the cell at \a key. * @param key Cell to update. @@ -630,27 +675,41 @@ void Sheet::updateProperty(CellAddress key) if (input) { CurrentAddressLock lock(currentRow,currentCol,key); - output = cells.eval(input); + output.reset(input->eval()); } else { std::string s; if (cell->getStringContent(s)) - output = new StringExpression(this, s); + output.reset(new StringExpression(this, s)); else - output = new StringExpression(this, ""); + output.reset(new StringExpression(this, "")); } - /* Eval returns either NumberExpression or StringExpression objects */ - if (freecad_dynamic_cast(output)) { - NumberExpression * number = static_cast(output); - if (number->getUnit().isEmpty()) - setFloatProperty(key, number->getValue()); - else + /* Eval returns either NumberExpression or StringExpression, or + * PyObjectExpression objects */ + auto number = freecad_dynamic_cast(output.get()); + if(number) { + long l; + if (!number->getUnit().isEmpty()) setQuantityProperty(key, number->getValue(), number->getUnit()); + else if(number->isInteger(&l)) + setIntegerProperty(key,l); + else + setFloatProperty(key, number->getValue()); + }else{ + auto str_expr = freecad_dynamic_cast(output.get()); + if(str_expr) + setStringProperty(key, str_expr->getText().c_str()); + else { + Base::PyGILStateLocker lock; + auto py_expr = freecad_dynamic_cast(output.get()); + if(py_expr) + setObjectProperty(key, py_expr->getPyObject()); + else + setObjectProperty(key, Py::Object()); + } } - else - setStringProperty(key, freecad_dynamic_cast(output)->getText().c_str()); } else clear(key); @@ -1024,24 +1083,6 @@ std::vector Sheet::getUsedCells() const void Sheet::updateColumnsOrRows(bool horizontal, int section, int count) { - auto &hiddenProp = horizontal?hiddenColumns:hiddenRows; - const auto &hidden = hiddenProp.getValues(); - auto it = hidden.lower_bound(section); - if(it!=hidden.end()) { - std::set newHidden(hidden.begin(),it); - if(count>0) { - for(;it!=hidden.end();++it) - newHidden.insert(*it + count); - } else { - it = hidden.lower_bound(section-count); - if(it!=hidden.end()) { - for(;it!=hidden.end();++it) - newHidden.insert(*it+count); - } - } - hiddenProp.setValues(newHidden); - } - const auto &sizes = horizontal?columnWidths.getValues():rowHeights.getValues(); auto iter = sizes.lower_bound(section); if(iter!=sizes.end()) { @@ -1366,6 +1407,16 @@ void Sheet::renameObjectIdentifiers(const std::map &ranges) const { + for(auto range : ranges) { + do { + if(cells.getValue(*range)) + return true; + }while(range.next()); + } + return false; +} + std::string Sheet::getRow(int offset) const { if(currentRow < 0) throw Base::RuntimeError("No current row"); @@ -1396,14 +1447,6 @@ std::string Sheet::getColumn(int offset) const { return txt; } -void Sheet::onChanged(const App::Property *prop) { - if(!isRestoring() && getDocument() && !getDocument()->isPerformingTransaction()) { - if(prop == &PythonMode) - cells.setDirty(); - } - App::DocumentObject::onChanged(prop); -} - /////////////////////////////////////////////////////////////////////////////// TYPESYSTEM_SOURCE(Spreadsheet::PropertySpreadsheetQuantity, App::PropertyQuantity); diff --git a/src/Mod/Spreadsheet/App/Sheet.h b/src/Mod/Spreadsheet/App/Sheet.h index bb8de29dbd36..3bb91271b94b 100644 --- a/src/Mod/Spreadsheet/App/Sheet.h +++ b/src/Mod/Spreadsheet/App/Sheet.h @@ -152,6 +152,7 @@ class SpreadsheetExport Sheet : public App::DocumentObject void providesTo(App::CellAddress address, std::set & result) const; + bool hasCell(const std::vector &ranges) const; PyObject *getPyObject(); PropertySheet *getCells() { return &cells; } @@ -190,8 +191,6 @@ class SpreadsheetExport Sheet : public App::DocumentObject protected: - virtual void onChanged(const App::Property *prop); - void updateColumnsOrRows(bool horizontal, int section, int count) ; std::set providesTo(App::CellAddress address) const; @@ -210,8 +209,12 @@ class SpreadsheetExport Sheet : public App::DocumentObject App::Property *setStringProperty(App::CellAddress key, const std::string & value) ; + App::Property *setObjectProperty(App::CellAddress key, Py::Object obj) ; + App::Property *setFloatProperty(App::CellAddress key, double value); + App::Property *setIntegerProperty(App::CellAddress key, long value); + App::Property *setQuantityProperty(App::CellAddress key, double value, const Base::Unit &unit); void aliasRemoved(App::CellAddress address, const std::string &alias); diff --git a/src/Mod/Spreadsheet/App/SheetObserver.cpp b/src/Mod/Spreadsheet/App/SheetObserver.cpp index a9c901dd070e..1108fdc017a1 100644 --- a/src/Mod/Spreadsheet/App/SheetObserver.cpp +++ b/src/Mod/Spreadsheet/App/SheetObserver.cpp @@ -82,7 +82,7 @@ void SheetObserver::slotChangedObject(const DocumentObject &Obj, const Property return; isUpdating.insert(name); - sheet->recomputeDependants(&Prop); + sheet->recomputeDependants(&Obj,Prop.getName()); isUpdating.erase(name); } } diff --git a/src/Mod/Spreadsheet/App/SheetObserver.h b/src/Mod/Spreadsheet/App/SheetObserver.h index c3a9dfcd74b3..b5705845820e 100644 --- a/src/Mod/Spreadsheet/App/SheetObserver.h +++ b/src/Mod/Spreadsheet/App/SheetObserver.h @@ -29,6 +29,7 @@ namespace Spreadsheet { class PropertySheet; +// SheetObserver is obselete as PropertySheet is now derived from PropertyLinkBase class SheetObserver : public App::DocumentObserver { public: SheetObserver(App::Document* document, PropertySheet *_sheet); diff --git a/src/Mod/Spreadsheet/Gui/PropertiesDialog.cpp b/src/Mod/Spreadsheet/Gui/PropertiesDialog.cpp index 0251d5df0122..90060043041c 100644 --- a/src/Mod/Spreadsheet/Gui/PropertiesDialog.cpp +++ b/src/Mod/Spreadsheet/Gui/PropertiesDialog.cpp @@ -25,6 +25,7 @@ #include "PropertiesDialog.h" #include #include +#include #include #include "ui_PropertiesDialog.h" @@ -229,17 +230,17 @@ void PropertiesDialog::apply() for (; i != ranges.end(); ++i) { if (orgAlignment != alignment) { - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.setAlignment('%s', '%s')", sheet->getNameInDocument(), + FCMD_OBJ_CMD2("setAlignment('%s', '%s')", sheet, i->rangeString().c_str(), Cell::encodeAlignment(alignment).c_str()); changes = true; } if (orgStyle != style) { - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.setStyle('%s', '%s')", sheet->getNameInDocument(), + FCMD_OBJ_CMD2("setStyle('%s', '%s')", sheet, i->rangeString().c_str(), Cell::encodeStyle(style).c_str()); changes = true; } if (orgForegroundColor != foregroundColor) { - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.setForeground('%s', (%f,%f,%f,%f))", sheet->getNameInDocument(), + FCMD_OBJ_CMD2("setForeground('%s', (%f,%f,%f,%f))", sheet, i->rangeString().c_str(), foregroundColor.r, foregroundColor.g, @@ -248,7 +249,7 @@ void PropertiesDialog::apply() changes = true; } if (orgBackgroundColor != backgroundColor) { - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.setBackground('%s', (%f,%f,%f,%f))", sheet->getNameInDocument(), + FCMD_OBJ_CMD2("setBackground('%s', (%f,%f,%f,%f))", sheet, i->rangeString().c_str(), backgroundColor.r, backgroundColor.g, @@ -258,12 +259,12 @@ void PropertiesDialog::apply() } if (orgDisplayUnit != displayUnit) { std::string escapedstr = Base::Tools::escapedUnicodeFromUtf8(displayUnit.stringRep.c_str()); - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.setDisplayUnit('%s', '%s')", sheet->getNameInDocument(), + FCMD_OBJ_CMD2("setDisplayUnit('%s', '%s')", sheet, i->rangeString().c_str(), escapedstr.c_str()); changes = true; } if (ranges.size() == 1 && ranges[0].size() == 1 && orgAlias != alias) { - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.setAlias('%s', '%s')", sheet->getNameInDocument(), + FCMD_OBJ_CMD2("setAlias('%s', '%s')", sheet, i->address().c_str(), alias.c_str()); changes = true; } diff --git a/src/Mod/Spreadsheet/Gui/SheetModel.cpp b/src/Mod/Spreadsheet/Gui/SheetModel.cpp index 222d69475e45..2023556f529d 100644 --- a/src/Mod/Spreadsheet/Gui/SheetModel.cpp +++ b/src/Mod/Spreadsheet/Gui/SheetModel.cpp @@ -27,8 +27,10 @@ # include # include # include +# include #endif +#include #include #include "SheetModel.h" #include @@ -177,7 +179,7 @@ QVariant SheetModel::data(const QModelIndex &index, int role) const return QVariant(v); } #else - if (role == Qt::ToolTipRole) { + if (!cell->hasException() && role == Qt::ToolTipRole) { std::string alias; if (cell->getAlias(alias)) return QVariant(Base::Tools::fromStdString(alias)); @@ -186,14 +188,26 @@ QVariant SheetModel::data(const QModelIndex &index, int role) const if (cell->hasException()) { switch (role) { - case Qt::ToolTipRole: - return QVariant::fromValue(Base::Tools::fromStdString(cell->getException())); - case Qt::DisplayRole: + case Qt::ToolTipRole: { +#if QT_VERSION >= 0x050000 + QString txt(Base::Tools::fromStdString(cell->getException()).toHtmlEscaped()); +#else + QString txt(Qt::escape(Base::Tools::fromStdString(cell->getException()))); +#endif + return QVariant(QString::fromLatin1("
%1
").arg(txt)); + } + case Qt::DisplayRole: { #ifdef DEBUG_DEPS return QVariant::fromValue(QString::fromUtf8("#ERR: %1").arg(Tools::fromStdString(cell->getException()))); #else + std::string str; + if(cell->getStringContent(str)) + return QVariant::fromValue(QString::fromUtf8(str.c_str())); return QVariant::fromValue(QString::fromUtf8("#ERR")); #endif + } + case Qt::TextColorRole: + return QVariant::fromValue(QColor(255.0, 0, 0)); case Qt::TextAlignmentRole: return QVariant(Qt::AlignVCenter | Qt::AlignLeft); default: @@ -215,9 +229,6 @@ QVariant SheetModel::data(const QModelIndex &index, int role) const std::string address = CellAddress(row, col).toString(); Property * prop = sheet->getPropertyByName(address.c_str()); - if (prop == 0) - return QVariant(); - if (role == Qt::BackgroundRole) { Color color; @@ -267,7 +278,24 @@ QVariant SheetModel::data(const QModelIndex &index, int role) const return QVariant::fromValue(f); } - if (prop->isDerivedFrom(App::PropertyString::getClassTypeId())) { + if (!prop) { + switch (role) { + case Qt::TextColorRole: { + return QColor(0, 0, 255.0); + } + case Qt::TextAlignmentRole: { + qtAlignment = Qt::AlignHCenter | Qt::AlignVCenter; + return QVariant::fromValue(qtAlignment); + } + case Qt::DisplayRole: + if(cell->getExpression()) + return QVariant(QLatin1String("#PENDING")); + else + return QVariant(); + default: + return QVariant(); + } + } else if (prop->isDerivedFrom(App::PropertyString::getClassTypeId())) { /* String */ const App::PropertyString * stringProp = static_cast(prop); @@ -338,7 +366,7 @@ QVariant SheetModel::data(const QModelIndex &index, int role) const v = number + Base::Tools::fromStdString(" " + displayUnit.stringRep); } else { - v = QString::fromUtf8("ERR: unit"); + v = QString::fromUtf8("#ERR: unit"); } } else { @@ -356,9 +384,15 @@ QVariant SheetModel::data(const QModelIndex &index, int role) const return QVariant(); } } - else if (prop->isDerivedFrom(App::PropertyFloat::getClassTypeId())) { + else if (prop->isDerivedFrom(App::PropertyFloat::getClassTypeId()) + || prop->isDerivedFrom(App::PropertyInteger::getClassTypeId())) + { /* Number */ - const App::PropertyFloat * floatProp = static_cast(prop); + double d; + if(prop->isDerivedFrom(App::PropertyFloat::getClassTypeId())) + d = static_cast(prop)->getValue(); + else + d = static_cast(prop)->getValue(); switch (role) { case Qt::TextColorRole: { @@ -367,7 +401,7 @@ QVariant SheetModel::data(const QModelIndex &index, int role) const if (cell->getForeground(color)) return QVariant::fromValue(QColor(255.0 * color.r, 255.0 * color.g, 255.0 * color.b, 255.0 * color.a)); else { - if (floatProp->getValue() < 0) + if (d < 0) return QVariant::fromValue(QColor(negativeFgColor)); else return QVariant::fromValue(QColor(positiveFgColor)); @@ -390,13 +424,13 @@ QVariant SheetModel::data(const QModelIndex &index, int role) const // Display locale specific decimal separator (#0003875,#0003876) if (cell->getDisplayUnit(displayUnit)) { - QString number = QLocale::system().toString(floatProp->getValue() / displayUnit.scaler,'f',Base::UnitsApi::getDecimals()); - //QString number = QString::number(floatProp->getValue() / displayUnit.scaler); + QString number = QLocale::system().toString(d / displayUnit.scaler,'f',Base::UnitsApi::getDecimals()); + //QString number = QString::number(d / displayUnit.scaler); v = number + Base::Tools::fromStdString(" " + displayUnit.stringRep); } else { - v = QLocale::system().toString(floatProp->getValue(),'f',Base::UnitsApi::getDecimals()); - //v = QString::number(floatProp->getValue()); + v = QLocale::system().toString(d,'f',Base::UnitsApi::getDecimals()); + //v = QString::number(d); } return QVariant(v); } @@ -404,6 +438,50 @@ QVariant SheetModel::data(const QModelIndex &index, int role) const return QVariant(); } } + else if (prop->isDerivedFrom(App::PropertyPythonObject::getClassTypeId())) { + auto pyProp = static_cast(prop); + + switch (role) { + case Qt::TextColorRole: { + Color color; + + if (cell->getForeground(color)) + return QVariant::fromValue(QColor(255.0 * color.r, 255.0 * color.g, 255.0 * color.b, 255.0 * color.a)); + else + return QVariant(QColor(textFgColor)); + } + case Qt::TextAlignmentRole: { + if (alignment & Cell::ALIGNMENT_HIMPLIED) { + qtAlignment &= ~(Qt::AlignLeft | Qt::AlignHCenter | Qt::AlignRight); + qtAlignment |= Qt::AlignHCenter; + } + if (alignment & Cell::ALIGNMENT_VIMPLIED) { + qtAlignment &= ~(Qt::AlignTop | Qt::AlignVCenter | Qt::AlignBottom); + qtAlignment |= Qt::AlignVCenter; + } + return QVariant::fromValue(qtAlignment); + } + case Qt::DisplayRole: { + Base::PyGILStateLocker lock; + std::string value; + try { + value = pyProp->getValue().as_string(); + } catch (Py::Exception &) { + Base::PyException e; + value = "#ERR: "; + value += e.what(); + } catch (Base::Exception &e) { + value = "#ERR: "; + value += e.what(); + } catch (...) { + value = "#ERR: unknown exception"; + } + return QVariant(QString::fromUtf8(value.c_str())); + } + default: + return QVariant(); + } + } return QVariant(); } @@ -443,26 +521,27 @@ bool SheetModel::setData(const QModelIndex & index, const QVariant & value, int CellAddress address(index.row(), index.column()); try { - std::string strAddress = address.toString(); QString str = value.toString(); - std::string content; - Cell * cell = sheet->getCell(address); - - if (cell) - cell->getStringContent(content); - - if ( content != Base::Tools::toStdString(str)) { - str.replace(QString::fromUtf8("'"), QString::fromUtf8("\\'")); - Gui::Command::openCommand("Edit cell"); - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.set('%s', '%s')", sheet->getNameInDocument(), - strAddress.c_str(), str.toUtf8().constData()); - Gui::Command::commitCommand(); - Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.recompute()"); - } + Gui::Command::openCommand("Edit cell"); + // Because of possible complication of recursively escaped + // characters, let's take a shortcut and bypass the command + // interface for now. +#if 0 + std::string strAddress = address.toString(); + str.replace(QString::fromUtf8("\\"), QString::fromUtf8("\\\\")); + str.replace(QString::fromUtf8("'"), QString::fromUtf8("\\'")); + FCMD_OBJ_CMD(sheet,"set('" << strAddress << "','" << + str.toUtf8().constData() << "')"); +#else + sheet->setContent(address, str.toUtf8().constData()); +#endif + Gui::Command::commitCommand(); + Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.recompute()"); } catch (const Base::Exception& e) { - QMessageBox::critical(qApp->activeWindow(), QObject::tr("Cell contents"), QString::fromUtf8(e.what())); + e.ReportException(); Gui::Command::abortCommand(); + return false; } } return true; diff --git a/src/Mod/Spreadsheet/Gui/SheetTableView.cpp b/src/Mod/Spreadsheet/Gui/SheetTableView.cpp index 5a3913234fa0..44f66fa4f37e 100644 --- a/src/Mod/Spreadsheet/Gui/SheetTableView.cpp +++ b/src/Mod/Spreadsheet/Gui/SheetTableView.cpp @@ -26,11 +26,17 @@ # include # include # include +# include +# include #endif +#include +#include #include +#include #include #include "../App/Utils.h" +#include "../App/Cell.h" #include #include "SheetTableView.h" #include "LineEdit.h" @@ -147,7 +153,7 @@ void SheetTableView::insertRows() break; } - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.insertRows('%s', %d)", sheet->getNameInDocument(), + FCMD_OBJ_CMD2("insertRows('%s', %d)", sheet, rowName(prev).c_str(), count); } Gui::Command::commitCommand(); @@ -169,7 +175,7 @@ void SheetTableView::removeRows() /* Remove rows */ Gui::Command::openCommand("Remove rows"); for (std::vector::const_iterator it = sortedRows.begin(); it != sortedRows.end(); ++it) { - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.removeRows('%s', %d)", sheet->getNameInDocument(), + FCMD_OBJ_CMD2("removeRows('%s', %d)", sheet, rowName(*it).c_str(), 1); } Gui::Command::commitCommand(); @@ -207,7 +213,7 @@ void SheetTableView::insertColumns() break; } - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.insertColumns('%s', %d)", sheet->getNameInDocument(), + FCMD_OBJ_CMD2("insertColumns('%s', %d)", sheet, columnName(prev).c_str(), count); } Gui::Command::commitCommand(); @@ -229,7 +235,7 @@ void SheetTableView::removeColumns() /* Remove columns */ Gui::Command::openCommand("Remove rows"); for (std::vector::const_iterator it = sortedColumns.begin(); it != sortedColumns.end(); ++it) - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.removeColumns('%s', %d)", sheet->getNameInDocument(), + FCMD_OBJ_CMD2("removeColumns('%s', %d)", sheet, columnName(*it).c_str(), 1); Gui::Command::commitCommand(); Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.recompute()"); @@ -405,6 +411,8 @@ void SheetTableView::deleteSelection() } } +static const QLatin1String _SheetMime("application/x-fc-spreadsheet"); + void SheetTableView::copySelection() { QModelIndexList selection = selectionModel()->selectedIndexes(); @@ -412,7 +420,6 @@ void SheetTableView::copySelection() int maxRow = 0; int minCol = INT_MAX; int maxCol = 0; - for (auto it : selection) { int row = it.row(); int col = it.column(); @@ -434,7 +441,13 @@ void SheetTableView::copySelection() if (i < maxRow) selectedText.append(QChar::fromLatin1('\n')); } - QApplication::clipboard()->setText(selectedText); + + Base::StringWriter writer; + sheet->getCells()->copyCells(writer,selectedRanges()); + QMimeData *mime = new QMimeData(); + mime->setText(selectedText); + mime->setData(_SheetMime,QByteArray(writer.getString().c_str())); + QApplication::clipboard()->setMimeData(mime); } void SheetTableView::cutSelection() @@ -445,20 +458,50 @@ void SheetTableView::cutSelection() void SheetTableView::pasteClipboard() { - QString text = QApplication::clipboard()->text(); - QStringList rows = text.split(QLatin1Char('\n')); + const QMimeData* mimeData = QApplication::clipboard()->mimeData(); + if(!mimeData || !mimeData->hasText()) + return; + + if(selectionModel()->selectedIndexes().size()>1) { + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Spreadsheet"), + QObject::tr("Spreadsheet does not support range selection when pasting.\n" + "Please select one cell only.")); + return; + } QModelIndex current = currentIndex(); - int i=0; - for (auto it : rows) { - QStringList cols = it.split(QLatin1Char('\t')); - int j=0; - for (auto jt : cols) { - QModelIndex index = model()->index(current.row()+i, current.column()+j); - model()->setData(index, jt); - j++; + + App::AutoTransaction committer("Paste cell"); + try { + if (!mimeData->hasFormat(_SheetMime)) { + QStringList cells; + QString text = mimeData->text(); + int i=0; + for (auto it : text.split(QLatin1Char('\n'))) { + QStringList cols = it.split(QLatin1Char('\t')); + int j=0; + for (auto jt : cols) { + QModelIndex index = model()->index(current.row()+i, current.column()+j); + model()->setData(index, jt); + j++; + } + i++; + } + }else{ + QByteArray res = mimeData->data(_SheetMime); + Base::ByteArrayIStreambuf buf(res); + std::istream in(0); + in.rdbuf(&buf); + Base::XMLReader reader("", in); + sheet->getCells()->pasteCells(reader,CellAddress(current.row(),current.column())); } - i++; + + GetApplication().getActiveDocument()->recompute(); + + }catch(Base::Exception &e) { + e.ReportException(); + QMessageBox::critical(Gui::getMainWindow(), QObject::tr("Copy & Paste failed"), + QString::fromLatin1(e.what())); } } diff --git a/src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp b/src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp index 0aedaac9a3db..d0c4b57dc9d3 100644 --- a/src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp +++ b/src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp @@ -148,6 +148,19 @@ bool SheetView::onMsg(const char *pMsg, const char **) getGuiDocument()->saveAs(); return true; } + else if(strcmp("Std_Delete",pMsg) == 0) { + std::vector ranges = selectedRanges(); + if (sheet->hasCell(ranges)) { + Gui::Command::openCommand("Clear cell(s)"); + std::vector::const_iterator i = ranges.begin(); + for (; i != ranges.end(); ++i) { + FCMD_OBJ_CMD(sheet, "clear('" << i->rangeString() << "')"); + } + Gui::Command::commitCommand(); + Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.recompute()"); + } + return true; + } else if (strcmp("Cut",pMsg) == 0) { ui->cells->cutSelection(); return true; @@ -233,16 +246,8 @@ void SheetView::columnResizeFinished() return; blockSignals(true); - Gui::Command::openCommand("Resize column"); - - QMap::const_iterator i = newColumnSizes.begin(); - while (i != newColumnSizes.end()) { - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.setColumnWidth('%s', %d)", sheet->getNameInDocument(), - columnName(i.key()).c_str(), i.value()); - ++i; - } - Gui::Command::commitCommand(); - Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.recompute()"); + for(auto &v : newColumnSizes) + sheet->setColumnWidth(v.first,v.second); blockSignals(false); newColumnSizes.clear(); } @@ -253,16 +258,8 @@ void SheetView::rowResizeFinished() return; blockSignals(true); - Gui::Command::openCommand("Resize row"); - - QMap::const_iterator i = newRowSizes.begin(); - while (i != newRowSizes.end()) { - Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.setRowHeight('%s', %d)", sheet->getNameInDocument(), - rowName(i.key()).c_str(), i.value()); - ++i; - } - Gui::Command::commitCommand(); - Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.recompute()"); + for(auto &v : newRowSizes) + sheet->setRowHeight(v.first,v.second); blockSignals(false); newRowSizes.clear(); } @@ -333,7 +330,8 @@ void SheetView::updateCell(const App::Property *prop) } CellAddress address; - sheet->getCellAddress(prop, address); + if(!sheet->getCellAddress(prop, address)) + return; if (currentIndex().row() == address.row() && currentIndex().column() == address.col() ) updateContentLine(); diff --git a/src/Mod/Spreadsheet/Gui/SpreadsheetView.h b/src/Mod/Spreadsheet/Gui/SpreadsheetView.h index ad7f9d1cbfc0..a900d80d7627 100644 --- a/src/Mod/Spreadsheet/Gui/SpreadsheetView.h +++ b/src/Mod/Spreadsheet/Gui/SpreadsheetView.h @@ -104,8 +104,8 @@ protected Q_SLOTS: boost::signals2::scoped_connection rowHeightChangedConnection; boost::signals2::scoped_connection positionChangedConnection; - QMap newColumnSizes; - QMap newRowSizes; + std::map newColumnSizes; + std::map newRowSizes; }; } // namespace SpreadsheetModGui diff --git a/src/Mod/Spreadsheet/Gui/ViewProviderSpreadsheet.cpp b/src/Mod/Spreadsheet/Gui/ViewProviderSpreadsheet.cpp index c8d8c0cffa57..8a704f313061 100644 --- a/src/Mod/Spreadsheet/Gui/ViewProviderSpreadsheet.cpp +++ b/src/Mod/Spreadsheet/Gui/ViewProviderSpreadsheet.cpp @@ -25,6 +25,7 @@ #include "PreCompiled.h" #ifndef _PreComp_ +# include # include # include # include @@ -43,6 +44,8 @@ #include #include #include +#include +#include #include #include #include @@ -143,32 +146,14 @@ Sheet *ViewProviderSheet::getSpreadsheetObject() const return freecad_dynamic_cast(pcObject); } -bool ViewProviderSheet::onDelete(const std::vector &) +void ViewProviderSheet::beforeDelete() { - // If view is closed, delete the object - if (view.isNull()) - return true; - - // View is not closed, delete cell contents instead if it is active - if (Gui::Application::Instance->activeDocument()) { - Gui::MDIView* activeWindow = Gui::getMainWindow()->activeWindow(); - SpreadsheetGui::SheetView * sheetView = freecad_dynamic_cast(activeWindow); - - if (sheetView) { - sheetView->deleteSelection(); - return false; - } - } - - // If the view is open but not active, try to close it. - // This may ask the user for permission in case it's the - // last view of the document. (#0003496) - QWidget* window = view; - QWidget* parent = view->parentWidget(); - if (qobject_cast(parent)) { - window = parent; - } - return window->close(); + ViewProviderDocumentObject::beforeDelete(); + if(!view) + return; + if(view==Gui::getMainWindow()->activeWindow()) + getDocument()->setActiveView(0,Gui::View3DInventor::getClassTypeId()); + Gui::getMainWindow()->removeWindow(view); } SheetView *ViewProviderSheet::showSpreadsheetView() @@ -184,6 +169,10 @@ SheetView *ViewProviderSheet::showSpreadsheetView() } return view; +} + +Gui::MDIView *ViewProviderSheet::getMDIView() { + return showSpreadsheetView(); } void ViewProviderSheet::updateData(const App::Property* prop) diff --git a/src/Mod/Spreadsheet/Gui/ViewProviderSpreadsheet.h b/src/Mod/Spreadsheet/Gui/ViewProviderSpreadsheet.h index 40e32b20a750..55d3d2f670b0 100644 --- a/src/Mod/Spreadsheet/Gui/ViewProviderSpreadsheet.h +++ b/src/Mod/Spreadsheet/Gui/ViewProviderSpreadsheet.h @@ -57,7 +57,7 @@ class SpreadsheetGuiExport ViewProviderSheet : public Gui::ViewProviderDocumentO Spreadsheet::Sheet* getSpreadsheetObject() const; - virtual bool onDelete(const std::vector &); + virtual void beforeDelete() override; QIcon getIcon() const; @@ -65,6 +65,8 @@ class SpreadsheetGuiExport ViewProviderSheet : public Gui::ViewProviderDocumentO virtual bool isShow(void) const { return true; } + virtual Gui::MDIView *getMDIView() override; + protected: SheetView* showSpreadsheetView(); void updateData(const App::Property *prop);