Add undo/redo support for folio add, delete, and reorder (#575)

Three new QUndoCommand subclasses (AddDiagramCommand, RemoveDiagramCommand,
MoveDiagramCommand) pushed onto the project's existing (already
project-scoped) undo stack, so folio structure edits are undoable
alongside every item-level edit already on that stack.

- QETProject::addDiagram()/detachDiagram() are the shared attach/detach
  primitives: they mutate the diagram list, connect/disconnect the two
  per-diagram signals set up at add time, and emit diagramAdded/
  diagramRemoved. AddDiagramCommand and RemoveDiagramCommand call these
  (via friend access) for both redo and undo, so a removed diagram is
  parked rather than destroyed -- it's only actually deleted if the
  command itself falls out of undo history while still detached.
- ProjectView reacts to diagramRemoved the same way it already reacted to
  diagramAdded (tearing down/rebuilding the tab), so both directions of
  both commands go through the same reactive path every other diagram
  listener (project database, cross-references, generic panel) already
  relies on.
- MoveDiagramCommand wraps a new ProjectView::setDiagramPosition(), which
  performs the tab move and the project's diagramOrderChanged() list
  reorder synchronously in one step, instead of relying on the queued
  tabMoved connection (needed for interactive drag-and-drop) to catch up
  later -- avoiding a second, redundant reorder from that queued call.
- Multi-folio delete and multi-folio move (QETDiagramEditor::removeDiagrams()
  and the moveDiagram*(QList<Diagram*>) batch slots) wrap their per-diagram
  loop in QUndoStack::beginMacro()/endMacro(), so a multi-select action is
  one undo step, matching current UX.
- Softened the delete confirmation's "this change is irreversible" wording
  now that it no longer is.

Verified headlessly (Xvfb + xdotool + scrot): add/undo/redo, delete/undo/
redo (single and multi-select, single undo step for the batch), and
move/undo/redo all behave correctly against a 7-folio project.
This commit is contained in:
ispyisail
2026-08-01 17:03:39 +12:00
parent c772e1d3ea
commit 55886e5e8a
12 changed files with 499 additions and 25 deletions
+25 -2
View File
@@ -2249,17 +2249,26 @@ void QETDiagramEditor::removeDiagrams(const QList<Diagram *> &diagrams)
}
ProjectView *project_view = nullptr;
if (QETProject *diagram_project = diagrams.first()->project()) {
project_view = findProject(diagram_project);
QETProject *project = diagrams.first()->project();
if (project) {
project_view = findProject(project);
}
if (project_view) project_view->setUpdatesEnabled(false);
if (pa) pa->setUpdatesEnabled(false);
if (project) {
project->undoStack()->beginMacro(diagrams.count() == 1
? tr("Supprimer le folio")
: tr("Supprimer %1 folios").arg(diagrams.count()));
}
foreach (Diagram *diagram, diagrams) {
removeDiagramSilent(diagram);
}
if (project) project->undoStack()->endMacro();
if (pa) pa->setUpdatesEnabled(true);
if (project_view) project_view->setUpdatesEnabled(true);
@@ -2289,10 +2298,12 @@ void QETDiagramEditor::moveDiagramUp(const QList<Diagram *> &diagrams) {
if (!diagram_project->isReadOnly()) {
if (ProjectView *project_view = findProject(diagram_project)) {
// Forward loop for moving up
diagram_project->undoStack()->beginMacro(tr("Déplacer les folios"));
for (int i = 0; i < safeDiagrams.size(); ++i) {
project_view->moveDiagramUp(safeDiagrams.at(i));
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
}
diagram_project->undoStack()->endMacro();
}
}
}
@@ -2305,10 +2316,12 @@ void QETDiagramEditor::moveDiagramDown(const QList<Diagram *> &diagrams) {
if (!diagram_project->isReadOnly()) {
if (ProjectView *project_view = findProject(diagram_project)) {
// Backward loop for moving down
diagram_project->undoStack()->beginMacro(tr("Déplacer les folios"));
for (int i = safeDiagrams.size() - 1; i >= 0; --i) {
project_view->moveDiagramDown(safeDiagrams.at(i));
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
}
diagram_project->undoStack()->endMacro();
}
}
}
@@ -2321,10 +2334,12 @@ void QETDiagramEditor::moveDiagramUpTop(const QList<Diagram *> &diagrams) {
if (!diagram_project->isReadOnly()) {
if (ProjectView *project_view = findProject(diagram_project)) {
// Backward loop to preserve relative order of the selected items when moving to top
diagram_project->undoStack()->beginMacro(tr("Déplacer les folios"));
for (int i = safeDiagrams.size() - 1; i >= 0; --i) {
project_view->moveDiagramUpTop(safeDiagrams.at(i));
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
}
diagram_project->undoStack()->endMacro();
}
}
}
@@ -2337,10 +2352,12 @@ void QETDiagramEditor::moveDiagramUpx10(const QList<Diagram *> &diagrams) {
if (!diagram_project->isReadOnly()) {
if (ProjectView *project_view = findProject(diagram_project)) {
// Forward loop for moving up
diagram_project->undoStack()->beginMacro(tr("Déplacer les folios"));
for (int i = 0; i < safeDiagrams.size(); ++i) {
project_view->moveDiagramUpx10(safeDiagrams.at(i));
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
}
diagram_project->undoStack()->endMacro();
}
}
}
@@ -2353,10 +2370,12 @@ void QETDiagramEditor::moveDiagramDownx10(const QList<Diagram *> &diagrams) {
if (!diagram_project->isReadOnly()) {
if (ProjectView *project_view = findProject(diagram_project)) {
// Backward loop for moving down
diagram_project->undoStack()->beginMacro(tr("Déplacer les folios"));
for (int i = safeDiagrams.size() - 1; i >= 0; --i) {
project_view->moveDiagramDownx10(safeDiagrams.at(i));
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
}
diagram_project->undoStack()->endMacro();
}
}
}
@@ -2369,10 +2388,12 @@ void QETDiagramEditor::moveDiagramUpx100(const QList<Diagram *> &diagrams) {
if (!diagram_project->isReadOnly()) {
if (ProjectView *project_view = findProject(diagram_project)) {
// Forward loop for moving up
diagram_project->undoStack()->beginMacro(tr("Déplacer les folios"));
for (int i = 0; i < safeDiagrams.size(); ++i) {
project_view->moveDiagramUpx100(safeDiagrams.at(i));
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
}
diagram_project->undoStack()->endMacro();
}
}
}
@@ -2385,10 +2406,12 @@ void QETDiagramEditor::moveDiagramDownx100(const QList<Diagram *> &diagrams) {
if (!diagram_project->isReadOnly()) {
if (ProjectView *project_view = findProject(diagram_project)) {
// Backward loop for moving down
diagram_project->undoStack()->beginMacro(tr("Déplacer les folios"));
for (int i = safeDiagrams.size() - 1; i >= 0; --i) {
project_view->moveDiagramDownx100(safeDiagrams.at(i));
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
}
diagram_project->undoStack()->endMacro();
}
}
}