From 127e8098e81b8f015528af320e285e75a23733c1 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Tue, 24 Dec 2019 10:34:50 +0800 Subject: [PATCH] Spreadsheet: add a few context menu options --- src/Mod/Spreadsheet/App/Sheet.cpp | 6 + src/Mod/Spreadsheet/App/Sheet.h | 2 + src/Mod/Spreadsheet/App/SheetPy.xml | 15 +++ src/Mod/Spreadsheet/App/SheetPyImp.cpp | 128 +++++++++++++++++---- src/Mod/Spreadsheet/Gui/SheetTableView.cpp | 73 +++++++++++- src/Mod/Spreadsheet/Gui/SheetTableView.h | 20 +++- 6 files changed, 214 insertions(+), 30 deletions(-) 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/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; };