mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-08-03 10:34:14 +02:00
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:
+82
-13
@@ -33,6 +33,8 @@
|
||||
#include "ui/dialogwaiting.h"
|
||||
#include "ui/projectpropertiesdialog.h"
|
||||
#include "ui/titleblockpropertieswidget.h"
|
||||
#include "undocommand/movediagramcommand.h"
|
||||
#include "undocommand/removediagramcommand.h"
|
||||
|
||||
/**
|
||||
Constructeur
|
||||
@@ -87,6 +89,10 @@ void ProjectView::setProject(QETProject *project)
|
||||
Q_UNUSED(project)
|
||||
this->diagramAdded(diagram);
|
||||
});
|
||||
connect(m_project, &QETProject::diagramRemoved, [this](QETProject *project, Diagram *diagram) {
|
||||
Q_UNUSED(project)
|
||||
this->diagramRemoved(diagram);
|
||||
});
|
||||
|
||||
adjustReadOnlyState();
|
||||
loadDiagrams();
|
||||
@@ -383,7 +389,7 @@ void ProjectView::removeDiagram(DiagramView *diagram_view, bool silent)
|
||||
int answer = QET::QetMessageBox::question(
|
||||
this,
|
||||
tr("Supprimer le folio ?", "message box title"),
|
||||
tr("Êtes-vous sûr de vouloir supprimer ce folio du projet ? Ce changement est irréversible.", "message box content"),
|
||||
tr("Êtes-vous sûr de vouloir supprimer ce folio du projet ?", "message box content"),
|
||||
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
|
||||
QMessageBox::No
|
||||
);
|
||||
@@ -392,16 +398,33 @@ void ProjectView::removeDiagram(DiagramView *diagram_view, bool silent)
|
||||
}
|
||||
}
|
||||
|
||||
//Remove the diagram view of the tabs widget
|
||||
m_project->undoStack()->push(new RemoveDiagramCommand(m_project, diagram_view->diagram()));
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ProjectView::diagramRemoved
|
||||
Slot called when the project emits diagramRemoved (either from a direct,
|
||||
non-undoable QETProject::removeDiagram() call, or -- the normal UI path
|
||||
-- from a RemoveDiagramCommand's redo()/undo()). Tears down the tab and
|
||||
DiagramView for the given diagram; the diagram itself is left untouched,
|
||||
since by this point it has already been detached (and possibly parked
|
||||
for undo) by the caller.
|
||||
@param diagram
|
||||
*/
|
||||
void ProjectView::diagramRemoved(Diagram *diagram)
|
||||
{
|
||||
DiagramView *diagram_view = findDiagram(diagram);
|
||||
if (!diagram_view)
|
||||
return;
|
||||
|
||||
int index_to_remove = m_diagram_ids.key(diagram_view);
|
||||
m_tab->removeTab(index_to_remove);
|
||||
m_diagram_view_list.removeAll(diagram_view);
|
||||
rebuildDiagramsMap();
|
||||
|
||||
m_project -> removeDiagram(diagram_view -> diagram());
|
||||
emit(diagramRemoved(diagram_view));
|
||||
delete diagram_view;
|
||||
|
||||
emit(diagramRemoved(diagram_view));
|
||||
updateAllTabsTitle();
|
||||
m_project -> setModified(true);
|
||||
}
|
||||
@@ -485,7 +508,7 @@ void ProjectView::moveDiagramUp(DiagramView *diagram_view) {
|
||||
// le schema est le premier du projet
|
||||
return;
|
||||
}
|
||||
m_tab -> tabBar() -> moveTab(diagram_view_position, diagram_view_position - 1);
|
||||
m_project->undoStack()->push(new MoveDiagramCommand(this, diagram_view->diagram(), diagram_view_position - 1));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -506,7 +529,7 @@ void ProjectView::moveDiagramDown(DiagramView *diagram_view) {
|
||||
// le schema est le dernier du projet
|
||||
return;
|
||||
}
|
||||
m_tab -> tabBar() -> moveTab(diagram_view_position, diagram_view_position + 1);
|
||||
m_project->undoStack()->push(new MoveDiagramCommand(this, diagram_view->diagram(), diagram_view_position + 1));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -528,7 +551,7 @@ void ProjectView::moveDiagramUpTop(DiagramView *diagram_view)
|
||||
// le schema est le premier du projet
|
||||
return;
|
||||
}
|
||||
m_tab->tabBar()->moveTab(diagram_view_position, 0);
|
||||
m_project->undoStack()->push(new MoveDiagramCommand(this, diagram_view->diagram(), 0));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -550,7 +573,7 @@ void ProjectView::moveDiagramUpx10(DiagramView *diagram_view) {
|
||||
// le schema est le premier du projet
|
||||
return;
|
||||
}
|
||||
m_tab -> tabBar() -> moveTab(diagram_view_position, diagram_view_position - 10);
|
||||
m_project->undoStack()->push(new MoveDiagramCommand(this, diagram_view->diagram(), diagram_view_position - 10));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -573,7 +596,7 @@ void ProjectView::moveDiagramUpx100(DiagramView *diagram_view) {
|
||||
// The diagram is the first of the project
|
||||
return;
|
||||
}
|
||||
m_tab->tabBar()->moveTab(diagram_view_position, diagram_view_position - 100);
|
||||
m_project->undoStack()->push(new MoveDiagramCommand(this, diagram_view->diagram(), diagram_view_position - 100));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -598,7 +621,7 @@ void ProjectView::moveDiagramDownx100(DiagramView *diagram_view) {
|
||||
// The diagram is the last of the project
|
||||
return;
|
||||
}
|
||||
m_tab->tabBar()->moveTab(diagram_view_position, diagram_view_position + 100);
|
||||
m_project->undoStack()->push(new MoveDiagramCommand(this, diagram_view->diagram(), diagram_view_position + 100));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -621,7 +644,7 @@ void ProjectView::moveDiagramDownx10(DiagramView *diagram_view) {
|
||||
// le schema est le dernier du projet
|
||||
return;
|
||||
}
|
||||
m_tab -> tabBar() -> moveTab(diagram_view_position, diagram_view_position + 10);
|
||||
m_project->undoStack()->push(new MoveDiagramCommand(this, diagram_view->diagram(), diagram_view_position + 10));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1052,10 +1075,10 @@ void ProjectView::tabMoved(int from, int to)
|
||||
{
|
||||
if (!m_project)
|
||||
return;
|
||||
|
||||
|
||||
m_project->diagramOrderChanged(from, to);
|
||||
rebuildDiagramsMap();
|
||||
|
||||
|
||||
//Rebuild the title of each diagram in range from - to
|
||||
for (int i= qMin(from,to) ; i< qMax(from,to)+1 ; ++i)
|
||||
{
|
||||
@@ -1064,6 +1087,52 @@ void ProjectView::tabMoved(int from, int to)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ProjectView::setDiagramPosition
|
||||
Move \p diagram's tab to \p new_position (out-of-range values are
|
||||
clamped by the underlying tab bar, same as a direct QTabBar::moveTab()
|
||||
call). Used by MoveDiagramCommand for both redo and undo.
|
||||
|
||||
tabMoved() -- the slot that normally keeps the project's diagram list in
|
||||
sync with the tab bar -- is connected with Qt::QueuedConnection (needed
|
||||
so interactive drag-and-drop reordering settles before the model
|
||||
updates). That queued call would run a second, redundant
|
||||
diagramOrderChanged() after this method already performed it
|
||||
synchronously, so that one connection is temporarily dropped around the
|
||||
move (blocking all of the tab bar's signals instead would also suppress
|
||||
QTabWidget's own internal tabMoved connection, which is what keeps its
|
||||
page stack in the same order as the tab bar -- breaking the move
|
||||
visually).
|
||||
@param diagram
|
||||
@param new_position
|
||||
*/
|
||||
void ProjectView::setDiagramPosition(Diagram *diagram, int new_position)
|
||||
{
|
||||
if (!m_project)
|
||||
return;
|
||||
|
||||
DiagramView *diagram_view = findDiagram(diagram);
|
||||
if (!diagram_view)
|
||||
return;
|
||||
|
||||
int current_position = m_diagram_ids.key(diagram_view, -1);
|
||||
if (current_position < 0)
|
||||
return;
|
||||
|
||||
disconnect(m_tab->tabBar(), SIGNAL(tabMoved(int,int)), this, SLOT(tabMoved(int,int)));
|
||||
m_tab->tabBar()->moveTab(current_position, new_position);
|
||||
connect(m_tab->tabBar(), SIGNAL(tabMoved(int,int)), this, SLOT(tabMoved(int,int)), Qt::QueuedConnection);
|
||||
|
||||
rebuildDiagramsMap();
|
||||
|
||||
int actual_new_position = m_tab->indexOf(diagram_view);
|
||||
if (actual_new_position != current_position)
|
||||
m_project->diagramOrderChanged(current_position, actual_new_position);
|
||||
|
||||
for (int i = qMin(current_position, actual_new_position); i <= qMax(current_position, actual_new_position); ++i)
|
||||
updateTabTitle(m_diagram_ids.value(i));
|
||||
}
|
||||
|
||||
/**
|
||||
@param diagram Schema a trouver
|
||||
@return le DiagramView correspondant au schema passe en parametre, ou 0 si
|
||||
|
||||
Reference in New Issue
Block a user