Spreadsheet: Decrease default size (#27152)
* Spreadsheet: dynamic row and coulumn counts * Spreadsheet: test distant cell * Spreadsheet: fix removeColumns condition * Spreadsheet: Fix zoom detachment * Spreadsheet: Fix SheetModel::containInView * Spreadsheet: Make sure the number of rows/cols decreases correctly after removal * Spreadsheet: Code quality fixes * Spreadsheet: Fix typo * Spreadsheet: Move sheet->insert/remove outside Qt signals * Update src/Mod/Spreadsheet/Gui/SheetTableView.cpp Co-authored-by: Benjamin Nauck <[email protected]> * Update src/Mod/Spreadsheet/Gui/SheetModel.h Co-authored-by: Benjamin Nauck <[email protected]> * Spreadsheet: Add comment about nesting beginInsertColumn * Spreadsheet: Remove c-style Qt header --------- Co-authored-by: Benjamin Nauck <[email protected]>
This commit is contained in:
co-authored by
Benjamin Nauck
parent
d2b84db105
commit
1027be1efa
@@ -23,10 +23,10 @@
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include <QFont>
|
||||
#include <QLocale>
|
||||
|
||||
|
||||
#include <App/Document.h>
|
||||
#include <Base/Interpreter.h>
|
||||
#include <Base/Tools.h>
|
||||
@@ -36,6 +36,7 @@
|
||||
#include <Mod/Spreadsheet/App/Sheet.h>
|
||||
|
||||
#include "SheetModel.h"
|
||||
#include "App/Range.h"
|
||||
|
||||
|
||||
using namespace SpreadsheetGui;
|
||||
@@ -46,13 +47,17 @@ namespace sp = std::placeholders;
|
||||
SheetModel::SheetModel(Sheet* _sheet, QObject* parent)
|
||||
: QAbstractTableModel(parent)
|
||||
, sheet(_sheet)
|
||||
, rows(1000)
|
||||
, cols(26)
|
||||
{
|
||||
containSheetDataInView();
|
||||
|
||||
// NOLINTBEGIN
|
||||
cellUpdatedConnection = sheet->cellUpdated.connect(
|
||||
std::bind(&SheetModel::cellUpdated, this, sp::_1)
|
||||
connections.emplace_back(
|
||||
sheet->cellUpdated.connect(std::bind(&SheetModel::cellUpdated, this, sp::_1))
|
||||
);
|
||||
rangeUpdatedConnection = sheet->rangeUpdated.connect(
|
||||
std::bind(&SheetModel::rangeUpdated, this, sp::_1)
|
||||
connections.emplace_back(
|
||||
sheet->rangeUpdated.connect(std::bind(&SheetModel::rangeUpdated, this, sp::_1))
|
||||
);
|
||||
// NOLINTEND
|
||||
|
||||
@@ -65,53 +70,108 @@ SheetModel::SheetModel(Sheet* _sheet, QObject* parent)
|
||||
textFgColor = QColor(QString::fromStdString(hGrp->GetASCII("TextColor", "#000000")));
|
||||
positiveFgColor = QColor(QString::fromStdString(hGrp->GetASCII("PositiveNumberColor", "#000000")));
|
||||
negativeFgColor = QColor(QString::fromStdString(hGrp->GetASCII("NegativeNumberColor", "#000000")));
|
||||
|
||||
|
||||
const QStringList alphabet {QStringLiteral("A"), QStringLiteral("B"), QStringLiteral("C"),
|
||||
QStringLiteral("D"), QStringLiteral("E"), QStringLiteral("F"),
|
||||
QStringLiteral("G"), QStringLiteral("H"), QStringLiteral("I"),
|
||||
QStringLiteral("J"), QStringLiteral("K"), QStringLiteral("L"),
|
||||
QStringLiteral("M"), QStringLiteral("N"), QStringLiteral("O"),
|
||||
QStringLiteral("P"), QStringLiteral("Q"), QStringLiteral("R"),
|
||||
QStringLiteral("S"), QStringLiteral("T"), QStringLiteral("U"),
|
||||
QStringLiteral("V"), QStringLiteral("W"), QStringLiteral("X"),
|
||||
QStringLiteral("Y"), QStringLiteral("Z")};
|
||||
|
||||
for (const QString& letter : alphabet) {
|
||||
columnLabels << letter;
|
||||
}
|
||||
|
||||
for (const QString& left : alphabet) {
|
||||
for (const QString& right : alphabet) {
|
||||
columnLabels << left + right;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 1; i <= maxRowCount; i++) {
|
||||
rowLabels << QString::number(i);
|
||||
}
|
||||
}
|
||||
|
||||
SheetModel::~SheetModel()
|
||||
{
|
||||
cellUpdatedConnection.disconnect();
|
||||
rangeUpdatedConnection.disconnect();
|
||||
}
|
||||
{}
|
||||
|
||||
int SheetModel::rowCount(const QModelIndex& parent) const
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
return maxRowCount;
|
||||
return rows;
|
||||
}
|
||||
|
||||
int SheetModel::columnCount(const QModelIndex& parent) const
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
return maxColumnCount;
|
||||
return cols;
|
||||
}
|
||||
|
||||
bool SheetModel::insertRows(int row, int count, const QModelIndex& parent)
|
||||
{
|
||||
if (rows + count > CellAddress::MAX_ROWS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
beginInsertRows(parent, rows, rows + count - 1);
|
||||
rows += count;
|
||||
endInsertRows();
|
||||
|
||||
// Called after endInsertRows to avoid potential nesting. It will call
|
||||
// `SheetModel::(cell|range)Updated` on modified cells anyways, sending necessary view signals.
|
||||
sheet->insertRows(row, count);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SheetModel::insertColumns(int column, int count, const QModelIndex& parent)
|
||||
{
|
||||
if (cols + count > CellAddress::MAX_COLUMNS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
beginInsertColumns(parent, column, column + count - 1);
|
||||
cols += count;
|
||||
endInsertColumns();
|
||||
|
||||
// Called after endInsertColumns to avoid potential nesting. It will call
|
||||
// `SheetModel::(cell|range)Updated` on modified cells anyways, sending necessary view signals.
|
||||
sheet->insertColumns(column, count);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SheetModel::removeRows(int row, int count, const QModelIndex& parent)
|
||||
{
|
||||
if (count >= rows) {
|
||||
// Prevent the header from disappearing
|
||||
return false;
|
||||
}
|
||||
beginRemoveRows(parent, row, row + count - 1);
|
||||
rows -= count;
|
||||
endRemoveRows();
|
||||
sheet->removeRows(row, count);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SheetModel::removeColumns(int column, int count, const QModelIndex& parent)
|
||||
{
|
||||
if (count >= cols) {
|
||||
// Prevent the header from disappearing
|
||||
return false;
|
||||
}
|
||||
beginRemoveColumns(parent, column, column + count - 1);
|
||||
cols -= count;
|
||||
endRemoveColumns();
|
||||
sheet->removeColumns(column, count);
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
QString encodeColumn(int column)
|
||||
{
|
||||
int toSkipTotal = 0;
|
||||
int toSkipNext = 26;
|
||||
int length = 1;
|
||||
|
||||
while (toSkipTotal + toSkipNext <= column) {
|
||||
toSkipTotal += toSkipNext;
|
||||
toSkipNext *= 26;
|
||||
length += 1;
|
||||
}
|
||||
|
||||
column -= toSkipTotal;
|
||||
|
||||
QString res;
|
||||
for (int i = 0; i < length; i++) {
|
||||
res = QString(static_cast<char>('A' + (column % 26))) + res;
|
||||
column /= 26;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
QVariant formatCellDisplay(QString value, const Cell* cell)
|
||||
{
|
||||
std::string alias;
|
||||
@@ -552,16 +612,12 @@ QVariant SheetModel::data(const QModelIndex& index, int role) const
|
||||
QVariant SheetModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (role == Qt::SizeHintRole) {
|
||||
const int width
|
||||
= (orientation == Qt::Horizontal ? sheet->getColumnWidth(section)
|
||||
: PropertyColumnWidths::defaultHeaderWidth);
|
||||
const int height
|
||||
= (orientation == Qt::Horizontal ? PropertyRowHeights::defaultHeight
|
||||
: sheet->getRowHeight(section));
|
||||
return QSize {width, height};
|
||||
return orientation == Qt::Horizontal
|
||||
? QSize(sheet->getColumnWidth(section), PropertyRowHeights::defaultHeight)
|
||||
: QSize(PropertyColumnWidths::defaultHeaderWidth, sheet->getRowHeight(section));
|
||||
}
|
||||
if (role == Qt::DisplayRole) {
|
||||
return (orientation == Qt::Horizontal ? columnLabels.at(section) : rowLabels.at(section));
|
||||
return orientation == Qt::Horizontal ? encodeColumn(section) : QString::number(section + 1);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -621,19 +677,39 @@ Qt::ItemFlags SheetModel::flags(const QModelIndex& /*index*/) const
|
||||
return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled;
|
||||
}
|
||||
|
||||
void SheetModel::containSheetDataInView()
|
||||
{
|
||||
CellAddress address = std::get<1>(sheet->getUsedRange());
|
||||
if (address.row() >= rows) {
|
||||
beginInsertRows(QModelIndex(), rows, address.row());
|
||||
rows = address.row() + 1;
|
||||
endInsertRows();
|
||||
}
|
||||
if (address.col() >= cols) {
|
||||
beginInsertColumns(QModelIndex(), cols, address.col());
|
||||
cols = address.col() + 1;
|
||||
endInsertColumns();
|
||||
}
|
||||
}
|
||||
|
||||
void SheetModel::cellUpdated(CellAddress address)
|
||||
{
|
||||
QModelIndex i = index(address.row(), address.col());
|
||||
|
||||
Q_EMIT dataChanged(i, i);
|
||||
containSheetDataInView();
|
||||
if (address.row() < rows && address.col() < cols) {
|
||||
QModelIndex i = index(address.row(), address.col());
|
||||
Q_EMIT dataChanged(i, i);
|
||||
}
|
||||
}
|
||||
|
||||
void SheetModel::rangeUpdated(const Range& range)
|
||||
{
|
||||
QModelIndex i = index(range.from().row(), range.from().col());
|
||||
QModelIndex j = index(range.to().row(), range.to().col());
|
||||
|
||||
Q_EMIT dataChanged(i, j);
|
||||
containSheetDataInView();
|
||||
if (range.from().row() < rows && range.from().col() < cols) {
|
||||
QModelIndex i = index(range.from().row(), range.from().col());
|
||||
QModelIndex j
|
||||
= index(std::min(range.to().row(), rows - 1), std::min(range.to().col(), cols - 1));
|
||||
Q_EMIT dataChanged(i, j);
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_SheetModel.cpp"
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fastsignals/connection.h"
|
||||
#include <QAbstractTableModel>
|
||||
|
||||
#include <App/Range.h>
|
||||
@@ -47,6 +48,10 @@ public:
|
||||
explicit SheetModel(QObject* parent);
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex()) override;
|
||||
bool insertColumns(int column, int count, const QModelIndex& parent = QModelIndex()) override;
|
||||
bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()) override;
|
||||
bool removeColumns(int column, int count, const QModelIndex& parent = QModelIndex()) override;
|
||||
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||
bool setData(const QModelIndex& index, const QVariant& value, int role) override;
|
||||
@@ -56,20 +61,17 @@ private Q_SLOTS:
|
||||
void setCellData(QModelIndex index, QString str);
|
||||
|
||||
private:
|
||||
void containSheetDataInView();
|
||||
void cellUpdated(App::CellAddress address);
|
||||
void rangeUpdated(const App::Range& range);
|
||||
|
||||
fastsignals::scoped_connection cellUpdatedConnection;
|
||||
fastsignals::scoped_connection rangeUpdatedConnection;
|
||||
std::vector<fastsignals::scoped_connection> connections;
|
||||
Spreadsheet::Sheet* sheet;
|
||||
int rows, cols;
|
||||
QColor aliasBgColor;
|
||||
QColor textFgColor;
|
||||
QColor positiveFgColor;
|
||||
QColor negativeFgColor;
|
||||
|
||||
QVariantList columnLabels, rowLabels;
|
||||
|
||||
static constexpr int maxRowCount = 16384, maxColumnCount = 26 + 26 * 26;
|
||||
};
|
||||
|
||||
} // namespace SpreadsheetGui
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
@@ -47,7 +48,6 @@
|
||||
|
||||
#include "DlgBindSheet.h"
|
||||
#include "DlgSheetConf.h"
|
||||
#include "LineEdit.h"
|
||||
#include "PropertiesDialog.h"
|
||||
#include "SheetTableView.h"
|
||||
|
||||
@@ -56,6 +56,31 @@ using namespace SpreadsheetGui;
|
||||
using namespace Spreadsheet;
|
||||
using namespace App;
|
||||
|
||||
/// Gathers adjacent columns/rows into ranges. Returns the ranges in descending order.
|
||||
static std::vector<std::pair<int, int>> selectionRanges(
|
||||
const QModelIndexList& selection,
|
||||
Qt::Orientation orientation
|
||||
)
|
||||
{
|
||||
std::vector<int> values;
|
||||
for (const auto& index : selection) {
|
||||
values.emplace_back(orientation == Qt::Horizontal ? index.column() : index.row());
|
||||
}
|
||||
std::ranges::sort(values, std::greater<>());
|
||||
|
||||
std::vector<std::pair<int, int>> ranges;
|
||||
for (int value : values) {
|
||||
if (ranges.empty() || value < ranges.back().first - 1) {
|
||||
ranges.emplace_back(value, value);
|
||||
}
|
||||
else {
|
||||
ranges.back().first = value;
|
||||
}
|
||||
}
|
||||
|
||||
return ranges;
|
||||
}
|
||||
|
||||
void SheetViewHeader::mouseMoveEvent(QMouseEvent* e)
|
||||
{
|
||||
// for some reason QWidget::setCursor() has no effect in QGraphicsView
|
||||
@@ -95,30 +120,6 @@ bool SheetViewHeader::viewportEvent(QEvent* e)
|
||||
return QHeaderView::viewportEvent(e);
|
||||
}
|
||||
|
||||
static std::pair<int, int> selectedMinMaxRows(QModelIndexList list)
|
||||
{
|
||||
int min = std::numeric_limits<int>::max();
|
||||
int max = 0;
|
||||
for (const auto& item : list) {
|
||||
int row = item.row();
|
||||
min = std::min(row, min);
|
||||
max = std::max(row, max);
|
||||
}
|
||||
return {min, max};
|
||||
}
|
||||
|
||||
static std::pair<int, int> selectedMinMaxColumns(QModelIndexList list)
|
||||
{
|
||||
int min = std::numeric_limits<int>::max();
|
||||
int max = 0;
|
||||
for (const auto& item : list) {
|
||||
int column = item.column();
|
||||
min = std::min(column, min);
|
||||
max = std::max(column, max);
|
||||
}
|
||||
return {min, max};
|
||||
}
|
||||
|
||||
SheetTableView::SheetTableView(QWidget* parent)
|
||||
: QTableView(parent)
|
||||
, sheet(nullptr)
|
||||
@@ -132,23 +133,17 @@ SheetTableView::SheetTableView(QWidget* parent)
|
||||
connect(verticalHeader(), &QWidget::customContextMenuRequested, [this](const QPoint& point) {
|
||||
Q_UNUSED(point)
|
||||
QMenu menu {nullptr};
|
||||
const auto selection = selectionModel()->selectedRows();
|
||||
const auto& [min, max] = selectedMinMaxRows(selection);
|
||||
if (bool isContiguous = max - min == selection.size() - 1) {
|
||||
Q_UNUSED(isContiguous)
|
||||
/*: This is shown in the context menu for the vertical header in a spreadsheet.
|
||||
The number refers to how many lines are selected and will be inserted. */
|
||||
const auto& selection = selectionModel()->selectedRows();
|
||||
const auto& ranges = selectionRanges(selection, Qt::Vertical);
|
||||
if (ranges.size() <= 1) {
|
||||
auto insertBefore = menu.addAction(tr("Insert %n Rows Above", "", selection.size()));
|
||||
connect(insertBefore, &QAction::triggered, this, &SheetTableView::insertRows);
|
||||
|
||||
if (max < model()->rowCount() - 1) {
|
||||
auto insertAfter = menu.addAction(tr("Insert %n Rows Below", "", selection.size()));
|
||||
connect(insertAfter, &QAction::triggered, this, &SheetTableView::insertRowsAfter);
|
||||
}
|
||||
connect(insertBefore, &QAction::triggered, [this] { insertRows(false); });
|
||||
auto insertAfter = menu.addAction(tr("Insert %n Rows Below", "", selection.size()));
|
||||
connect(insertAfter, &QAction::triggered, [this] { insertRows(true); });
|
||||
}
|
||||
else {
|
||||
auto insert = menu.addAction(tr("Insert %n Non-Contiguous Rows", "", selection.size()));
|
||||
connect(insert, &QAction::triggered, this, &SheetTableView::insertRows);
|
||||
connect(insert, &QAction::triggered, [this] { insertRows(false); });
|
||||
}
|
||||
auto remove = menu.addAction(tr("Remove Rows", ""));
|
||||
connect(remove, &QAction::triggered, this, &SheetTableView::removeRows);
|
||||
@@ -158,23 +153,17 @@ SheetTableView::SheetTableView(QWidget* parent)
|
||||
connect(horizontalHeader(), &QWidget::customContextMenuRequested, [this](const QPoint& point) {
|
||||
Q_UNUSED(point)
|
||||
QMenu menu {nullptr};
|
||||
const auto selection = selectionModel()->selectedColumns();
|
||||
const auto& [min, max] = selectedMinMaxColumns(selection);
|
||||
if (bool isContiguous = max - min == selection.size() - 1) {
|
||||
Q_UNUSED(isContiguous)
|
||||
/*: This is shown in the context menu for the horizontal header in a spreadsheet.
|
||||
The number refers to how many lines are selected and will be inserted. */
|
||||
const auto& selection = selectionModel()->selectedColumns();
|
||||
const auto& ranges = selectionRanges(selection, Qt::Horizontal);
|
||||
if (ranges.size() <= 1) {
|
||||
auto insertAbove = menu.addAction(tr("Insert %n Columns Left", "", selection.size()));
|
||||
connect(insertAbove, &QAction::triggered, this, &SheetTableView::insertColumns);
|
||||
|
||||
if (max < model()->columnCount() - 1) {
|
||||
auto insertAfter = menu.addAction(tr("Insert %n Columns Right", "", selection.size()));
|
||||
connect(insertAfter, &QAction::triggered, this, &SheetTableView::insertColumnsAfter);
|
||||
}
|
||||
connect(insertAbove, &QAction::triggered, [this] { insertColumns(false); });
|
||||
auto insertAfter = menu.addAction(tr("Insert %n Columns Right", "", selection.size()));
|
||||
connect(insertAfter, &QAction::triggered, [this] { insertColumns(true); });
|
||||
}
|
||||
else {
|
||||
auto insert = menu.addAction(tr("Insert %n Non-Contiguous Columns", "", selection.size()));
|
||||
connect(insert, &QAction::triggered, this, &SheetTableView::insertColumns);
|
||||
connect(insert, &QAction::triggered, [this] { insertColumns(false); });
|
||||
}
|
||||
auto remove = menu.addAction(tr("Remove Columns", ""));
|
||||
connect(remove, &QAction::triggered, this, &SheetTableView::removeColumns);
|
||||
@@ -328,151 +317,55 @@ QModelIndexList SheetTableView::selectedIndexesRaw() const
|
||||
return selectedIndexes();
|
||||
}
|
||||
|
||||
void SheetTableView::insertRows()
|
||||
void SheetTableView::insertRows(bool after)
|
||||
{
|
||||
assert(sheet);
|
||||
|
||||
QModelIndexList rows = selectionModel()->selectedRows();
|
||||
std::vector<int> sortedRows;
|
||||
|
||||
/* Make sure rows are sorted in ascending order */
|
||||
for (const auto& it : rows) {
|
||||
sortedRows.push_back(it.row());
|
||||
}
|
||||
std::sort(sortedRows.begin(), sortedRows.end());
|
||||
|
||||
/* Insert rows */
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Insert Rows"));
|
||||
std::vector<int>::const_reverse_iterator it = sortedRows.rbegin();
|
||||
while (it != sortedRows.rend()) {
|
||||
int prev = *it;
|
||||
int count = 1;
|
||||
|
||||
/* Collect neighbouring rows into one chunk */
|
||||
++it;
|
||||
while (it != sortedRows.rend()) {
|
||||
if (*it == prev - 1) {
|
||||
prev = *it;
|
||||
++count;
|
||||
++it;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
for (const auto& [begin, end] : selectionRanges(selectionModel()->selectedRows(), Qt::Vertical)) {
|
||||
if (!model()->insertRows(after ? end + 1 : begin, end - begin + 1)) {
|
||||
Gui::Command::abortCommand();
|
||||
return;
|
||||
}
|
||||
|
||||
Gui::cmdAppObjectArgs(sheet, "insertRows('%s', %d)", rowName(prev).c_str(), count);
|
||||
}
|
||||
Gui::Command::commitCommand();
|
||||
Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.recompute()");
|
||||
}
|
||||
|
||||
void SheetTableView::insertRowsAfter()
|
||||
void SheetTableView::insertColumns(bool after)
|
||||
{
|
||||
assert(sheet);
|
||||
const auto rows = selectionModel()->selectedRows();
|
||||
const auto& [min, max] = selectedMinMaxRows(rows);
|
||||
assert(max - min == rows.size() - 1);
|
||||
Q_UNUSED(min)
|
||||
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Insert Rows"));
|
||||
Gui::cmdAppObjectArgs(sheet, "insertRows('%s', %d)", rowName(max + 1).c_str(), rows.size());
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Insert Columns"));
|
||||
for (const auto& [begin, end] :
|
||||
selectionRanges(selectionModel()->selectedColumns(), Qt::Horizontal)) {
|
||||
if (!model()->insertColumns(after ? end + 1 : begin, end - begin + 1)) {
|
||||
Gui::Command::abortCommand();
|
||||
return;
|
||||
}
|
||||
}
|
||||
Gui::Command::commitCommand();
|
||||
Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.recompute()");
|
||||
}
|
||||
|
||||
void SheetTableView::removeRows()
|
||||
{
|
||||
assert(sheet);
|
||||
|
||||
QModelIndexList rows = selectionModel()->selectedRows();
|
||||
std::vector<int> sortedRows;
|
||||
|
||||
/* Make sure rows are sorted in descending order */
|
||||
for (const auto& it : rows) {
|
||||
sortedRows.push_back(it.row());
|
||||
}
|
||||
std::sort(sortedRows.begin(), sortedRows.end(), std::greater<>());
|
||||
|
||||
/* Remove rows */
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Remove Rows"));
|
||||
for (const auto& it : sortedRows) {
|
||||
Gui::cmdAppObjectArgs(sheet, "removeRows('%s', %d)", rowName(it).c_str(), 1);
|
||||
}
|
||||
Gui::Command::commitCommand();
|
||||
Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.recompute()");
|
||||
}
|
||||
|
||||
void SheetTableView::insertColumns()
|
||||
{
|
||||
assert(sheet);
|
||||
|
||||
QModelIndexList cols = selectionModel()->selectedColumns();
|
||||
std::vector<int> sortedColumns;
|
||||
|
||||
/* Make sure rows are sorted in ascending order */
|
||||
for (const auto& it : cols) {
|
||||
sortedColumns.push_back(it.column());
|
||||
}
|
||||
std::sort(sortedColumns.begin(), sortedColumns.end());
|
||||
|
||||
/* Insert columns */
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Insert Columns"));
|
||||
std::vector<int>::const_reverse_iterator it = sortedColumns.rbegin();
|
||||
while (it != sortedColumns.rend()) {
|
||||
int prev = *it;
|
||||
int count = 1;
|
||||
|
||||
/* Collect neighbouring columns into one chunk */
|
||||
++it;
|
||||
while (it != sortedColumns.rend()) {
|
||||
if (*it == prev - 1) {
|
||||
prev = *it;
|
||||
++count;
|
||||
++it;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
for (const auto& [begin, end] : selectionRanges(selectionModel()->selectedRows(), Qt::Vertical)) {
|
||||
if (!model()->removeRows(begin, end - begin + 1)) {
|
||||
Gui::Command::abortCommand();
|
||||
return;
|
||||
}
|
||||
|
||||
Gui::cmdAppObjectArgs(sheet, "insertColumns('%s', %d)", columnName(prev).c_str(), count);
|
||||
}
|
||||
Gui::Command::commitCommand();
|
||||
Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.recompute()");
|
||||
}
|
||||
|
||||
void SheetTableView::insertColumnsAfter()
|
||||
{
|
||||
assert(sheet);
|
||||
const auto columns = selectionModel()->selectedColumns();
|
||||
const auto& [min, max] = selectedMinMaxColumns(columns);
|
||||
assert(max - min == columns.size() - 1);
|
||||
Q_UNUSED(min)
|
||||
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Insert Columns"));
|
||||
Gui::cmdAppObjectArgs(sheet, "insertColumns('%s', %d)", columnName(max + 1).c_str(), columns.size());
|
||||
Gui::Command::commitCommand();
|
||||
Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.recompute()");
|
||||
}
|
||||
|
||||
void SheetTableView::removeColumns()
|
||||
{
|
||||
assert(sheet);
|
||||
|
||||
QModelIndexList cols = selectionModel()->selectedColumns();
|
||||
std::vector<int> sortedColumns;
|
||||
|
||||
/* Make sure rows are sorted in descending order */
|
||||
for (const auto& it : cols) {
|
||||
sortedColumns.push_back(it.column());
|
||||
}
|
||||
std::sort(sortedColumns.begin(), sortedColumns.end(), std::greater<>());
|
||||
|
||||
/* Remove columns */
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Remove Rows"));
|
||||
for (const auto& it : sortedColumns) {
|
||||
Gui::cmdAppObjectArgs(sheet, "removeColumns('%s', %d)", columnName(it).c_str(), 1);
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Remove Columns"));
|
||||
for (const auto& [begin, end] :
|
||||
selectionRanges(selectionModel()->selectedColumns(), Qt::Horizontal)) {
|
||||
if (!model()->removeColumns(begin, end - begin + 1)) {
|
||||
Gui::Command::abortCommand();
|
||||
return;
|
||||
}
|
||||
}
|
||||
Gui::Command::commitCommand();
|
||||
Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.recompute()");
|
||||
|
||||
@@ -83,18 +83,17 @@ public Q_SLOTS:
|
||||
protected Q_SLOTS:
|
||||
void commitData(QWidget* editor) override;
|
||||
void updateCellSpan();
|
||||
void insertRows();
|
||||
void insertRowsAfter();
|
||||
void removeRows();
|
||||
void insertColumns();
|
||||
void insertColumnsAfter();
|
||||
void removeColumns();
|
||||
void cellProperties();
|
||||
void onRecompute();
|
||||
void onBind();
|
||||
void onConfSetup();
|
||||
|
||||
protected:
|
||||
void insertRows(bool after);
|
||||
void insertColumns(bool after);
|
||||
void removeRows();
|
||||
void removeColumns();
|
||||
|
||||
bool edit(const QModelIndex& index, EditTrigger trigger, QEvent* event) override;
|
||||
bool event(QEvent* event) override;
|
||||
void closeEditor(QWidget* editor, QAbstractItemDelegate::EndEditHint hint) override;
|
||||
|
||||
@@ -186,20 +186,20 @@ void ZoomableView::updateView(void)
|
||||
/* QGraphicsView has hardcoded margins therefore we have to avoid fitInView
|
||||
* Find more information at https://bugreports.qt.io/browse/QTBUG-42331 */
|
||||
|
||||
const qreal scale_factor = static_cast<qreal>(m_zoomLevel) / 100.0,
|
||||
new_w = static_cast<qreal>(viewport()->rect().width()) / scale_factor,
|
||||
new_h = static_cast<qreal>(viewport()->rect().height()) / scale_factor;
|
||||
const qreal scale_factor = static_cast<qreal>(m_zoomLevel) / 100.0;
|
||||
const qreal new_w = static_cast<qreal>(viewport()->rect().width()) / scale_factor;
|
||||
const qreal new_h = static_cast<qreal>(viewport()->rect().height()) / scale_factor;
|
||||
|
||||
const QRectF new_geometry {0.0, 0.0, new_w, new_h};
|
||||
const QRectF new_geometry_f {0.0, 0.0, new_w, new_h};
|
||||
const QRect new_geometry = new_geometry_f.toRect();
|
||||
|
||||
const QRect old_geometry {stv->geometry()};
|
||||
stv->setGeometry(1, 1, old_geometry.width() - 1, old_geometry.height() - 1);
|
||||
stv->setGeometry(1, 1, new_geometry.width() - 1, new_geometry.height() - 1);
|
||||
|
||||
resetTransform();
|
||||
qpw->setGeometry(new_geometry);
|
||||
setSceneRect(new_geometry);
|
||||
qpw->setGeometry(new_geometry_f);
|
||||
setSceneRect(new_geometry_f);
|
||||
scale(scale_factor, scale_factor);
|
||||
centerOn(new_geometry.center());
|
||||
centerOn(new_geometry_f.center());
|
||||
}
|
||||
|
||||
void ZoomableView::focusOutEvent(QFocusEvent* event)
|
||||
|
||||
@@ -1873,6 +1873,15 @@ class SpreadsheetCases(unittest.TestCase):
|
||||
self.assertEqual(sheet.getContents("A1"), "'36C")
|
||||
self.assertEqual(sheet.get("A1"), "36C")
|
||||
|
||||
def testDistantCell(self):
|
||||
sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")
|
||||
sheet.set("ZX12345", "5")
|
||||
sheet.set("A1", "=ZX12345")
|
||||
|
||||
self.doc.recompute()
|
||||
|
||||
self.assertEqual(sheet.A1, 5)
|
||||
|
||||
def testVectorFunctions(self):
|
||||
sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user