diff --git a/src/Mod/Spreadsheet/App/Cell.cpp b/src/Mod/Spreadsheet/App/Cell.cpp index 8b3db1e69e..c1fbfc2016 100644 --- a/src/Mod/Spreadsheet/App/Cell.cpp +++ b/src/Mod/Spreadsheet/App/Cell.cpp @@ -80,7 +80,6 @@ const int Cell::MARK_SET = 0x40000000; const int Cell::EXCEPTION_SET = 0x20000000; const int Cell::PARSE_EXCEPTION_SET = 0x80000000; const int Cell::RESOLVE_EXCEPTION_SET= 0x01000000; -const int Cell::SPANS_UPDATED = 0x10000000; /* Alignment */ const int Cell::ALIGNMENT_LEFT = 0x01; @@ -619,7 +618,6 @@ void Cell::setSpans(int rows, int columns) rowSpan = (rows == -1 ? 1 : rows); colSpan = (columns == -1 ? 1 : columns); setUsed(SPANS_SET, (rowSpan != 1 || colSpan != 1) ); - setUsed(SPANS_UPDATED); setDirty(); signaller.tryInvoke(); } diff --git a/src/Mod/Spreadsheet/App/Cell.h b/src/Mod/Spreadsheet/App/Cell.h index ce07716b49..214eebf48b 100644 --- a/src/Mod/Spreadsheet/App/Cell.h +++ b/src/Mod/Spreadsheet/App/Cell.h @@ -118,8 +118,6 @@ public: bool isMarked() const { return isUsed(MARK_SET); } - bool spansChanged() const { return isUsed(SPANS_UPDATED); } - void visit(App::ExpressionVisitor & v); App::CellAddress getAddress() const { return address; } @@ -172,7 +170,6 @@ private: static const int ALIAS_SET; static const int SPANS_SET; static const int MARK_SET; - static const int SPANS_UPDATED; static const int EXCEPTION_SET; static const int PARSE_EXCEPTION_SET; static const int RESOLVE_EXCEPTION_SET; diff --git a/src/Mod/Spreadsheet/App/PropertySheet.cpp b/src/Mod/Spreadsheet/App/PropertySheet.cpp index 93a55c6734..4e29773483 100644 --- a/src/Mod/Spreadsheet/App/PropertySheet.cpp +++ b/src/Mod/Spreadsheet/App/PropertySheet.cpp @@ -680,6 +680,7 @@ void PropertySheet::setSpans(CellAddress address, int rows, int columns) Cell * cell = nonNullCellAt(address); assert(cell); cell->setSpans(rows, columns); + owner->cellSpanChanged(address); } void PropertySheet::clearAlias(CellAddress address) @@ -1025,32 +1026,47 @@ unsigned int PropertySheet::getMemSize() const bool PropertySheet::mergeCells(CellAddress from, CellAddress to) { - // Check that this merge is not overlapping other merged cells - for (int r = from.row(); r <= to.row(); ++r) { - for (int c = from.col(); c <= to.col(); ++c) { - if (mergedCells.find(CellAddress(r, c)) != mergedCells.end()) { - Base::Console().Warning("Cells merging is not possible when already merged cells are in the target range.\n"); - return false; - } - } + if (from == to) { + splitCell(from); + return true; + } + + auto it = mergedCells.find(from); + if (it != mergedCells.end()) { + int rows, cols; + cellAt(it->second)->getSpans(rows, cols); + if (to.row() - from.row() + 1 == rows + && to.col() - from.col() + 1 == cols) + return false; } AtomicPropertyChange signaller(*this); + // Check that this merge is not overlapping other merged cells + for (int r = from.row(); r <= to.row(); ++r) { + for (int c = from.col(); c <= to.col(); ++c) { + splitCell(CellAddress(r, c)); + } + } + // Clear cells that will be hidden by the merge - for (int r = from.row(); r <= to.row(); ++r) - for (int c = from.col(); c <= to.col(); ++c) + for (int r = from.row(); r <= to.row(); ++r) { + for (int c = from.col(); c <= to.col(); ++c) { if ( !(r == from.row() && c == from.col()) ) clear(CellAddress(r, c)); + } + } // Update internal structure to track merged cells - for (int r = from.row(); r <= to.row(); ++r) + for (int r = from.row(); r <= to.row(); ++r) { for (int c = from.col(); c <= to.col(); ++c) { mergedCells[CellAddress(r, c)] = from; setDirty(CellAddress(r, c)); } + } setSpans(from, to.row() - from.row() + 1, to.col() - from.col() + 1); + signaller.tryInvoke(); return true; @@ -1068,13 +1084,15 @@ void PropertySheet::splitCell(CellAddress address) AtomicPropertyChange signaller(*this); cellAt(anchor)->getSpans(rows, cols); - for (int r = anchor.row(); r <= anchor.row() + rows; ++r) - for (int c = anchor.col(); c <= anchor.col() + cols; ++c) { + for (int r = anchor.row(); r < anchor.row() + rows; ++r) { + for (int c = anchor.col(); c < anchor.col() + cols; ++c) { setDirty(CellAddress(r, c)); mergedCells.erase(CellAddress(r, c)); } + } setSpans(anchor, -1, -1); + signaller.tryInvoke(); } diff --git a/src/Mod/Spreadsheet/App/Sheet.cpp b/src/Mod/Spreadsheet/App/Sheet.cpp index 93d3bd31c0..122834a6ca 100644 --- a/src/Mod/Spreadsheet/App/Sheet.cpp +++ b/src/Mod/Spreadsheet/App/Sheet.cpp @@ -810,9 +810,6 @@ void Sheet::recomputeCell(CellAddress p) if(e.isDerivedFrom(Base::AbortException::getClassTypeId())) throw; } - - if (!cell || cell->spansChanged()) - cellSpanChanged(p); } PropertySheet::BindingType diff --git a/src/Mod/Spreadsheet/Gui/SheetTableView.cpp b/src/Mod/Spreadsheet/Gui/SheetTableView.cpp index 19e2c42b86..fed547566e 100644 --- a/src/Mod/Spreadsheet/Gui/SheetTableView.cpp +++ b/src/Mod/Spreadsheet/Gui/SheetTableView.cpp @@ -210,6 +210,9 @@ SheetTableView::SheetTableView(QWidget *parent) connect(actionDel,SIGNAL(triggered()), this, SLOT(deleteSelection())); setTabKeyNavigation(false); + + timer.setSingleShot(true); + QObject::connect(&timer, &QTimer::timeout, [this](){updateCellSpan();}); } void SheetTableView::onRecompute() { @@ -456,20 +459,31 @@ SheetTableView::~SheetTableView() } -void SheetTableView::updateCellSpan(CellAddress address) +void SheetTableView::updateCellSpan() { int rows, cols; - sheet->getSpans(address, rows, cols); + // Unspan first to avoid overlap + for (const auto &addr : spanChanges) { + if (rowSpan(addr.row(), addr.col()) > 1 || columnSpan(addr.row(), addr.col()) > 1) + setSpan(addr.row(), addr.col(), 1, 1); + } - if (rows != rowSpan(address.row(), address.col()) || cols != columnSpan(address.row(), address.col())) - setSpan(address.row(), address.col(), rows, cols); + for (const auto &addr : spanChanges) { + sheet->getSpans(addr, rows, cols); + if (rows > 1 || cols > 1) + setSpan(addr.row(), addr.col(), rows, cols); + } + spanChanges.clear(); } void SheetTableView::setSheet(Sheet* _sheet) { sheet = _sheet; - cellSpanChangedConnection = sheet->cellSpanChanged.connect(bind(&SheetTableView::updateCellSpan, this, bp::_1)); + cellSpanChangedConnection = sheet->cellSpanChanged.connect([&](const CellAddress &addr) { + spanChanges.insert(addr); + timer.start(10); + }); // Update row and column spans std::vector usedCells = sheet->getUsedCells(); @@ -477,8 +491,11 @@ void SheetTableView::setSheet(Sheet* _sheet) for (std::vector::const_iterator i = usedCells.begin(); i != usedCells.end(); ++i) { CellAddress address(*i); - if (sheet->isMergedCell(address)) - updateCellSpan(address); + if (sheet->isMergedCell(address)) { + int rows, cols; + sheet->getSpans(address, rows, cols); + setSpan(address.row(), address.col(), rows, cols); + } } // Update column widths and row height diff --git a/src/Mod/Spreadsheet/Gui/SheetTableView.h b/src/Mod/Spreadsheet/Gui/SheetTableView.h index c537d70f2d..bba842823c 100644 --- a/src/Mod/Spreadsheet/Gui/SheetTableView.h +++ b/src/Mod/Spreadsheet/Gui/SheetTableView.h @@ -25,6 +25,7 @@ #include #include +#include #include @@ -73,7 +74,7 @@ public Q_SLOTS: protected Q_SLOTS: void commitData(QWidget *editor) override; - void updateCellSpan(App::CellAddress address); + void updateCellSpan(); void insertRows(); void insertRowsAfter(); void removeRows(); @@ -113,7 +114,10 @@ protected: QAction *actionDel; QAction *actionBind; + QTimer timer; + boost::signals2::scoped_connection cellSpanChangedConnection; + std::set spanChanges; }; }