diff --git a/src/App/Expression.cpp b/src/App/Expression.cpp
index 8149ce708b..d7d69884fc 100644
--- a/src/App/Expression.cpp
+++ b/src/App/Expression.cpp
@@ -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;
diff --git a/src/App/ExpressionParser.h b/src/App/ExpressionParser.h
index 0e3b9f8a95..e0dfe1ee0d 100644
--- a/src/App/ExpressionParser.h
+++ b/src/App/ExpressionParser.h
@@ -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,
diff --git a/src/App/Property.cpp b/src/App/Property.cpp
index 8f2142fd52..d4571eadd6 100644
--- a/src/App/Property.cpp
+++ b/src/App/Property.cpp
@@ -32,6 +32,7 @@
#include "ObjectIdentifier.h"
#include "PropertyContainer.h"
#include
+#include
#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 guard(StatusBits,Busy);
+ signalChanged(*this);
+ }
+ }
StatusBits.set(Touched);
}
diff --git a/src/App/Property.h b/src/App/Property.h
index 7a1cc64944..9a33b6dad8 100644
--- a/src/App/Property.h
+++ b/src/App/Property.h
@@ -31,6 +31,7 @@
#include
#include
#include
+#include
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 signalChanged;
};
diff --git a/src/Gui/propertyeditor/PropertyItem.cpp b/src/Gui/propertyeditor/PropertyItem.cpp
index e94075eb73..1fd77d65b8 100644
--- a/src/Gui/propertyeditor/PropertyItem.cpp
+++ b/src/Gui/propertyeditor/PropertyItem.cpp
@@ -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 << "";
- }
- 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 << "";
}
+ 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()));
diff --git a/src/Mod/Spreadsheet/App/Cell.cpp b/src/Mod/Spreadsheet/App/Cell.cpp
index f58fbb89c6..0b23d6eb83 100644
--- a/src/Mod/Spreadsheet/App/Cell.cpp
+++ b/src/Mod/Spreadsheet/App/Cell.cpp
@@ -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(expression.get())) {
+ s.clear();
+ if(expression->hasComponent())
+ s = "=" + expression->toString(persistent);
+ else if (freecad_dynamic_cast(expression.get())) {
s = static_cast(expression.get())->getText();
char * end;
errno = 0;
diff --git a/src/Mod/Spreadsheet/App/Sheet.cpp b/src/Mod/Spreadsheet/App/Sheet.cpp
index 561e88c303..61e4c2b17b 100644
--- a/src/Mod/Spreadsheet/App/Sheet.cpp
+++ b/src/Mod/Spreadsheet/App/Sheet.cpp
@@ -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.
diff --git a/src/Mod/Spreadsheet/App/Sheet.h b/src/Mod/Spreadsheet/App/Sheet.h
index 18e898b849..d1e7324773 100644
--- a/src/Mod/Spreadsheet/App/Sheet.h
+++ b/src/Mod/Spreadsheet/App/Sheet.h
@@ -179,6 +179,8 @@ public:
void touchCells(App::Range range);
+ void recomputeCells(App::Range range);
+
// Signals
boost::signals2::signal cellUpdated;
diff --git a/src/Mod/Spreadsheet/App/SheetPy.xml b/src/Mod/Spreadsheet/App/SheetPy.xml
index 679371b665..a2c4bf6330 100644
--- a/src/Mod/Spreadsheet/App/SheetPy.xml
+++ b/src/Mod/Spreadsheet/App/SheetPy.xml
@@ -165,5 +165,20 @@
Get given spreadsheet row height
+
+
+ touchCells(from, to=None): touch cells in the given range
+
+
+
+
+
+recomputeCells(from, to=None)
+
+Manually recompute cells in the given range with the given order without
+following depedency order.
+
+
+
diff --git a/src/Mod/Spreadsheet/App/SheetPyImp.cpp b/src/Mod/Spreadsheet/App/SheetPyImp.cpp
index 221de6a1c9..815f0c96a5 100644
--- a/src/Mod/Spreadsheet/App/SheetPyImp.cpp
+++ b/src/Mod/Spreadsheet/App/SheetPyImp.cpp
@@ -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 > tok(line, e);
- for(tokenizer >::iterator i = tok.begin(); i != tok.end();++i)
- alignment = Cell::decodeAlignment(*i, alignment);
+ for(tokenizer >::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* )
diff --git a/src/Mod/Spreadsheet/Gui/SheetModel.cpp b/src/Mod/Spreadsheet/Gui/SheetModel.cpp
index 7ac9611fdd..341466214f 100644
--- a/src/Mod/Spreadsheet/Gui/SheetModel.cpp
+++ b/src/Mod/Spreadsheet/Gui/SheetModel.cpp
@@ -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(prop)->getValue();
- else
- d = static_cast(prop)->getValue();
+ else {
+ isInteger = true;
+ l = static_cast(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:
diff --git a/src/Mod/Spreadsheet/Gui/SheetTableView.cpp b/src/Mod/Spreadsheet/Gui/SheetTableView.cpp
index 86b532213e..634a30e815 100644
--- a/src/Mod/Spreadsheet/Gui/SheetTableView.cpp
+++ b/src/Mod/Spreadsheet/Gui/SheetTableView.cpp
@@ -24,6 +24,7 @@
#ifndef _PreComp_
# include
# include
+# include
# include
# include
# include
@@ -35,6 +36,7 @@
#include
#include
#include
+#include
#include
#include
#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"
diff --git a/src/Mod/Spreadsheet/Gui/SheetTableView.h b/src/Mod/Spreadsheet/Gui/SheetTableView.h
index 828856fd4f..840a4961e2 100644
--- a/src/Mod/Spreadsheet/Gui/SheetTableView.h
+++ b/src/Mod/Spreadsheet/Gui/SheetTableView.h
@@ -58,12 +58,15 @@ public:
void edit(const QModelIndex &index);
void setSheet(Spreadsheet::Sheet *_sheet);
std::vector 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;
};