Spreadsheet changes

Various changes to support in-place editing, and more.
This commit is contained in:
Zheng, Lei
2019-07-12 13:25:50 +08:00
committed by wmayer
parent 32ad54c9ef
commit e911b69769
17 changed files with 334 additions and 180 deletions

View File

@@ -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<App::StringExpression>(expression)) {
s = static_cast<App::StringExpression*>(expression)->getText();
if (freecad_dynamic_cast<App::StringExpression>(expression.get())) {
s = static_cast<App::StringExpression*>(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<App::ConstantExpression>(expression))
else if (freecad_dynamic_cast<App::ConstantExpression>(expression.get()))
s = "=" + expression->toString();
else if (freecad_dynamic_cast<App::NumberExpression>(expression))
else if (freecad_dynamic_cast<App::NumberExpression>(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<StringExpression>(expression);
auto expr = freecad_dynamic_cast<StringExpression>(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;

View File

@@ -37,11 +37,6 @@ class XMLReader;
class Writer;
}
namespace App {
class Expression;
class ExpressionVisitor;
}
namespace Spreadsheet {
class PropertySheet;
@@ -152,9 +147,7 @@ private:
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 @@ private:
PropertySheet * owner;
int used;
App::Expression * expression;
mutable App::ExpressionPtr expression;
int alignment;
std::set<std::string> style;
App::Color foregroundColor;

View File

@@ -63,8 +63,11 @@ App::Property *PropertyColumnWidths::Copy() const
void PropertyColumnWidths::Paste(const App::Property &from)
{
setValues(static_cast<const PropertyColumnWidths&>(from).getValues());
}
void PropertyColumnWidths::setValues(const std::map<int,int> &values) {
aboutToSetValue();
const PropertyColumnWidths * frompcw = static_cast<const PropertyColumnWidths*>(&from);
std::map<int, int>::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;

View File

@@ -39,6 +39,8 @@ public:
void setValue(int col, int width);
void setValues(const std::map<int,int> &);
std::map<int, int> getValues() const {
return *this;
}

View File

@@ -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<const PropertyRowHeights&>(from).getValues());
}
void PropertyRowHeights::setValues(const std::map<int,int> &values) {
aboutToSetValue();
const PropertyRowHeights * fromprh = static_cast<const PropertyRowHeights*>(&from);
std::map<int, int>::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;

View File

@@ -39,6 +39,8 @@ public:
void setValue(int row, int height);
void setValues(const std::map<int,int> &);
int getValue(int row) const {
std::map<int, int>::const_iterator i = find(row);
return i != end() ? i->second : defaultHeight;

View File

@@ -254,6 +254,8 @@ bool Sheet::exportToFile(const std::string &filename, char delimiter, char quote
field << static_cast<PropertyQuantity*>(prop)->getValue();
else if (prop->isDerivedFrom((PropertyFloat::getClassTypeId())))
field << static_cast<PropertyFloat*>(prop)->getValue();
else if (prop->isDerivedFrom((PropertyInteger::getClassTypeId())))
field << static_cast<PropertyInteger*>(prop)->getValue();
else if (prop->isDerivedFrom((PropertyString::getClassTypeId())))
field << static_cast<PropertyString*>(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<PropertyInteger>(addDynamicProperty(
"App::PropertyInteger", key.toString().c_str(), 0, 0,
Prop_ReadOnly | Prop_Hidden | Prop_NoPersist));
}
else
intProp = static_cast<PropertyInteger*>(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<PropertyPythonObject>(prop);
if (!pyProp) {
if (prop) {
this->removeDynamicProperty(key.toString().c_str());
propAddress.erase(prop);
}
pyProp = freecad_dynamic_cast<PropertyPythonObject>(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<NumberExpression>(output)) {
NumberExpression * number = static_cast<NumberExpression*>(output);
if (number->getUnit().isEmpty())
setFloatProperty(key, number->getValue());
else
/* Eval returns either NumberExpression or StringExpression, or
* PyObjectExpression objects */
auto number = freecad_dynamic_cast<NumberExpression>(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<StringExpression>(output.get());
if(str_expr)
setStringProperty(key, str_expr->getText().c_str());
else {
Base::PyGILStateLocker lock;
auto py_expr = freecad_dynamic_cast<PyObjectExpression>(output.get());
if(py_expr)
setObjectProperty(key, py_expr->getPyObject());
else
setObjectProperty(key, Py::Object());
}
}
else
setStringProperty(key, freecad_dynamic_cast<StringExpression>(output)->getText().c_str());
}
else
clear(key);
@@ -1024,24 +1083,6 @@ std::vector<std::string> 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<long> 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<ObjectIdentifier, ObjectIdent
cells.renameObjectIdentifiers(paths);
}
bool Sheet::hasCell(const std::vector<App::Range> &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);

View File

@@ -152,6 +152,7 @@ public:
void providesTo(App::CellAddress address, std::set<std::string> & result) const;
bool hasCell(const std::vector<App::Range> &ranges) const;
PyObject *getPyObject();
PropertySheet *getCells() { return &cells; }
@@ -190,8 +191,6 @@ public:
protected:
virtual void onChanged(const App::Property *prop);
void updateColumnsOrRows(bool horizontal, int section, int count) ;
std::set<App::CellAddress> providesTo(App::CellAddress address) const;
@@ -210,8 +209,12 @@ protected:
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);

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -25,6 +25,7 @@
#include "PropertiesDialog.h"
#include <Base/Tools.h>
#include <App/Range.h>
#include <App/Document.h>
#include <Gui/Command.h>
#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;
}

View File

@@ -27,8 +27,10 @@
# include <QApplication>
# include <QLocale>
# include <QMessageBox>
# include <QTextDocument>
#endif
#include <App/Document.h>
#include <Gui/Application.h>
#include "SheetModel.h"
#include <Mod/Spreadsheet/App/Utils.h>
@@ -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("<pre>%1</pre>").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<const App::PropertyString*>(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<const App::PropertyFloat*>(prop);
double d;
if(prop->isDerivedFrom(App::PropertyFloat::getClassTypeId()))
d = static_cast<const App::PropertyFloat*>(prop)->getValue();
else
d = static_cast<const App::PropertyInteger*>(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<const App::PropertyPythonObject*>(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;

View File

@@ -26,11 +26,17 @@
# include <QAction>
# include <QApplication>
# include <QClipboard>
# include <QMessageBox>
# include <QMimeData>
#endif
#include <App/Application.h>
#include <App/Document.h>
#include <Gui/Command.h>
#include <Gui/MainWindow.h>
#include <boost/bind.hpp>
#include "../App/Utils.h"
#include "../App/Cell.h"
#include <App/Range.h>
#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<int>::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<int>::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("<memory>", 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()));
}
}

View File

@@ -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<Range> ranges = selectedRanges();
if (sheet->hasCell(ranges)) {
Gui::Command::openCommand("Clear cell(s)");
std::vector<Range>::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<int, int>::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<int, int>::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();

View File

@@ -104,8 +104,8 @@ protected:
boost::signals2::scoped_connection rowHeightChangedConnection;
boost::signals2::scoped_connection positionChangedConnection;
QMap<int, int> newColumnSizes;
QMap<int, int> newRowSizes;
std::map<int, int> newColumnSizes;
std::map<int, int> newRowSizes;
};
} // namespace SpreadsheetModGui

View File

@@ -25,6 +25,7 @@
#include "PreCompiled.h"
#ifndef _PreComp_
# include <QApplication>
# include <QFile>
# include <QFileInfo>
# include <QImage>
@@ -43,6 +44,8 @@
#include <Gui/Application.h>
#include <Gui/MainWindow.h>
#include <Gui/Command.h>
#include <Gui/Document.h>
#include <Gui/View3DInventor.h>
#include <Base/FileInfo.h>
#include <Base/Stream.h>
#include <Base/Console.h>
@@ -143,32 +146,14 @@ Sheet *ViewProviderSheet::getSpreadsheetObject() const
return freecad_dynamic_cast<Sheet>(pcObject);
}
bool ViewProviderSheet::onDelete(const std::vector<std::string> &)
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<SpreadsheetGui::SheetView>(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<QMdiSubWindow*>(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)

View File

@@ -57,7 +57,7 @@ public:
Spreadsheet::Sheet* getSpreadsheetObject() const;
virtual bool onDelete(const std::vector<std::string> &);
virtual void beforeDelete() override;
QIcon getIcon() const;
@@ -65,6 +65,8 @@ public:
virtual bool isShow(void) const { return true; }
virtual Gui::MDIView *getMDIView() override;
protected:
SheetView* showSpreadsheetView();
void updateData(const App::Property *prop);