diff --git a/src/Mod/Spreadsheet/App/PropertySheet.cpp b/src/Mod/Spreadsheet/App/PropertySheet.cpp index 8a6fa6542d..286b72890f 100644 --- a/src/Mod/Spreadsheet/App/PropertySheet.cpp +++ b/src/Mod/Spreadsheet/App/PropertySheet.cpp @@ -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(); diff --git a/src/Mod/Spreadsheet/App/PropertySheet.h b/src/Mod/Spreadsheet/App/PropertySheet.h index 7e20cf6a7d..60694b85b5 100644 --- a/src/Mod/Spreadsheet/App/PropertySheet.h +++ b/src/Mod/Spreadsheet/App/PropertySheet.h @@ -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; diff --git a/src/Mod/Spreadsheet/App/Sheet.cpp b/src/Mod/Spreadsheet/App/Sheet.cpp index 3442e5a9b7..561e88c303 100644 --- a/src/Mod/Spreadsheet/App/Sheet.cpp +++ b/src/Mod/Spreadsheet/App/Sheet.cpp @@ -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. diff --git a/src/Mod/Spreadsheet/App/Sheet.h b/src/Mod/Spreadsheet/App/Sheet.h index 7b60256885..18e898b849 100644 --- a/src/Mod/Spreadsheet/App/Sheet.h +++ b/src/Mod/Spreadsheet/App/Sheet.h @@ -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; diff --git a/src/Mod/Spreadsheet/Gui/LineEdit.cpp b/src/Mod/Spreadsheet/Gui/LineEdit.cpp index 2e6a00a2c6..3116dcc6f7 100644 --- a/src/Mod/Spreadsheet/Gui/LineEdit.cpp +++ b/src/Mod/Spreadsheet/Gui/LineEdit.cpp @@ -26,68 +26,58 @@ # include #endif +#include + #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(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(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" diff --git a/src/Mod/Spreadsheet/Gui/LineEdit.h b/src/Mod/Spreadsheet/Gui/LineEdit.h index fa4e85185f..467e81cb7b 100644 --- a/src/Mod/Spreadsheet/Gui/LineEdit.h +++ b/src/Mod/Spreadsheet/Gui/LineEdit.h @@ -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; }; } diff --git a/src/Mod/Spreadsheet/Gui/SheetTableView.cpp b/src/Mod/Spreadsheet/Gui/SheetTableView.cpp index 11da87b0b5..86b532213e 100644 --- a/src/Mod/Spreadsheet/Gui/SheetTableView.cpp +++ b/src/Mod/Spreadsheet/Gui/SheetTableView.cpp @@ -101,6 +101,7 @@ static std::pair 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::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(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(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(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 ) diff --git a/src/Mod/Spreadsheet/Gui/SheetTableView.h b/src/Mod/Spreadsheet/Gui/SheetTableView.h index 5dbb7b3b05..fb0f260a23 100644 --- a/src/Mod/Spreadsheet/Gui/SheetTableView.h +++ b/src/Mod/Spreadsheet/Gui/SheetTableView.h @@ -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; diff --git a/src/Mod/Spreadsheet/Gui/SpreadsheetDelegate.cpp b/src/Mod/Spreadsheet/Gui/SpreadsheetDelegate.cpp index 835a0ec6af..682e22b0c4 100644 --- a/src/Mod/Spreadsheet/Gui/SpreadsheetDelegate.cpp +++ b/src/Mod/Spreadsheet/Gui/SpreadsheetDelegate.cpp @@ -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(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); diff --git a/src/Mod/Spreadsheet/Gui/SpreadsheetDelegate.h b/src/Mod/Spreadsheet/Gui/SpreadsheetDelegate.h index 03080af514..fe4efc7855 100644 --- a/src/Mod/Spreadsheet/Gui/SpreadsheetDelegate.h +++ b/src/Mod/Spreadsheet/Gui/SpreadsheetDelegate.h @@ -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; }; diff --git a/src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp b/src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp index 1bd97d7af6..5addb23b96 100644 --- a/src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp +++ b/src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp @@ -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 diff --git a/src/Mod/Spreadsheet/Gui/SpreadsheetView.h b/src/Mod/Spreadsheet/Gui/SpreadsheetView.h index 8e6aced3ef..141cdfe59b 100644 --- a/src/Mod/Spreadsheet/Gui/SpreadsheetView.h +++ b/src/Mod/Spreadsheet/Gui/SpreadsheetView.h @@ -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);