Merge pull request #5099 from chennes/spreadsheetAutoReturn
[Spreadsheet] Refactor keyboard handling
This commit is contained in:
@@ -982,6 +982,14 @@ void PropertySheet::getSpans(CellAddress address, int & rows, int & cols) const
|
||||
}
|
||||
}
|
||||
|
||||
App::CellAddress Spreadsheet::PropertySheet::getAnchor(App::CellAddress address) const
|
||||
{
|
||||
if (auto anchor = mergedCells.find(address); anchor != mergedCells.end())
|
||||
return anchor->second;
|
||||
else
|
||||
return address;
|
||||
}
|
||||
|
||||
bool PropertySheet::isMergedCell(CellAddress address) const
|
||||
{
|
||||
return mergedCells.find(address) != mergedCells.end();
|
||||
|
||||
@@ -148,6 +148,8 @@ public:
|
||||
|
||||
void getSpans(App::CellAddress address, int &rows, int &cols) const;
|
||||
|
||||
App::CellAddress getAnchor(App::CellAddress address) const;
|
||||
|
||||
bool isMergedCell(App::CellAddress address) const;
|
||||
|
||||
bool isHidden(App::CellAddress address) const;
|
||||
|
||||
@@ -1063,6 +1063,11 @@ bool Sheet::isMergedCell(CellAddress address) const
|
||||
return cells.isMergedCell(address);
|
||||
}
|
||||
|
||||
App::CellAddress Spreadsheet::Sheet::getAnchor(App::CellAddress address) const
|
||||
{
|
||||
return cells.getAnchor(address);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set column with of column \a col to \a width-
|
||||
* @param col Index of column.
|
||||
|
||||
@@ -110,6 +110,8 @@ public:
|
||||
|
||||
bool isMergedCell(App::CellAddress address) const;
|
||||
|
||||
App::CellAddress getAnchor(App::CellAddress address) const;
|
||||
|
||||
void setColumnWidth(int col, int width);
|
||||
|
||||
int getColumnWidth(int col) const;
|
||||
|
||||
@@ -26,68 +26,58 @@
|
||||
# include <QKeyEvent>
|
||||
#endif
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include "LineEdit.h"
|
||||
|
||||
using namespace SpreadsheetGui;
|
||||
|
||||
LineEdit::LineEdit(QWidget *parent)
|
||||
: Gui::ExpressionLineEdit(parent, false, true)
|
||||
, current()
|
||||
, deltaCol(0)
|
||||
, deltaRow(0)
|
||||
, lastKeyPressed(0)
|
||||
, lastModifiers(0)
|
||||
{
|
||||
setFocusPolicy(Qt::FocusPolicy::ClickFocus);
|
||||
}
|
||||
|
||||
bool LineEdit::eventFilter(QObject* object, QEvent* event)
|
||||
{
|
||||
if (event && event->type() == QEvent::KeyPress) {
|
||||
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
|
||||
if (keyEvent->key() == Qt::Key_Tab) {
|
||||
// Special tab handling -- must be done via a QApplication event filter, otherwise the widget
|
||||
// system will always grab the tab events
|
||||
if (completerActive()) {
|
||||
hideCompleter();
|
||||
event->accept();
|
||||
return true; // To make sure this tab press doesn't do anything else
|
||||
}
|
||||
else {
|
||||
lastKeyPressed = keyEvent->key();
|
||||
lastModifiers = keyEvent->modifiers();
|
||||
}
|
||||
}
|
||||
}
|
||||
return false; // We don't usually actually "handle" the tab event, we just keep track of it
|
||||
}
|
||||
|
||||
bool LineEdit::event(QEvent *event)
|
||||
{
|
||||
if (event && event->type() == QEvent::KeyPress) {
|
||||
if (event && event->type() == QEvent::FocusIn) {
|
||||
qApp->installEventFilter(this);
|
||||
}
|
||||
else if (event && event->type() == QEvent::FocusOut) {
|
||||
qApp->removeEventFilter(this);
|
||||
if (lastKeyPressed)
|
||||
Q_EMIT finishedWithKey(lastKeyPressed, lastModifiers);
|
||||
lastKeyPressed = 0;
|
||||
}
|
||||
else if (event && event->type() == QEvent::KeyPress && !completerActive()) {
|
||||
QKeyEvent * kevent = static_cast<QKeyEvent*>(event);
|
||||
|
||||
if (kevent->key() == Qt::Key_Tab) {
|
||||
if (kevent->modifiers() == 0) {
|
||||
deltaCol = 1;
|
||||
deltaRow = 0;
|
||||
Q_EMIT returnPressed();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (kevent->key() == Qt::Key_Backtab) {
|
||||
if (kevent->modifiers() == Qt::ShiftModifier) {
|
||||
deltaCol = -1;
|
||||
deltaRow = 0;
|
||||
Q_EMIT returnPressed();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (kevent->key() == Qt::Key_Enter || kevent->key() == Qt::Key_Return) {
|
||||
if (kevent->modifiers() == 0) {
|
||||
deltaCol = 0;
|
||||
deltaRow = 1;
|
||||
Q_EMIT returnPressed();
|
||||
return true;
|
||||
}
|
||||
else if (kevent->modifiers() == Qt::ShiftModifier) {
|
||||
deltaCol = 0;
|
||||
deltaRow = -1;
|
||||
Q_EMIT returnPressed();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
lastKeyPressed = kevent->key();
|
||||
lastModifiers = kevent->modifiers();
|
||||
}
|
||||
return Gui::ExpressionLineEdit::event(event);
|
||||
}
|
||||
|
||||
void LineEdit::setIndex(QModelIndex _current)
|
||||
{
|
||||
current = _current;
|
||||
}
|
||||
|
||||
QModelIndex LineEdit::next() const
|
||||
{
|
||||
const QAbstractItemModel * m = current.model();
|
||||
|
||||
return m->index(qMin(qMax(0, current.row() + deltaRow), m->rowCount() - 1 ),
|
||||
qMin(qMax(0, current.column() + deltaCol), m->columnCount() - 1 ) );
|
||||
}
|
||||
|
||||
#include "moc_LineEdit.cpp"
|
||||
|
||||
@@ -36,13 +36,17 @@ public:
|
||||
explicit LineEdit(QWidget *parent = 0);
|
||||
|
||||
bool event(QEvent *event);
|
||||
void setIndex(QModelIndex _current);
|
||||
QModelIndex next() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void finishedWithKey(int key, Qt::KeyboardModifiers modifiers);
|
||||
|
||||
private:
|
||||
QModelIndex current;
|
||||
int deltaCol;
|
||||
int deltaRow;
|
||||
bool eventFilter(QObject* object, QEvent* event);
|
||||
|
||||
|
||||
private:
|
||||
int lastKeyPressed;
|
||||
Qt::KeyboardModifiers lastModifiers;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -101,6 +101,7 @@ static std::pair<int, int> selectedMinMaxColumns(QModelIndexList list)
|
||||
SheetTableView::SheetTableView(QWidget *parent)
|
||||
: QTableView(parent)
|
||||
, sheet(0)
|
||||
, tabCounter(0)
|
||||
{
|
||||
setHorizontalHeader(new SheetViewHeader(this,Qt::Horizontal));
|
||||
setVerticalHeader(new SheetViewHeader(this,Qt::Vertical));
|
||||
@@ -319,7 +320,7 @@ void SheetTableView::insertColumnsAfter()
|
||||
{
|
||||
assert(sheet != 0);
|
||||
const auto columns = selectionModel()->selectedColumns();
|
||||
const auto & [min, max] = selectedMinMaxColumns(columns);
|
||||
const auto& [min, max] = selectedMinMaxColumns(columns);
|
||||
assert(max - min == columns.size() - 1);
|
||||
Q_UNUSED(min)
|
||||
|
||||
@@ -345,7 +346,7 @@ void SheetTableView::removeColumns()
|
||||
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Remove rows"));
|
||||
for (std::vector<int>::const_iterator it = sortedColumns.begin(); it != sortedColumns.end(); ++it)
|
||||
Gui::cmdAppObjectArgs(sheet, "removeColumns('%s', %d)",
|
||||
columnName(*it).c_str(), 1);
|
||||
columnName(*it).c_str(), 1);
|
||||
Gui::Command::commitCommand();
|
||||
Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.recompute()");
|
||||
}
|
||||
@@ -365,7 +366,7 @@ void SheetTableView::updateCellSpan(CellAddress address)
|
||||
setSpan(address.row(), address.col(), rows, cols);
|
||||
}
|
||||
|
||||
void SheetTableView::setSheet(Sheet * _sheet)
|
||||
void SheetTableView::setSheet(Sheet* _sheet)
|
||||
{
|
||||
sheet = _sheet;
|
||||
cellSpanChangedConnection = sheet->cellSpanChanged.connect(bind(&SheetTableView::updateCellSpan, this, bp::_1));
|
||||
@@ -399,57 +400,45 @@ void SheetTableView::setSheet(Sheet * _sheet)
|
||||
|
||||
}
|
||||
|
||||
void SheetTableView::commitData ( QWidget * editor )
|
||||
void SheetTableView::commitData(QWidget* editor)
|
||||
{
|
||||
QTableView::commitData(editor);
|
||||
}
|
||||
|
||||
bool SheetTableView::edit ( const QModelIndex & index, EditTrigger trigger, QEvent * event )
|
||||
bool SheetTableView::edit(const QModelIndex& index, EditTrigger trigger, QEvent* event)
|
||||
{
|
||||
if (trigger & (QAbstractItemView::DoubleClicked | QAbstractItemView::AnyKeyPressed | QAbstractItemView::EditKeyPressed) )
|
||||
if (trigger & (QAbstractItemView::DoubleClicked | QAbstractItemView::AnyKeyPressed | QAbstractItemView::EditKeyPressed))
|
||||
currentEditIndex = index;
|
||||
return QTableView::edit(index, trigger, event);
|
||||
}
|
||||
|
||||
bool SheetTableView::event(QEvent *event)
|
||||
bool SheetTableView::event(QEvent* event)
|
||||
{
|
||||
/* Catch key presses for navigating the table; Enter/Return (+Shift), and Tab (+Shift) */
|
||||
if (event && event->type() == QEvent::KeyPress) {
|
||||
QKeyEvent * kevent = static_cast<QKeyEvent*>(event);
|
||||
|
||||
if (kevent->key() == Qt::Key_Tab) {
|
||||
QModelIndex c = currentIndex();
|
||||
|
||||
if (kevent->modifiers() == 0) {
|
||||
setCurrentIndex(model()->index(c.row(), qMin(c.column() + 1, model()->columnCount() -1)));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (kevent->key() == Qt::Key_Backtab) {
|
||||
QModelIndex c = currentIndex();
|
||||
|
||||
if (kevent->modifiers() == Qt::ShiftModifier) {
|
||||
setCurrentIndex(model()->index(c.row(), qMax(c.column() - 1, 0)));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (kevent->key() == Qt::Key_Enter || kevent->key() == Qt::Key_Return) {
|
||||
QModelIndex c = currentIndex();
|
||||
|
||||
if (kevent->modifiers() == 0) {
|
||||
setCurrentIndex(model()->index(qMin(c.row() + 1, model()->rowCount() - 1), c.column()));
|
||||
return true;
|
||||
}
|
||||
else if (kevent->modifiers() == Qt::ShiftModifier) {
|
||||
setCurrentIndex(model()->index(qMax(c.row() - 1, 0), c.column()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (kevent->key() == Qt::Key_Delete) {
|
||||
if (event && event->type() == QEvent::KeyPress && this->hasFocus()) {
|
||||
// If this widget has focus, look for keyboard events that represent movement shortcuts
|
||||
// and handle them.
|
||||
QKeyEvent* kevent = static_cast<QKeyEvent*>(event);
|
||||
switch (kevent->key()) {
|
||||
case Qt::Key_Return: [[fallthrough]];
|
||||
case Qt::Key_Enter: [[fallthrough]];
|
||||
case Qt::Key_Home: [[fallthrough]];
|
||||
case Qt::Key_End: [[fallthrough]];
|
||||
case Qt::Key_Left: [[fallthrough]];
|
||||
case Qt::Key_Right: [[fallthrough]];
|
||||
case Qt::Key_Up: [[fallthrough]];
|
||||
case Qt::Key_Down: [[fallthrough]];
|
||||
case Qt::Key_Tab: [[fallthrough]];
|
||||
case Qt::Key_Backtab:
|
||||
finishEditWithMove(kevent->key(), kevent->modifiers(), true);
|
||||
return true;
|
||||
// Also handle the delete key here:
|
||||
case Qt::Key_Delete:
|
||||
deleteSelection();
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
else if (kevent->matches(QKeySequence::Cut)) {
|
||||
if (kevent->matches(QKeySequence::Cut)) {
|
||||
cutSelection();
|
||||
return true;
|
||||
}
|
||||
@@ -468,18 +457,19 @@ bool SheetTableView::event(QEvent *event)
|
||||
kevent->modifiers() == Qt::ShiftModifier ||
|
||||
kevent->modifiers() == Qt::KeypadModifier) {
|
||||
switch (kevent->key()) {
|
||||
case Qt::Key_Return:
|
||||
case Qt::Key_Enter:
|
||||
case Qt::Key_Delete:
|
||||
case Qt::Key_Home:
|
||||
case Qt::Key_End:
|
||||
case Qt::Key_Backspace:
|
||||
case Qt::Key_Left:
|
||||
case Qt::Key_Right:
|
||||
case Qt::Key_Up:
|
||||
case Qt::Key_Down:
|
||||
case Qt::Key_Return: [[fallthrough]];
|
||||
case Qt::Key_Enter: [[fallthrough]];
|
||||
case Qt::Key_Delete: [[fallthrough]];
|
||||
case Qt::Key_Home: [[fallthrough]];
|
||||
case Qt::Key_End: [[fallthrough]];
|
||||
case Qt::Key_Backspace: [[fallthrough]];
|
||||
case Qt::Key_Left: [[fallthrough]];
|
||||
case Qt::Key_Right: [[fallthrough]];
|
||||
case Qt::Key_Up: [[fallthrough]];
|
||||
case Qt::Key_Down: [[fallthrough]];
|
||||
case Qt::Key_Tab:
|
||||
kevent->accept();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -614,13 +604,239 @@ void SheetTableView::pasteClipboard()
|
||||
}
|
||||
}
|
||||
|
||||
void SheetTableView::closeEditor(QWidget * editor, QAbstractItemDelegate::EndEditHint hint)
|
||||
void SheetTableView::finishEditWithMove(int keyPressed, Qt::KeyboardModifiers modifiers, bool handleTabMotion)
|
||||
{
|
||||
SpreadsheetGui::LineEdit * le = qobject_cast<SpreadsheetGui::LineEdit*>(editor);
|
||||
// A utility lambda for finding the beginning and ending of data regions
|
||||
auto scanForRegionBoundary = [this](int& r, int& c, int dr, int dc) {
|
||||
auto startAddress = CellAddress(r, c);
|
||||
auto startCell = sheet->getCell(startAddress);
|
||||
bool startedAtEmptyCell = startCell ? !startCell->isUsed() : true;
|
||||
const int maxRow = this->model()->rowCount() - 1;
|
||||
const int maxCol = this->model()->columnCount() - 1;
|
||||
while (c + dc >= 0 && r + dr >= 0 && c + dc <= maxCol && r + dr <= maxRow) {
|
||||
r += dr;
|
||||
c += dc;
|
||||
auto cell = sheet->getCell(CellAddress(r, c));
|
||||
auto cellIsEmpty = cell ? !cell->isUsed() : true;
|
||||
if (cellIsEmpty && !startedAtEmptyCell) {
|
||||
// Don't stop at the empty cell, stop at the last non-empty cell
|
||||
r -= dr;
|
||||
c -= dc;
|
||||
break;
|
||||
}
|
||||
else if (!cellIsEmpty && startedAtEmptyCell) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (r == startAddress.row() && c == startAddress.col()) {
|
||||
// Always move at least one cell:
|
||||
r += dr;
|
||||
c += dc;
|
||||
}
|
||||
r = std::max(0, std::min(r, maxRow));
|
||||
c = std::max(0, std::min(c, maxCol));
|
||||
};
|
||||
|
||||
currentEditIndex = QModelIndex();
|
||||
int targetRow = currentIndex().row();
|
||||
int targetColumn = currentIndex().column();
|
||||
int colSpan;
|
||||
int rowSpan;
|
||||
sheet->getSpans(CellAddress(targetRow, targetColumn), rowSpan, colSpan);
|
||||
switch (keyPressed) {
|
||||
case Qt::Key_Return:
|
||||
case Qt::Key_Enter:
|
||||
if (modifiers == Qt::NoModifier) {
|
||||
targetRow += rowSpan;
|
||||
targetColumn -= tabCounter;
|
||||
}
|
||||
else if (modifiers == Qt::ShiftModifier) {
|
||||
targetRow -= 1;
|
||||
targetColumn -= tabCounter;
|
||||
}
|
||||
else {
|
||||
// For an unrecognized modifier, just go down
|
||||
targetRow += rowSpan;
|
||||
}
|
||||
tabCounter = 0;
|
||||
break;
|
||||
|
||||
case Qt::Key_Home:
|
||||
// Home: row 1, same column
|
||||
// Ctrl-Home: row 1, column 1
|
||||
targetRow = 0;
|
||||
if (modifiers == Qt::ControlModifier)
|
||||
targetColumn = 0;
|
||||
tabCounter = 0;
|
||||
break;
|
||||
|
||||
case Qt::Key_End:
|
||||
{
|
||||
// End should take you to the last occupied cell in the current column
|
||||
// Ctrl-End takes you to the last cell in the sheet
|
||||
auto usedCells = sheet->getCells()->getUsedCells();
|
||||
for (const auto& cell : usedCells) {
|
||||
if (modifiers == Qt::NoModifier) {
|
||||
if (cell.col() == targetColumn)
|
||||
targetRow = std::max(targetRow, cell.row());
|
||||
}
|
||||
else if (modifiers == Qt::ControlModifier) {
|
||||
targetRow = std::max(targetRow, cell.row());
|
||||
targetColumn = std::max(targetColumn, cell.col());
|
||||
}
|
||||
}
|
||||
tabCounter = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case Qt::Key_Left:
|
||||
if (targetColumn == 0)
|
||||
break; // Nothing to do, we're already in the first column
|
||||
if (modifiers == Qt::NoModifier || modifiers == Qt::ShiftModifier)
|
||||
targetColumn--;
|
||||
else if (modifiers == Qt::ControlModifier ||
|
||||
modifiers == (Qt::ControlModifier | Qt::ShiftModifier))
|
||||
scanForRegionBoundary(targetRow, targetColumn, 0, -1);
|
||||
else
|
||||
targetColumn--; //Unrecognized modifier combination: default to just moving one cell
|
||||
tabCounter = 0;
|
||||
break;
|
||||
case Qt::Key_Right:
|
||||
if (targetColumn >= this->model()->columnCount() - 1)
|
||||
break; // Nothing to do, we're already in the last column
|
||||
if (modifiers == Qt::NoModifier || modifiers == Qt::ShiftModifier)
|
||||
targetColumn += colSpan;
|
||||
else if (modifiers == Qt::ControlModifier ||
|
||||
modifiers == (Qt::ControlModifier | Qt::ShiftModifier))
|
||||
scanForRegionBoundary(targetRow, targetColumn, 0, 1);
|
||||
else
|
||||
targetColumn += colSpan; //Unrecognized modifier combination: default to just moving one cell
|
||||
tabCounter = 0;
|
||||
break;
|
||||
case Qt::Key_Up:
|
||||
if (targetRow == 0)
|
||||
break; // Nothing to do, we're already in the first column
|
||||
if (modifiers == Qt::NoModifier || modifiers == Qt::ShiftModifier)
|
||||
targetRow--;
|
||||
else if (modifiers == Qt::ControlModifier ||
|
||||
modifiers == (Qt::ControlModifier | Qt::ShiftModifier))
|
||||
scanForRegionBoundary(targetRow, targetColumn, -1, 0);
|
||||
else
|
||||
targetRow--; //Unrecognized modifier combination: default to just moving one cell
|
||||
tabCounter = 0;
|
||||
break;
|
||||
case Qt::Key_Down:
|
||||
if (targetRow >= this->model()->rowCount() - 1)
|
||||
break; // Nothing to do, we're already in the last row
|
||||
if (modifiers == Qt::NoModifier || modifiers == Qt::ShiftModifier)
|
||||
targetRow += rowSpan;
|
||||
else if (modifiers == Qt::ControlModifier ||
|
||||
modifiers == (Qt::ControlModifier | Qt::ShiftModifier))
|
||||
scanForRegionBoundary(targetRow, targetColumn, 1, 0);
|
||||
else
|
||||
targetRow += rowSpan; //Unrecognized modifier combination: default to just moving one cell
|
||||
tabCounter = 0;
|
||||
break;
|
||||
case Qt::Key_Tab:
|
||||
if (modifiers == Qt::NoModifier) {
|
||||
tabCounter++;
|
||||
if (handleTabMotion)
|
||||
targetColumn += colSpan;
|
||||
}
|
||||
else if (modifiers == Qt::ShiftModifier) {
|
||||
tabCounter = 0;
|
||||
if (handleTabMotion)
|
||||
targetColumn--;
|
||||
}
|
||||
break;
|
||||
case Qt::Key_Backtab:
|
||||
if (modifiers == Qt::NoModifier) {
|
||||
targetColumn--;
|
||||
}
|
||||
tabCounter = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (this->sheet->isMergedCell(CellAddress(targetRow, targetColumn))) {
|
||||
auto anchor = this->sheet->getAnchor(CellAddress(targetRow, targetColumn));
|
||||
targetRow = anchor.row();
|
||||
targetColumn = anchor.col();
|
||||
}
|
||||
|
||||
// Overflow/underflow protection:
|
||||
const int maxRow = this->model()->rowCount() - 1;
|
||||
const int maxCol = this->model()->columnCount() - 1;
|
||||
targetRow = std::max(0, std::min(targetRow, maxRow));
|
||||
targetColumn = std::max(0, std::min(targetColumn, maxCol));
|
||||
|
||||
if (!(modifiers & Qt::ShiftModifier) || keyPressed == Qt::Key_Tab || keyPressed == Qt::Key_Enter || keyPressed == Qt::Key_Return) {
|
||||
// We have to use this method so that Ctrl-modifier combinations don't result in multiple selection
|
||||
this->selectionModel()->setCurrentIndex(model()->index(targetRow, targetColumn),
|
||||
QItemSelectionModel::ClearAndSelect);
|
||||
}
|
||||
else if (modifiers & Qt::ShiftModifier) {
|
||||
// With shift down, this motion becomes a block selection command, rather than just simple motion:
|
||||
ModifyBlockSelection(targetRow, targetColumn);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SheetTableView::ModifyBlockSelection(int targetRow, int targetCol)
|
||||
{
|
||||
int startingRow = currentIndex().row();
|
||||
int startingCol = currentIndex().column();
|
||||
|
||||
// Get the current block selection size:
|
||||
auto selection = this->selectionModel()->selection();
|
||||
for (const auto& range : selection) {
|
||||
if (range.contains(currentIndex())) {
|
||||
// This range contains the current cell, so it's the one we're going to modify (assuming we're at one of the corners)
|
||||
int rangeMinRow = range.top();
|
||||
int rangeMaxRow = range.bottom();
|
||||
int rangeMinCol = range.left();
|
||||
int rangeMaxCol = range.right();
|
||||
if ((startingRow == rangeMinRow || startingRow == rangeMaxRow) &&
|
||||
(startingCol == rangeMinCol || startingCol == rangeMaxCol)) {
|
||||
if (range.contains(model()->index(targetRow, targetCol))) {
|
||||
// If the range already contains the target cell, then we're making the range smaller
|
||||
if (startingRow == rangeMinRow)
|
||||
rangeMinRow = targetRow;
|
||||
if (startingRow == rangeMaxRow)
|
||||
rangeMaxRow = targetRow;
|
||||
if (startingCol == rangeMinCol)
|
||||
rangeMinCol = targetCol;
|
||||
if (startingCol == rangeMaxCol)
|
||||
rangeMaxCol = targetCol;
|
||||
}
|
||||
else {
|
||||
// We're making the range bigger
|
||||
rangeMinRow = std::min(rangeMinRow, targetRow);
|
||||
rangeMaxRow = std::max(rangeMaxRow, targetRow);
|
||||
rangeMinCol = std::min(rangeMinCol, targetCol);
|
||||
rangeMaxCol = std::max(rangeMaxCol, targetCol);
|
||||
}
|
||||
QItemSelection oldRange(range.topLeft(), range.bottomRight());
|
||||
this->selectionModel()->select(oldRange, QItemSelectionModel::Deselect);
|
||||
QItemSelection newRange(model()->index(rangeMinRow, rangeMinCol), model()->index(rangeMaxRow, rangeMaxCol));
|
||||
this->selectionModel()->select(newRange, QItemSelectionModel::Select);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this->selectionModel()->setCurrentIndex(model()->index(targetRow, targetCol), QItemSelectionModel::Current);
|
||||
}
|
||||
|
||||
void SheetTableView::closeEditor(QWidget * editor, QAbstractItemDelegate::EndEditHint hint)
|
||||
{
|
||||
QTableView::closeEditor(editor, hint);
|
||||
setCurrentIndex(le->next());
|
||||
}
|
||||
|
||||
void SheetTableView::mousePressEvent(QMouseEvent* event)
|
||||
{
|
||||
tabCounter = 0;
|
||||
QTableView::mousePressEvent(event);
|
||||
}
|
||||
|
||||
void SheetTableView::edit ( const QModelIndex & index )
|
||||
|
||||
@@ -62,6 +62,9 @@ public:
|
||||
void copySelection();
|
||||
void cutSelection();
|
||||
void pasteClipboard();
|
||||
void finishEditWithMove(int keyPressed, Qt::KeyboardModifiers modifiers, bool handleTabMotion = false);
|
||||
|
||||
void ModifyBlockSelection(int targetRow, int targetColumn);
|
||||
|
||||
protected Q_SLOTS:
|
||||
void commitData(QWidget *editor);
|
||||
@@ -77,8 +80,10 @@ 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);
|
||||
|
||||
QModelIndex currentEditIndex;
|
||||
int tabCounter;
|
||||
Spreadsheet::Sheet * sheet;
|
||||
|
||||
boost::signals2::scoped_connection cellSpanChangedConnection;
|
||||
|
||||
@@ -47,28 +47,11 @@ QWidget *SpreadsheetDelegate::createEditor(QWidget *parent,
|
||||
const QModelIndex &index) const
|
||||
{
|
||||
SpreadsheetGui::LineEdit *editor = new SpreadsheetGui::LineEdit(parent);
|
||||
editor->setIndex(index);
|
||||
|
||||
editor->setDocumentObject(sheet);
|
||||
connect(editor, SIGNAL(returnPressed()), this, SLOT(commitAndCloseEditor()));
|
||||
connect(editor, &SpreadsheetGui::LineEdit::finishedWithKey, this, &SpreadsheetDelegate::on_editorFinishedWithKey);
|
||||
return editor;
|
||||
}
|
||||
|
||||
void SpreadsheetDelegate::commitAndCloseEditor()
|
||||
{
|
||||
Gui::ExpressionLineEdit *editor = qobject_cast<Gui::ExpressionLineEdit *>(sender());
|
||||
if (editor->completerActive()) {
|
||||
editor->hideCompleter();
|
||||
return;
|
||||
}
|
||||
|
||||
// See https://forum.freecadweb.org/viewtopic.php?f=3&t=41694
|
||||
// It looks like the slot commitAndCloseEditor() is not needed any more and even
|
||||
// causes a crash when doing so because the LineEdit is still accessed after its destruction.
|
||||
//Q_EMIT commitData(editor);
|
||||
//Q_EMIT closeEditor(editor);
|
||||
}
|
||||
|
||||
void SpreadsheetDelegate::setEditorData(QWidget *editor,
|
||||
const QModelIndex &index) const
|
||||
{
|
||||
@@ -89,6 +72,11 @@ void SpreadsheetDelegate::setModelData(QWidget *editor,
|
||||
}
|
||||
}
|
||||
|
||||
void SpreadsheetDelegate::on_editorFinishedWithKey(int key, Qt::KeyboardModifiers modifiers)
|
||||
{
|
||||
Q_EMIT finishedWithKey(key, modifiers);
|
||||
}
|
||||
|
||||
QSize SpreadsheetDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const
|
||||
{
|
||||
Q_UNUSED(option);
|
||||
|
||||
@@ -45,8 +45,10 @@ public:
|
||||
const QModelIndex &index) const;
|
||||
|
||||
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
||||
Q_SIGNALS:
|
||||
void finishedWithKey(int key, Qt::KeyboardModifiers modifiers);
|
||||
private Q_SLOTS:
|
||||
void commitAndCloseEditor();
|
||||
void on_editorFinishedWithKey(int key, Qt::KeyboardModifiers modifiers);
|
||||
private:
|
||||
Spreadsheet::Sheet * sheet;
|
||||
};
|
||||
|
||||
@@ -98,9 +98,12 @@ SheetView::SheetView(Gui::Document *pcDocument, App::DocumentObject *docObj, QWi
|
||||
connect(ui->cells->verticalHeader(), SIGNAL(sectionResized ( int, int, int ) ),
|
||||
this, SLOT(rowResized(int, int, int)));
|
||||
|
||||
connect(ui->cellContent, SIGNAL(returnPressed()), this, SLOT( editingFinished() ));
|
||||
connect(ui->cellAlias, SIGNAL(returnPressed()), this, SLOT( editingFinished() ));
|
||||
connect(ui->cellAlias, SIGNAL(textEdited(QString)), this, SLOT(aliasChanged(QString)));
|
||||
connect(delegate, &SpreadsheetDelegate::finishedWithKey, this, &SheetView::editingFinishedWithKey);
|
||||
connect(ui->cellContent, &LineEdit::finishedWithKey, this, [this](int, Qt::KeyboardModifiers) {confirmContentChanged(ui->cellContent->text()); });
|
||||
connect(ui->cellContent, &LineEdit::returnPressed, this, [this]() {confirmContentChanged(ui->cellContent->text()); });
|
||||
connect(ui->cellAlias, &LineEdit::finishedWithKey, this, [this](int, Qt::KeyboardModifiers) {confirmAliasChanged(ui->cellAlias->text()); });
|
||||
connect(ui->cellAlias, &LineEdit::returnPressed, this, [this]() {confirmAliasChanged(ui->cellAlias->text()); });
|
||||
connect(ui->cellAlias, &LineEdit::textEdited, this, &SheetView::aliasChanged);
|
||||
|
||||
columnWidthChangedConnection = sheet->columnWidthChanged.connect(bind(&SheetView::resizeColumn, this, bp::_1, bp::_2));
|
||||
rowHeightChangedConnection = sheet->rowHeightChanged.connect(bind(&SheetView::resizeRow, this, bp::_1, bp::_2));
|
||||
@@ -215,20 +218,6 @@ void SheetView::setCurrentCell(QString str)
|
||||
updateAliasLine();
|
||||
}
|
||||
|
||||
void SheetView::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
if (event->key() == Qt::Key_Delete) {
|
||||
if (event->modifiers() == 0) {
|
||||
//model()->setData(currentIndex(), QVariant(), Qt::EditRole);
|
||||
}
|
||||
else if (event->modifiers() == Qt::ControlModifier) {
|
||||
//model()->setData(currentIndex(), QVariant(), Qt::EditRole);
|
||||
}
|
||||
}
|
||||
else
|
||||
Gui::MDIView::keyPressEvent(event);
|
||||
}
|
||||
|
||||
void SheetView::updateContentLine()
|
||||
{
|
||||
QModelIndex i = ui->cells->currentIndex();
|
||||
@@ -238,7 +227,6 @@ void SheetView::updateContentLine()
|
||||
if (const auto * cell = sheet->getCell(CellAddress(i.row(), i.column())))
|
||||
(void)cell->getStringContent(str);
|
||||
ui->cellContent->setText(QString::fromUtf8(str.c_str()));
|
||||
ui->cellContent->setIndex(i);
|
||||
ui->cellContent->setEnabled(true);
|
||||
|
||||
// Update completer model; for the time being, we do this by setting the document object of the input line.
|
||||
@@ -255,7 +243,6 @@ void SheetView::updateAliasLine()
|
||||
if (const auto * cell = sheet->getCell(CellAddress(i.row(), i.column())))
|
||||
(void)cell->getAlias(str);
|
||||
ui->cellAlias->setText(QString::fromUtf8(str.c_str()));
|
||||
ui->cellAlias->setIndex(i);
|
||||
ui->cellAlias->setEnabled(true);
|
||||
|
||||
// Update completer model; for the time being, we do this by setting the document object of the input line.
|
||||
@@ -322,51 +309,53 @@ void SheetView::resizeRow(int col, int newSize)
|
||||
ui->cells->setRowHeight(col, newSize);
|
||||
}
|
||||
|
||||
void SheetView::editingFinished()
|
||||
void SheetView::editingFinishedWithKey(int key, Qt::KeyboardModifiers modifiers)
|
||||
{
|
||||
if (ui->cellContent->completerActive()) {
|
||||
ui->cellContent->hideCompleter();
|
||||
return;
|
||||
}
|
||||
|
||||
if (ui->cellAlias->completerActive()) {
|
||||
ui->cellAlias->hideCompleter();
|
||||
return;
|
||||
}
|
||||
|
||||
QModelIndex i = ui->cells->currentIndex();
|
||||
|
||||
if (i.isValid()) {
|
||||
QString str = ui->cellAlias->text();
|
||||
bool aliasOkay = true;
|
||||
|
||||
if (str.length()!= 0 && !sheet->isValidAlias(Base::Tools::toStdString(str))){
|
||||
aliasOkay = false;
|
||||
}
|
||||
|
||||
ui->cellAlias->setDocumentObject(sheet);
|
||||
ui->cells->model()->setData(i, QVariant(ui->cellContent->text()), Qt::EditRole);
|
||||
ui->cells->finishEditWithMove(key, modifiers);
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto * cell = sheet->getCell(CellAddress(i.row(), i.column()))){
|
||||
if (!aliasOkay){
|
||||
//do not show error message if failure to set new alias is because it is already the same string
|
||||
std::string current_alias;
|
||||
(void)cell->getAlias(current_alias);
|
||||
if (str != QString::fromUtf8(current_alias.c_str())){
|
||||
Base::Console().Error("Unable to set alias: %s\n", Base::Tools::toStdString(str).c_str());
|
||||
}
|
||||
} else {
|
||||
std::string address = CellAddress(i.row(), i.column()).toString();
|
||||
Gui::cmdAppObjectArgs(sheet, "setAlias('%s', '%s')",
|
||||
address, str.toStdString());
|
||||
Gui::cmdAppDocument(sheet->getDocument(), "recompute()");
|
||||
void SheetView::confirmAliasChanged(const QString& text)
|
||||
{
|
||||
bool aliasOkay = true;
|
||||
|
||||
ui->cellAlias->setDocumentObject(sheet);
|
||||
if (text.length() != 0 && !sheet->isValidAlias(Base::Tools::toStdString(text))) {
|
||||
aliasOkay = false;
|
||||
}
|
||||
|
||||
QModelIndex i = ui->cells->currentIndex();
|
||||
if (const auto* cell = sheet->getCell(CellAddress(i.row(), i.column()))) {
|
||||
if (!aliasOkay) {
|
||||
//do not show error message if failure to set new alias is because it is already the same string
|
||||
std::string current_alias;
|
||||
(void)cell->getAlias(current_alias);
|
||||
if (text != QString::fromUtf8(current_alias.c_str())) {
|
||||
Base::Console().Error("Unable to set alias: %s\n", Base::Tools::toStdString(text).c_str());
|
||||
}
|
||||
}
|
||||
ui->cells->setCurrentIndex(ui->cellContent->next());
|
||||
ui->cells->setFocus();
|
||||
else {
|
||||
std::string address = CellAddress(i.row(), i.column()).toString();
|
||||
Gui::cmdAppObjectArgs(sheet, "setAlias('%s', '%s')",
|
||||
address, text.toStdString());
|
||||
Gui::cmdAppDocument(sheet->getDocument(), "recompute()");
|
||||
ui->cells->setFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SheetView::confirmContentChanged(const QString& text)
|
||||
{
|
||||
QModelIndex i = ui->cells->currentIndex();
|
||||
ui->cells->model()->setData(i, QVariant(ui->cellContent->text()), Qt::EditRole);
|
||||
ui->cells->setFocus();
|
||||
}
|
||||
|
||||
void SheetView::aliasChanged(const QString& text)
|
||||
{
|
||||
// check live the input and highlight if the user input invalid characters
|
||||
|
||||
@@ -82,8 +82,10 @@ public:
|
||||
virtual void deleteSelf();
|
||||
|
||||
protected Q_SLOTS:
|
||||
void editingFinished();
|
||||
void editingFinishedWithKey(int key, Qt::KeyboardModifiers modifiers);
|
||||
void confirmAliasChanged(const QString& text);
|
||||
void aliasChanged(const QString& text);
|
||||
void confirmContentChanged(const QString& text);
|
||||
void currentChanged( const QModelIndex & current, const QModelIndex & previous );
|
||||
void columnResized(int col, int oldSize, int newSize);
|
||||
void rowResized(int row, int oldSize, int newSize);
|
||||
@@ -94,7 +96,6 @@ protected:
|
||||
void updateContentLine();
|
||||
void updateAliasLine();
|
||||
void setCurrentCell(QString str);
|
||||
void keyPressEvent(QKeyEvent *event);
|
||||
void resizeColumn(int col, int newSize);
|
||||
void resizeRow(int col, int newSize);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user