Spreadsheet: Implement undo functionality where missing

Implement undo/redo functionality in the Spreadsheet application
for the "extend" function, the drag-and-drop function, and when
copying and pasting.
This commit is contained in:
martinfalisse 2022-04-11 17:05:23 +02:00 committed by Ali Mohammad Pur
parent 22575c9370
commit 356eca7e33
Notes: sideshowbarker 2024-07-17 11:51:12 +09:00
5 changed files with 28 additions and 9 deletions

View File

@ -292,12 +292,13 @@ Position Sheet::offset_relative_to(Position const& base, Position const& offset,
return { new_column, new_row };
}
void Sheet::copy_cells(Vector<Position> from, Vector<Position> to, Optional<Position> resolve_relative_to, CopyOperation copy_operation)
Vector<CellChange> Sheet::copy_cells(Vector<Position> from, Vector<Position> to, Optional<Position> resolve_relative_to, CopyOperation copy_operation)
{
Vector<CellChange> cell_changes;
// Disallow misaligned copies.
if (to.size() > 1 && from.size() != to.size()) {
dbgln("Cannot copy {} cells to {} cells", from.size(), to.size());
return;
return cell_changes;
}
Vector<Position> target_cells;
@ -307,16 +308,20 @@ void Sheet::copy_cells(Vector<Position> from, Vector<Position> to, Optional<Posi
auto copy_to = [&](auto& source_position, Position target_position) {
auto& target_cell = ensure(target_position);
auto* source_cell = at(source_position);
auto previous_data = target_cell.data();
if (!source_cell) {
target_cell.set_data("");
cell_changes.append(CellChange(target_cell, previous_data));
return;
}
target_cell.copy_from(*source_cell);
if (copy_operation == CopyOperation::Cut)
if (!target_cells.contains_slow(source_position))
source_cell->set_data("");
cell_changes.append(CellChange(target_cell, previous_data));
if (copy_operation == CopyOperation::Cut && !target_cells.contains_slow(source_position)) {
cell_changes.append(CellChange(*source_cell, source_cell->data()));
source_cell->set_data("");
}
};
// Resolve each index as relative to the first index offset from the selection.
@ -359,6 +364,8 @@ void Sheet::copy_cells(Vector<Position> from, Vector<Position> to, Optional<Posi
dbgln_if(COPY_DEBUG, "Paste from '{}' to '{}'", position.to_url(*this), target.to_url(*this));
copy_to(position, resolve_relative_to.has_value() ? offset_relative_to(target, position, resolve_relative_to.value()) : target);
}
return cell_changes;
}
RefPtr<Sheet> Sheet::from_json(JsonObject const& object, Workbook& workbook)

View File

@ -135,7 +135,7 @@ public:
Cut
};
void copy_cells(Vector<Position> from, Vector<Position> to, Optional<Position> resolve_relative_to = {}, CopyOperation copy_operation = CopyOperation::Copy);
Vector<CellChange> copy_cells(Vector<Position> from, Vector<Position> to, Optional<Position> resolve_relative_to = {}, CopyOperation copy_operation = CopyOperation::Copy);
/// Gives the bottom-right corner of the smallest bounding box containing all the written data, optionally limited to the given column.
Position written_data_bounds(Optional<size_t> column_index = {}) const;

View File

@ -30,6 +30,7 @@ public:
void update();
Function<void(Cell&, String&)> on_cell_data_change;
Function<void(Vector<CellChange>)> on_cells_data_change;
private:
explicit SheetModel(Sheet& sheet)

View File

@ -220,6 +220,7 @@ void InfinitelyScrollableTableView::mouseup_event(GUI::MouseEvent& event)
Vector<Position> from;
Position position { (size_t)m_target_cell.column(), (size_t)m_target_cell.row() };
from.append(position);
Vector<CellChange> cell_changes;
selection().for_each_index([&](auto& index) {
if (index == m_starting_selection_index)
return;
@ -227,8 +228,11 @@ void InfinitelyScrollableTableView::mouseup_event(GUI::MouseEvent& event)
Vector<Position> to;
Position position { (size_t)index.column(), (size_t)index.row() };
to.append(position);
sheet.copy_cells(from, to);
auto cell_change = sheet.copy_cells(from, to);
cell_changes.extend(cell_change);
});
if (static_cast<SheetModel&>(*this->model()).on_cells_data_change)
static_cast<SheetModel&>(*this->model()).on_cells_data_change(cell_changes);
update();
}
@ -425,7 +429,9 @@ SpreadsheetView::SpreadsheetView(Sheet& sheet)
return;
auto first_position = source_positions.take_first();
m_sheet->copy_cells(move(source_positions), move(target_positions), first_position, Spreadsheet::Sheet::CopyOperation::Cut);
auto cell_changes = m_sheet->copy_cells(move(source_positions), move(target_positions), first_position, Spreadsheet::Sheet::CopyOperation::Cut);
if (model()->on_cells_data_change)
model()->on_cells_data_change(cell_changes);
return;
}

View File

@ -193,7 +193,8 @@ SpreadsheetWidget::SpreadsheetWidget(GUI::Window& parent_window, NonnullRefPtrVe
return;
auto first_position = source_positions.take_first();
sheet.copy_cells(move(source_positions), move(target_positions), first_position, action == "cut" ? Spreadsheet::Sheet::CopyOperation::Cut : Spreadsheet::Sheet::CopyOperation::Copy);
auto cell_changes = sheet.copy_cells(move(source_positions), move(target_positions), first_position, action == "cut" ? Spreadsheet::Sheet::CopyOperation::Cut : Spreadsheet::Sheet::CopyOperation::Copy);
undo_stack().push(make<CellsUndoCommand>(cell_changes));
} else {
for (auto& cell : sheet.selected_cells())
sheet.ensure(cell).set_data(StringView { data.data.data(), data.data.size() });
@ -284,6 +285,10 @@ void SpreadsheetWidget::setup_tabs(NonnullRefPtrVector<Sheet> new_sheets)
undo_stack().push(make<CellsUndoCommand>(cell, previous_data));
window()->set_modified(true);
};
new_view.model()->on_cells_data_change = [&](Vector<CellChange> cell_changes) {
undo_stack().push(make<CellsUndoCommand>(cell_changes));
window()->set_modified(true);
};
new_view.on_selection_changed = [&](Vector<Position>&& selection) {
auto* sheet_ptr = current_worksheet_if_available();
// How did this even happen?