Spreadsheet: fix cell span synchronization with SheetTableView
Also, allow merge cell with overlaps, by auto split overlapped cells first
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -810,9 +810,6 @@ void Sheet::recomputeCell(CellAddress p)
|
||||
if(e.isDerivedFrom(Base::AbortException::getClassTypeId()))
|
||||
throw;
|
||||
}
|
||||
|
||||
if (!cell || cell->spansChanged())
|
||||
cellSpanChanged(p);
|
||||
}
|
||||
|
||||
PropertySheet::BindingType
|
||||
|
||||
@@ -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<std::string> usedCells = sheet->getUsedCells();
|
||||
@@ -477,8 +491,11 @@ void SheetTableView::setSheet(Sheet* _sheet)
|
||||
for (std::vector<std::string>::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
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include <QHeaderView>
|
||||
#include <QTableView>
|
||||
#include <QTimer>
|
||||
|
||||
#include <Mod/Spreadsheet/App/Sheet.h>
|
||||
|
||||
@@ -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<App::CellAddress> spanChanges;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user