Merge pull request #5111 from chennes/spreadsheetCherryPickRealThunder
[Spreadsheet] Cherry-pick realthunder Round 1
This commit is contained in:
@@ -129,7 +129,7 @@ FC_LOG_LEVEL_INIT("Expression",true,true)
|
||||
|
||||
static inline std::ostream &operator<<(std::ostream &os, const App::Expression *expr) {
|
||||
if(expr) {
|
||||
os << std::endl;
|
||||
os << "\nin expression: ";
|
||||
expr->toString(os);
|
||||
}
|
||||
return os;
|
||||
@@ -1715,6 +1715,62 @@ FunctionExpression::FunctionExpression(const DocumentObject *_owner, Function _f
|
||||
, fname(std::move(name))
|
||||
, args(_args)
|
||||
{
|
||||
switch (f) {
|
||||
case ACOS:
|
||||
case ASIN:
|
||||
case ATAN:
|
||||
case ABS:
|
||||
case EXP:
|
||||
case LOG:
|
||||
case LOG10:
|
||||
case SIN:
|
||||
case SINH:
|
||||
case TAN:
|
||||
case TANH:
|
||||
case SQRT:
|
||||
case COS:
|
||||
case COSH:
|
||||
case ROUND:
|
||||
case TRUNC:
|
||||
case CEIL:
|
||||
case FLOOR:
|
||||
case MINVERT:
|
||||
case STR:
|
||||
if (args.size() != 1)
|
||||
EXPR_THROW("Invalid number of arguments: exactly one required.");
|
||||
break;
|
||||
case MOD:
|
||||
case ATAN2:
|
||||
case POW:
|
||||
if (args.size() != 2)
|
||||
EXPR_THROW("Invalid number of arguments: exactly two required.");
|
||||
break;
|
||||
case HYPOT:
|
||||
case CATH:
|
||||
if (args.size() < 2 || args.size() > 3)
|
||||
EXPR_THROW("Invalid number of arguments: exactly two, or three required.");
|
||||
break;
|
||||
case STDDEV:
|
||||
case SUM:
|
||||
case AVERAGE:
|
||||
case COUNT:
|
||||
case MIN:
|
||||
case MAX:
|
||||
case CREATE:
|
||||
case MSCALE:
|
||||
if (args.size() == 0)
|
||||
EXPR_THROW("Invalid number of arguments: at least one required.");
|
||||
break;
|
||||
case LIST:
|
||||
case TUPLE:
|
||||
break;
|
||||
case NONE:
|
||||
case AGGREGATES:
|
||||
case LAST:
|
||||
default:
|
||||
PARSER_THROW("Unknown function");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
FunctionExpression::~FunctionExpression()
|
||||
@@ -2029,6 +2085,8 @@ Py::Object FunctionExpression::evaluate(const Expression *expr, int f, const std
|
||||
PyObjectBase::__PyInit(res.ptr(),tuple.ptr(),dict.ptr());
|
||||
}
|
||||
return res;
|
||||
} else if (f == STR) {
|
||||
return Py::String(args[0]->getPyValue().as_string());
|
||||
}
|
||||
|
||||
Py::Object e1 = args[0]->getPyValue();
|
||||
@@ -2361,6 +2419,8 @@ void FunctionExpression::_toString(std::ostream &ss, bool persistent,int) const
|
||||
ss << "minvert("; break;;
|
||||
case CREATE:
|
||||
ss << "create("; break;;
|
||||
case STR:
|
||||
ss << "str("; break;;
|
||||
default:
|
||||
ss << fname << "("; break;;
|
||||
}
|
||||
@@ -3182,6 +3242,7 @@ static void initParser(const App::DocumentObject *owner)
|
||||
registered_functions["mscale"] = FunctionExpression::MSCALE;
|
||||
registered_functions["minvert"] = FunctionExpression::MINVERT;
|
||||
registered_functions["create"] = FunctionExpression::CREATE;
|
||||
registered_functions["str"] = FunctionExpression::STR;
|
||||
|
||||
// Aggregates
|
||||
registered_functions["sum"] = FunctionExpression::SUM;
|
||||
|
||||
@@ -272,6 +272,7 @@ public:
|
||||
MSCALE, // matrix scale by vector
|
||||
MINVERT, // invert matrix/placement/rotation
|
||||
CREATE, // create new object of a given type
|
||||
STR, // stringify
|
||||
|
||||
// Aggregates
|
||||
AGGREGATES,
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "ObjectIdentifier.h"
|
||||
#include "PropertyContainer.h"
|
||||
#include <Base/Exception.h>
|
||||
#include <Base/Tools.h>
|
||||
#include "Application.h"
|
||||
#include "DocumentObject.h"
|
||||
|
||||
@@ -212,8 +213,13 @@ void Property::setReadOnly(bool readOnly)
|
||||
void Property::hasSetValue(void)
|
||||
{
|
||||
PropertyCleaner guard(this);
|
||||
if (father)
|
||||
if (father) {
|
||||
father->onChanged(this);
|
||||
if(!testStatus(Busy)) {
|
||||
Base::BitsetLocker<decltype(StatusBits)> guard(StatusBits,Busy);
|
||||
signalChanged(*this);
|
||||
}
|
||||
}
|
||||
StatusBits.set(Touched);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <boost/any.hpp>
|
||||
#include <string>
|
||||
#include <bitset>
|
||||
#include <boost/signals2.hpp>
|
||||
|
||||
namespace Py {
|
||||
class Object;
|
||||
@@ -75,6 +76,7 @@ public:
|
||||
// relevant for the container using it
|
||||
EvalOnRestore = 14, // In case of expression binding, evaluate the
|
||||
// expression on restore and touch the object on value change.
|
||||
Busy = 15, // internal use to avoid recursive signaling
|
||||
|
||||
// The following bits are corresponding to PropertyType set when the
|
||||
// property added. These types are meant to be static, and cannot be
|
||||
@@ -269,6 +271,9 @@ private:
|
||||
private:
|
||||
PropertyContainer *father;
|
||||
const char *myName;
|
||||
|
||||
public:
|
||||
boost::signals2::signal<void (const App::Property&)> signalChanged;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -347,49 +347,65 @@ QVariant PropertyItem::toString(const QVariant& prop) const
|
||||
if (prop != QVariant() || propertyItems.size()!=1)
|
||||
return prop;
|
||||
|
||||
Base::PyGILStateLocker lock;
|
||||
Py::Object pyobj(propertyItems[0]->getPyObject(), true);
|
||||
std::ostringstream ss;
|
||||
if (pyobj.isNone()) {
|
||||
ss << "<None>";
|
||||
}
|
||||
else if(pyobj.isSequence()) {
|
||||
ss << '[';
|
||||
Py::Sequence seq(pyobj);
|
||||
bool first = true;
|
||||
Py_ssize_t i=0;
|
||||
for (i=0; i<2 && i < seq.size(); ++i) {
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
ss << ", ";
|
||||
ss << Py::Object(seq[i]).as_string();
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Base::PyGILStateLocker lock;
|
||||
Py::Object pyobj(propertyItems[0]->getPyObject(), true);
|
||||
std::ostringstream ss;
|
||||
if (pyobj.isNone()) {
|
||||
ss << "<None>";
|
||||
}
|
||||
else if(pyobj.isSequence()) {
|
||||
ss << '[';
|
||||
Py::Sequence seq(pyobj);
|
||||
bool first = true;
|
||||
Py_ssize_t i=0;
|
||||
for (i=0; i<2 && i < seq.size(); ++i) {
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
ss << ", ";
|
||||
ss << Py::Object(seq[i]).as_string();
|
||||
}
|
||||
|
||||
if (i < seq.size())
|
||||
ss << "...";
|
||||
ss << ']';
|
||||
}
|
||||
else if (pyobj.isMapping()) {
|
||||
ss << '{';
|
||||
Py::Mapping map(pyobj);
|
||||
bool first = true;
|
||||
auto it = map.begin();
|
||||
for(int i=0; i<2 && it != map.end(); ++it, ++i) {
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
ss << ", ";
|
||||
const auto &v = *it;
|
||||
ss << Py::Object(v.first).as_string() << ':' << Py::Object(v.second).as_string();
|
||||
if (i < seq.size())
|
||||
ss << "...";
|
||||
ss << ']';
|
||||
}
|
||||
else if (pyobj.isMapping()) {
|
||||
ss << '{';
|
||||
Py::Mapping map(pyobj);
|
||||
bool first = true;
|
||||
auto it = map.begin();
|
||||
for(int i=0; i<2 && it != map.end(); ++it, ++i) {
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
ss << ", ";
|
||||
const auto &v = *it;
|
||||
ss << Py::Object(v.first).as_string() << ':' << Py::Object(v.second).as_string();
|
||||
}
|
||||
|
||||
if (it != map.end())
|
||||
ss << "...";
|
||||
ss << '}';
|
||||
}
|
||||
else {
|
||||
ss << pyobj.as_string();
|
||||
if (it != map.end())
|
||||
ss << "...";
|
||||
ss << '}';
|
||||
}
|
||||
else
|
||||
ss << pyobj.as_string();
|
||||
} catch (Py::Exception &) {
|
||||
Base::PyException e;
|
||||
ss.str("");
|
||||
ss << "ERR: " << e.what();
|
||||
} catch (Base::Exception &e) {
|
||||
ss.str("");
|
||||
ss << "ERR: " << e.what();
|
||||
} catch (std::exception &e) {
|
||||
ss.str("");
|
||||
ss << "ERR: " << e.what();
|
||||
} catch (...) {
|
||||
ss.str("");
|
||||
ss << "ERR!";
|
||||
}
|
||||
|
||||
return QVariant(QString::fromUtf8(ss.str().c_str()));
|
||||
|
||||
@@ -247,7 +247,10 @@ 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.get())) {
|
||||
s.clear();
|
||||
if(expression->hasComponent())
|
||||
s = "=" + expression->toString(persistent);
|
||||
else if (freecad_dynamic_cast<App::StringExpression>(expression.get())) {
|
||||
s = static_cast<App::StringExpression*>(expression.get())->getText();
|
||||
char * end;
|
||||
errno = 0;
|
||||
|
||||
@@ -792,6 +792,12 @@ void Sheet::touchCells(Range range) {
|
||||
}while(range.next());
|
||||
}
|
||||
|
||||
void Sheet::recomputeCells(Range range) {
|
||||
do {
|
||||
recomputeCell(*range);
|
||||
}while(range.next());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Recompute cell at address \a p.
|
||||
* @param p Address of cell.
|
||||
|
||||
@@ -179,6 +179,8 @@ public:
|
||||
|
||||
void touchCells(App::Range range);
|
||||
|
||||
void recomputeCells(App::Range range);
|
||||
|
||||
// Signals
|
||||
|
||||
boost::signals2::signal<void (App::CellAddress)> cellUpdated;
|
||||
|
||||
@@ -165,5 +165,20 @@
|
||||
<UserDocu>Get given spreadsheet row height</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="touchCells">
|
||||
<Documentation>
|
||||
<UserDocu>touchCells(from, to=None): touch cells in the given range</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="recomputeCells">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
recomputeCells(from, to=None)
|
||||
|
||||
Manually recompute cells in the given range with the given order without
|
||||
following depedency order.
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
</PythonExport>
|
||||
</GenerateModel>
|
||||
|
||||
@@ -91,15 +91,41 @@ PyObject* SheetPy::set(PyObject *args)
|
||||
|
||||
PyObject* SheetPy::get(PyObject *args)
|
||||
{
|
||||
char *address;
|
||||
const char *address;
|
||||
const char *address2=0;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s:get", &address))
|
||||
if (!PyArg_ParseTuple(args, "s|s:get", &address, &address2))
|
||||
return 0;
|
||||
|
||||
PY_TRY {
|
||||
if(address2) {
|
||||
auto a1 = getSheetPtr()->getAddressFromAlias(address);
|
||||
if(a1.empty())
|
||||
a1 = address;
|
||||
auto a2 = getSheetPtr()->getAddressFromAlias(address2);
|
||||
if(a2.empty())
|
||||
a2 = address2;
|
||||
Range range(a1.c_str(),a2.c_str());
|
||||
Py::Tuple tuple(range.size());
|
||||
int i=0;
|
||||
do {
|
||||
App::Property *prop = getSheetPtr()->getPropertyByName(range.address().c_str());
|
||||
if(!prop) {
|
||||
PyErr_Format(PyExc_ValueError, "Invalid address '%s' in range %s:%s",
|
||||
range.address().c_str(), address, address2);
|
||||
return 0;
|
||||
}
|
||||
tuple.setItem(i++,Py::Object(prop->getPyObject(),true));
|
||||
}while(range.next());
|
||||
return Py::new_reference_to(tuple);
|
||||
}
|
||||
}PY_CATCH;
|
||||
|
||||
App::Property * prop = this->getSheetPtr()->getPropertyByName(address);
|
||||
|
||||
if (prop == 0) {
|
||||
PyErr_SetString(PyExc_ValueError, "Invalid address or property.");
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"Invalid cell address or property: %s",address);
|
||||
return 0;
|
||||
}
|
||||
return prop->getPyObject();
|
||||
@@ -113,21 +139,29 @@ PyObject* SheetPy::getContents(PyObject *args)
|
||||
if (!PyArg_ParseTuple(args, "s:getContents", &strAddress))
|
||||
return 0;
|
||||
|
||||
try {
|
||||
address = stringToAddress(strAddress);
|
||||
}
|
||||
catch (const Base::Exception & e) {
|
||||
PyErr_SetString(PyExc_ValueError, e.what());
|
||||
return 0;
|
||||
}
|
||||
PY_TRY {
|
||||
try {
|
||||
Sheet * sheet = getSheetPtr();
|
||||
std::string addr = sheet->getAddressFromAlias(strAddress);
|
||||
|
||||
std::string contents;
|
||||
const Cell * cell = this->getSheetPtr()->getCell(address);
|
||||
if (addr.empty())
|
||||
address = stringToAddress(strAddress);
|
||||
else
|
||||
address = stringToAddress(addr.c_str());
|
||||
}
|
||||
catch (const Base::Exception & e) {
|
||||
PyErr_SetString(PyExc_ValueError, e.what());
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cell)
|
||||
cell->getStringContent( contents );
|
||||
std::string contents;
|
||||
const Cell * cell = this->getSheetPtr()->getCell(address);
|
||||
|
||||
return Py::new_reference_to( Py::String( contents ) );
|
||||
if (cell)
|
||||
cell->getStringContent( contents );
|
||||
|
||||
return Py::new_reference_to( Py::String( contents ) );
|
||||
} PY_CATCH
|
||||
}
|
||||
|
||||
PyObject* SheetPy::clear(PyObject *args)
|
||||
@@ -587,8 +621,10 @@ PyObject* SheetPy::setAlignment(PyObject *args)
|
||||
std::string line = PyUnicode_AsUTF8(value);
|
||||
tokenizer<escaped_list_separator<char> > tok(line, e);
|
||||
|
||||
for(tokenizer<escaped_list_separator<char> >::iterator i = tok.begin(); i != tok.end();++i)
|
||||
alignment = Cell::decodeAlignment(*i, alignment);
|
||||
for(tokenizer<escaped_list_separator<char> >::iterator i = tok.begin(); i != tok.end();++i) {
|
||||
if(i->size())
|
||||
alignment = Cell::decodeAlignment(*i, alignment);
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::string error = std::string("style must be either set or string, not ") + value->ob_type->tp_name;
|
||||
@@ -903,15 +939,61 @@ PyObject* SheetPy::getRowHeight(PyObject *args)
|
||||
}
|
||||
}
|
||||
|
||||
PyObject *SheetPy::touchCells(PyObject *args) {
|
||||
const char *address;
|
||||
const char *address2=0;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s|s:touchCells", &address, &address2))
|
||||
return 0;
|
||||
|
||||
PY_TRY {
|
||||
std::string a1 = getSheetPtr()->getAddressFromAlias(address);
|
||||
if(a1.empty())
|
||||
a1 = address;
|
||||
|
||||
std::string a2;
|
||||
if(!address2) {
|
||||
a2 = a1;
|
||||
} else {
|
||||
a2 = getSheetPtr()->getAddressFromAlias(address2);
|
||||
if(a2.empty())
|
||||
a2 = address2;
|
||||
}
|
||||
getSheetPtr()->touchCells(Range(a1.c_str(),a2.c_str()));
|
||||
Py_Return;
|
||||
}PY_CATCH;
|
||||
}
|
||||
|
||||
PyObject *SheetPy::recomputeCells(PyObject *args) {
|
||||
const char *address;
|
||||
const char *address2=0;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s|s:touchCells", &address, &address2))
|
||||
return 0;
|
||||
|
||||
PY_TRY {
|
||||
std::string a1 = getSheetPtr()->getAddressFromAlias(address);
|
||||
if(a1.empty())
|
||||
a1 = address;
|
||||
|
||||
std::string a2;
|
||||
if(!address2) {
|
||||
a2 = a1;
|
||||
} else {
|
||||
a2 = getSheetPtr()->getAddressFromAlias(address2);
|
||||
if(a2.empty())
|
||||
a2 = address2;
|
||||
}
|
||||
getSheetPtr()->recomputeCells(Range(a1.c_str(),a2.c_str()));
|
||||
Py_Return;
|
||||
}PY_CATCH;
|
||||
}
|
||||
|
||||
// +++ custom attributes implementer ++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
PyObject *SheetPy::getCustomAttributes(const char* attr) const
|
||||
PyObject *SheetPy::getCustomAttributes(const char*) const
|
||||
{
|
||||
App::Property * prop = this->getSheetPtr()->getPropertyByName(attr);
|
||||
|
||||
if (prop == 0)
|
||||
return 0;
|
||||
return prop->getPyObject();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SheetPy::setCustomAttributes(const char* , PyObject* )
|
||||
|
||||
@@ -392,10 +392,15 @@ QVariant SheetModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
/* Number */
|
||||
double d;
|
||||
long l;
|
||||
bool isInteger = false;
|
||||
if(prop->isDerivedFrom(App::PropertyFloat::getClassTypeId()))
|
||||
d = static_cast<const App::PropertyFloat*>(prop)->getValue();
|
||||
else
|
||||
d = static_cast<const App::PropertyInteger*>(prop)->getValue();
|
||||
else {
|
||||
isInteger = true;
|
||||
l = static_cast<const App::PropertyInteger*>(prop)->getValue();
|
||||
d = l;
|
||||
}
|
||||
|
||||
switch (role) {
|
||||
case Qt::ForegroundRole: {
|
||||
@@ -431,10 +436,11 @@ QVariant SheetModel::data(const QModelIndex &index, int role) const
|
||||
//QString number = QString::number(d / displayUnit.scaler);
|
||||
v = number + Base::Tools::fromStdString(" " + displayUnit.stringRep);
|
||||
}
|
||||
else {
|
||||
v = QLocale().toString(d,'f',Base::UnitsApi::getDecimals());
|
||||
else if (!isInteger) {
|
||||
v = QLocale::system().toString(d,'f',Base::UnitsApi::getDecimals());
|
||||
//v = QString::number(d);
|
||||
}
|
||||
} else
|
||||
v = QString::number(l);
|
||||
return QVariant(v);
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#ifndef _PreComp_
|
||||
# include <QKeyEvent>
|
||||
# include <QAction>
|
||||
# include <QMenu>
|
||||
# include <QApplication>
|
||||
# include <QClipboard>
|
||||
# include <QMenu>
|
||||
@@ -35,6 +36,7 @@
|
||||
#include <App/AutoTransaction.h>
|
||||
#include <App/Document.h>
|
||||
#include <Gui/CommandT.h>
|
||||
#include <Gui/Application.h>
|
||||
#include <Gui/MainWindow.h>
|
||||
#include <boost_bind_bind.hpp>
|
||||
#include "../App/Utils.h"
|
||||
@@ -108,9 +110,6 @@ SheetTableView::SheetTableView(QWidget *parent)
|
||||
setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||
setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||
|
||||
horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
verticalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
connect(verticalHeader(), &QWidget::customContextMenuRequested,
|
||||
[this](const QPoint &point){
|
||||
QMenu menu(this);
|
||||
@@ -163,11 +162,46 @@ SheetTableView::SheetTableView(QWidget *parent)
|
||||
|
||||
auto cellProperties = new QAction(tr("Properties..."), this);
|
||||
addAction(cellProperties);
|
||||
|
||||
horizontalHeader()->setContextMenuPolicy(Qt::ActionsContextMenu);
|
||||
verticalHeader()->setContextMenuPolicy(Qt::ActionsContextMenu);
|
||||
|
||||
setContextMenuPolicy(Qt::ActionsContextMenu);
|
||||
setTabKeyNavigation(false);
|
||||
contextMenu = new QMenu(this);
|
||||
|
||||
contextMenu->addAction(cellProperties);
|
||||
connect(cellProperties, SIGNAL(triggered()), this, SLOT(cellProperties()));
|
||||
|
||||
contextMenu->addSeparator();
|
||||
QAction *recompute = new QAction(tr("Recompute"),this);
|
||||
connect(recompute, SIGNAL(triggered()), this, SLOT(onRecompute()));
|
||||
contextMenu->addAction(recompute);
|
||||
|
||||
contextMenu->addSeparator();
|
||||
actionMerge = contextMenu->addAction(tr("Merge cells"));
|
||||
connect(actionMerge,SIGNAL(triggered()), this, SLOT(mergeCells()));
|
||||
actionSplit = contextMenu->addAction(tr("Split cells"));
|
||||
connect(actionSplit,SIGNAL(triggered()), this, SLOT(splitCell()));
|
||||
|
||||
contextMenu->addSeparator();
|
||||
actionCut = contextMenu->addAction(tr("Cut"));
|
||||
connect(actionCut,SIGNAL(triggered()), this, SLOT(cutSelection()));
|
||||
actionCopy = contextMenu->addAction(tr("Copy"));
|
||||
connect(actionCopy,SIGNAL(triggered()), this, SLOT(copySelection()));
|
||||
actionPaste = contextMenu->addAction(tr("Paste"));
|
||||
connect(actionPaste,SIGNAL(triggered()), this, SLOT(pasteClipboard()));
|
||||
actionDel = contextMenu->addAction(tr("Delete"));
|
||||
connect(actionDel,SIGNAL(triggered()), this, SLOT(deleteSelection()));
|
||||
|
||||
setTabKeyNavigation(false);
|
||||
}
|
||||
|
||||
void SheetTableView::onRecompute() {
|
||||
Gui::Command::openCommand("Recompute cells");
|
||||
for(auto &range : selectedRanges()) {
|
||||
Gui::cmdAppObjectArgs(sheet, "recomputeCells('%s', '%s')",
|
||||
range.fromCellString(), range.toCellString());
|
||||
}
|
||||
Gui::Command::commitCommand();
|
||||
}
|
||||
|
||||
void SheetTableView::cellProperties()
|
||||
@@ -828,6 +862,14 @@ void SheetTableView::ModifyBlockSelection(int targetRow, int targetCol)
|
||||
this->selectionModel()->setCurrentIndex(model()->index(targetRow, targetCol), QItemSelectionModel::Current);
|
||||
}
|
||||
|
||||
void SheetTableView::mergeCells() {
|
||||
Gui::Application::Instance->commandManager().runCommandByName("Spreadsheet_MergeCells");
|
||||
}
|
||||
|
||||
void SheetTableView::splitCell() {
|
||||
Gui::Application::Instance->commandManager().runCommandByName("Spreadsheet_SplitCell");
|
||||
}
|
||||
|
||||
void SheetTableView::closeEditor(QWidget * editor, QAbstractItemDelegate::EndEditHint hint)
|
||||
{
|
||||
QTableView::closeEditor(editor, hint);
|
||||
@@ -845,4 +887,25 @@ void SheetTableView::edit ( const QModelIndex & index )
|
||||
QTableView::edit(index);
|
||||
}
|
||||
|
||||
void SheetTableView::contextMenuEvent(QContextMenuEvent *) {
|
||||
const QMimeData* mimeData = QApplication::clipboard()->mimeData();
|
||||
if(!selectionModel()->hasSelection()) {
|
||||
actionCut->setEnabled(false);
|
||||
actionCopy->setEnabled(false);
|
||||
actionDel->setEnabled(false);
|
||||
actionPaste->setEnabled(false);
|
||||
actionSplit->setEnabled(false);
|
||||
actionMerge->setEnabled(false);
|
||||
}else{
|
||||
actionPaste->setEnabled(mimeData && (mimeData->hasText() || mimeData->hasText()));
|
||||
actionCut->setEnabled(true);
|
||||
actionCopy->setEnabled(true);
|
||||
actionDel->setEnabled(true);
|
||||
actionSplit->setEnabled(true);
|
||||
actionMerge->setEnabled(true);
|
||||
}
|
||||
|
||||
contextMenu->exec(QCursor::pos());
|
||||
}
|
||||
|
||||
#include "moc_SheetTableView.cpp"
|
||||
|
||||
@@ -58,12 +58,15 @@ public:
|
||||
void edit(const QModelIndex &index);
|
||||
void setSheet(Spreadsheet::Sheet *_sheet);
|
||||
std::vector<App::Range> selectedRanges() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
void mergeCells();
|
||||
void splitCell();
|
||||
void deleteSelection();
|
||||
void copySelection();
|
||||
void cutSelection();
|
||||
void pasteClipboard();
|
||||
void finishEditWithMove(int keyPressed, Qt::KeyboardModifiers modifiers, bool handleTabMotion = false);
|
||||
|
||||
void finishEditWithMove(int keyPressed, Qt::KeyboardModifiers modifiers, bool handleTabMotion = false);
|
||||
void ModifyBlockSelection(int targetRow, int targetColumn);
|
||||
|
||||
protected Q_SLOTS:
|
||||
@@ -76,16 +79,29 @@ protected Q_SLOTS:
|
||||
void insertColumnsAfter();
|
||||
void removeColumns();
|
||||
void cellProperties();
|
||||
void onRecompute();
|
||||
|
||||
protected:
|
||||
bool edit(const QModelIndex &index, EditTrigger trigger, QEvent *event);
|
||||
bool event(QEvent *event);
|
||||
void closeEditor(QWidget *editor, QAbstractItemDelegate::EndEditHint hint);
|
||||
void mousePressEvent(QMouseEvent* event);
|
||||
|
||||
void contextMenuEvent (QContextMenuEvent * e);
|
||||
|
||||
QModelIndex currentEditIndex;
|
||||
Spreadsheet::Sheet * sheet;
|
||||
int tabCounter;
|
||||
|
||||
QMenu *contextMenu;
|
||||
|
||||
QAction *actionMerge;
|
||||
QAction *actionSplit;
|
||||
QAction *actionCopy;
|
||||
QAction *actionPaste;
|
||||
QAction *actionCut;
|
||||
QAction *actionDel;
|
||||
|
||||
boost::signals2::scoped_connection cellSpanChangedConnection;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user