Merge pull request #590 from ispyisail/feature-folio-undo-redo

Add undo/redo support for folio add, delete, and reorder
This commit is contained in:
Laurent Trinques
2026-08-01 07:09:57 +02:00
committed by GitHub
12 changed files with 499 additions and 25 deletions
+6
View File
@@ -735,6 +735,8 @@ set(QET_SRC_FILES
${QET_DIR}/sources/undocommand/addelementtextcommand.cpp ${QET_DIR}/sources/undocommand/addelementtextcommand.cpp
${QET_DIR}/sources/undocommand/addelementtextcommand.h ${QET_DIR}/sources/undocommand/addelementtextcommand.h
${QET_DIR}/sources/undocommand/adddiagramcommand.cpp
${QET_DIR}/sources/undocommand/adddiagramcommand.h
${QET_DIR}/sources/undocommand/addgraphicsobjectcommand.cpp ${QET_DIR}/sources/undocommand/addgraphicsobjectcommand.cpp
${QET_DIR}/sources/undocommand/addgraphicsobjectcommand.h ${QET_DIR}/sources/undocommand/addgraphicsobjectcommand.h
${QET_DIR}/sources/undocommand/changeelementdatacommand.cpp ${QET_DIR}/sources/undocommand/changeelementdatacommand.cpp
@@ -749,6 +751,10 @@ set(QET_SRC_FILES
${QET_DIR}/sources/undocommand/itemmodelcommand.h ${QET_DIR}/sources/undocommand/itemmodelcommand.h
${QET_DIR}/sources/undocommand/linkelementcommand.cpp ${QET_DIR}/sources/undocommand/linkelementcommand.cpp
${QET_DIR}/sources/undocommand/linkelementcommand.h ${QET_DIR}/sources/undocommand/linkelementcommand.h
${QET_DIR}/sources/undocommand/movediagramcommand.cpp
${QET_DIR}/sources/undocommand/movediagramcommand.h
${QET_DIR}/sources/undocommand/removediagramcommand.cpp
${QET_DIR}/sources/undocommand/removediagramcommand.h
${QET_DIR}/sources/undocommand/rotateselectioncommand.cpp ${QET_DIR}/sources/undocommand/rotateselectioncommand.cpp
${QET_DIR}/sources/undocommand/rotateselectioncommand.h ${QET_DIR}/sources/undocommand/rotateselectioncommand.h
${QET_DIR}/sources/undocommand/rotatetextscommand.cpp ${QET_DIR}/sources/undocommand/rotatetextscommand.cpp
+80 -11
View File
@@ -33,6 +33,8 @@
#include "ui/dialogwaiting.h" #include "ui/dialogwaiting.h"
#include "ui/projectpropertiesdialog.h" #include "ui/projectpropertiesdialog.h"
#include "ui/titleblockpropertieswidget.h" #include "ui/titleblockpropertieswidget.h"
#include "undocommand/movediagramcommand.h"
#include "undocommand/removediagramcommand.h"
/** /**
Constructeur Constructeur
@@ -87,6 +89,10 @@ void ProjectView::setProject(QETProject *project)
Q_UNUSED(project) Q_UNUSED(project)
this->diagramAdded(diagram); this->diagramAdded(diagram);
}); });
connect(m_project, &QETProject::diagramRemoved, [this](QETProject *project, Diagram *diagram) {
Q_UNUSED(project)
this->diagramRemoved(diagram);
});
adjustReadOnlyState(); adjustReadOnlyState();
loadDiagrams(); loadDiagrams();
@@ -383,7 +389,7 @@ void ProjectView::removeDiagram(DiagramView *diagram_view, bool silent)
int answer = QET::QetMessageBox::question( int answer = QET::QetMessageBox::question(
this, this,
tr("Supprimer le folio ?", "message box title"), 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::Yes | QMessageBox::No | QMessageBox::Cancel,
QMessageBox::No 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); int index_to_remove = m_diagram_ids.key(diagram_view);
m_tab->removeTab(index_to_remove); m_tab->removeTab(index_to_remove);
m_diagram_view_list.removeAll(diagram_view); m_diagram_view_list.removeAll(diagram_view);
rebuildDiagramsMap(); rebuildDiagramsMap();
m_project -> removeDiagram(diagram_view -> diagram()); emit(diagramRemoved(diagram_view));
delete diagram_view; delete diagram_view;
emit(diagramRemoved(diagram_view));
updateAllTabsTitle(); updateAllTabsTitle();
m_project -> setModified(true); m_project -> setModified(true);
} }
@@ -485,7 +508,7 @@ void ProjectView::moveDiagramUp(DiagramView *diagram_view) {
// le schema est le premier du projet // le schema est le premier du projet
return; 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 // le schema est le dernier du projet
return; 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 // le schema est le premier du projet
return; 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 // le schema est le premier du projet
return; 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 // The diagram is the first of the project
return; 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 // The diagram is the last of the project
return; 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 // le schema est le dernier du projet
return; 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));
} }
/** /**
@@ -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 @param diagram Schema a trouver
@return le DiagramView correspondant au schema passe en parametre, ou 0 si @return le DiagramView correspondant au schema passe en parametre, ou 0 si
+2
View File
@@ -102,6 +102,7 @@ class ProjectView : public QWidget
void changeTabDown(); void changeTabDown();
void changeFirstTab(); void changeFirstTab();
void changeLastTab(); void changeLastTab();
void setDiagramPosition(Diagram *diagram, int new_position);
public slots: public slots:
void removeDiagram(DiagramView *diagram_view, bool silent = false); void removeDiagram(DiagramView *diagram_view, bool silent = false);
@@ -168,6 +169,7 @@ class ProjectView : public QWidget
void setDisplayFallbackWidget(bool); void setDisplayFallbackWidget(bool);
void adjustReadOnlyState(); void adjustReadOnlyState();
void diagramAdded(Diagram *diagram); void diagramAdded(Diagram *diagram);
void diagramRemoved(Diagram *diagram);
// attributes // attributes
private: private:
+25 -2
View File
@@ -2249,17 +2249,26 @@ void QETDiagramEditor::removeDiagrams(const QList<Diagram *> &diagrams)
} }
ProjectView *project_view = nullptr; ProjectView *project_view = nullptr;
if (QETProject *diagram_project = diagrams.first()->project()) { QETProject *project = diagrams.first()->project();
project_view = findProject(diagram_project); if (project) {
project_view = findProject(project);
} }
if (project_view) project_view->setUpdatesEnabled(false); if (project_view) project_view->setUpdatesEnabled(false);
if (pa) pa->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) { foreach (Diagram *diagram, diagrams) {
removeDiagramSilent(diagram); removeDiagramSilent(diagram);
} }
if (project) project->undoStack()->endMacro();
if (pa) pa->setUpdatesEnabled(true); if (pa) pa->setUpdatesEnabled(true);
if (project_view) project_view->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 (!diagram_project->isReadOnly()) {
if (ProjectView *project_view = findProject(diagram_project)) { if (ProjectView *project_view = findProject(diagram_project)) {
// Forward loop for moving up // Forward loop for moving up
diagram_project->undoStack()->beginMacro(tr("Déplacer les folios"));
for (int i = 0; i < safeDiagrams.size(); ++i) { for (int i = 0; i < safeDiagrams.size(); ++i) {
project_view->moveDiagramUp(safeDiagrams.at(i)); project_view->moveDiagramUp(safeDiagrams.at(i));
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
} }
diagram_project->undoStack()->endMacro();
} }
} }
} }
@@ -2305,10 +2316,12 @@ void QETDiagramEditor::moveDiagramDown(const QList<Diagram *> &diagrams) {
if (!diagram_project->isReadOnly()) { if (!diagram_project->isReadOnly()) {
if (ProjectView *project_view = findProject(diagram_project)) { if (ProjectView *project_view = findProject(diagram_project)) {
// Backward loop for moving down // Backward loop for moving down
diagram_project->undoStack()->beginMacro(tr("Déplacer les folios"));
for (int i = safeDiagrams.size() - 1; i >= 0; --i) { for (int i = safeDiagrams.size() - 1; i >= 0; --i) {
project_view->moveDiagramDown(safeDiagrams.at(i)); project_view->moveDiagramDown(safeDiagrams.at(i));
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
} }
diagram_project->undoStack()->endMacro();
} }
} }
} }
@@ -2321,10 +2334,12 @@ void QETDiagramEditor::moveDiagramUpTop(const QList<Diagram *> &diagrams) {
if (!diagram_project->isReadOnly()) { if (!diagram_project->isReadOnly()) {
if (ProjectView *project_view = findProject(diagram_project)) { if (ProjectView *project_view = findProject(diagram_project)) {
// Backward loop to preserve relative order of the selected items when moving to top // 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) { for (int i = safeDiagrams.size() - 1; i >= 0; --i) {
project_view->moveDiagramUpTop(safeDiagrams.at(i)); project_view->moveDiagramUpTop(safeDiagrams.at(i));
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
} }
diagram_project->undoStack()->endMacro();
} }
} }
} }
@@ -2337,10 +2352,12 @@ void QETDiagramEditor::moveDiagramUpx10(const QList<Diagram *> &diagrams) {
if (!diagram_project->isReadOnly()) { if (!diagram_project->isReadOnly()) {
if (ProjectView *project_view = findProject(diagram_project)) { if (ProjectView *project_view = findProject(diagram_project)) {
// Forward loop for moving up // Forward loop for moving up
diagram_project->undoStack()->beginMacro(tr("Déplacer les folios"));
for (int i = 0; i < safeDiagrams.size(); ++i) { for (int i = 0; i < safeDiagrams.size(); ++i) {
project_view->moveDiagramUpx10(safeDiagrams.at(i)); project_view->moveDiagramUpx10(safeDiagrams.at(i));
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
} }
diagram_project->undoStack()->endMacro();
} }
} }
} }
@@ -2353,10 +2370,12 @@ void QETDiagramEditor::moveDiagramDownx10(const QList<Diagram *> &diagrams) {
if (!diagram_project->isReadOnly()) { if (!diagram_project->isReadOnly()) {
if (ProjectView *project_view = findProject(diagram_project)) { if (ProjectView *project_view = findProject(diagram_project)) {
// Backward loop for moving down // Backward loop for moving down
diagram_project->undoStack()->beginMacro(tr("Déplacer les folios"));
for (int i = safeDiagrams.size() - 1; i >= 0; --i) { for (int i = safeDiagrams.size() - 1; i >= 0; --i) {
project_view->moveDiagramDownx10(safeDiagrams.at(i)); project_view->moveDiagramDownx10(safeDiagrams.at(i));
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
} }
diagram_project->undoStack()->endMacro();
} }
} }
} }
@@ -2369,10 +2388,12 @@ void QETDiagramEditor::moveDiagramUpx100(const QList<Diagram *> &diagrams) {
if (!diagram_project->isReadOnly()) { if (!diagram_project->isReadOnly()) {
if (ProjectView *project_view = findProject(diagram_project)) { if (ProjectView *project_view = findProject(diagram_project)) {
// Forward loop for moving up // Forward loop for moving up
diagram_project->undoStack()->beginMacro(tr("Déplacer les folios"));
for (int i = 0; i < safeDiagrams.size(); ++i) { for (int i = 0; i < safeDiagrams.size(); ++i) {
project_view->moveDiagramUpx100(safeDiagrams.at(i)); project_view->moveDiagramUpx100(safeDiagrams.at(i));
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
} }
diagram_project->undoStack()->endMacro();
} }
} }
} }
@@ -2385,10 +2406,12 @@ void QETDiagramEditor::moveDiagramDownx100(const QList<Diagram *> &diagrams) {
if (!diagram_project->isReadOnly()) { if (!diagram_project->isReadOnly()) {
if (ProjectView *project_view = findProject(diagram_project)) { if (ProjectView *project_view = findProject(diagram_project)) {
// Backward loop for moving down // Backward loop for moving down
diagram_project->undoStack()->beginMacro(tr("Déplacer les folios"));
for (int i = safeDiagrams.size() - 1; i >= 0; --i) { for (int i = safeDiagrams.size() - 1; i >= 0; --i) {
project_view->moveDiagramDownx100(safeDiagrams.at(i)); project_view->moveDiagramDownx100(safeDiagrams.at(i));
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
} }
diagram_project->undoStack()->endMacro();
} }
} }
} }
+35 -9
View File
@@ -34,6 +34,7 @@
#include "TerminalStrip/terminalstrip.h" #include "TerminalStrip/terminalstrip.h"
#include "qetxml.h" #include "qetxml.h"
#include "qetversion.h" #include "qetversion.h"
#include "undocommand/adddiagramcommand.h"
#include <QElapsedTimer> #include <QElapsedTimer>
#include <QHash> #include <QHash>
@@ -1348,6 +1349,8 @@ bool QETProject::usesTitleBlockTemplate(const TitleBlockTemplateLocation &locati
/** /**
@brief QETProject::addNewDiagram @brief QETProject::addNewDiagram
Add a new diagram in project at position pos. Add a new diagram in project at position pos.
This is pushed as an undoable AddDiagramCommand: use undoStack()->undo()
to remove it again.
@param pos @param pos
@return the new created diagram @return the new created diagram
*/ */
@@ -1363,14 +1366,15 @@ Diagram *QETProject::addNewDiagram(int pos)
diagram->border_and_titleblock.importTitleBlock(defaultTitleBlockProperties()); diagram->border_and_titleblock.importTitleBlock(defaultTitleBlockProperties());
diagram->defaultConductorProperties = defaultConductorProperties(); diagram->defaultConductorProperties = defaultConductorProperties();
addDiagram(diagram, pos); m_undo_stack->push(new AddDiagramCommand(this, diagram, pos));
emit diagramAdded(this, diagram);
return(diagram); return(diagram);
} }
/** /**
@brief QETProject::removeDiagram @brief QETProject::removeDiagram
Remove diagram from project Remove diagram from project immediately (not undoable). UI code should
generally go through ProjectView::removeDiagram() instead, which pushes
an undoable RemoveDiagramCommand.
@param diagram @param diagram
*/ */
void QETProject::removeDiagram(Diagram *diagram) void QETProject::removeDiagram(Diagram *diagram)
@@ -1380,15 +1384,10 @@ void QETProject::removeDiagram(Diagram *diagram)
return; return;
} }
if (m_diagrams_list.removeAll(diagram)) detachDiagram(diagram);
{
emit diagramRemoved(this, diagram);
diagram->deleteLater(); diagram->deleteLater();
} }
updateDiagramsFolioData();
}
/** /**
Gere le fait que l'ordre des schemas ait change Gere le fait que l'ordre des schemas ait change
@param old_index ancien indice du schema deplace @param old_index ancien indice du schema deplace
@@ -1930,6 +1929,33 @@ void QETProject::addDiagram(Diagram *diagram, int pos)
} }
updateDiagramsFolioData(); updateDiagramsFolioData();
emit diagramAdded(this, diagram);
}
/**
@brief QETProject::detachDiagram
Inverse of addDiagram(): removes \p diagram from this project's diagram
list and disconnects the signals set up by addDiagram(), without
destroying it. Used by RemoveDiagramCommand (and removeDiagram()) so a
removed diagram can be parked and later reinserted by undo.
@param diagram
*/
void QETProject::detachDiagram(Diagram *diagram)
{
if (!diagram || !m_diagrams_list.contains(diagram)) {
return;
}
disconnect(&diagram->border_and_titleblock,
&BorderTitleBlock::needFolioData,
this,
&QETProject::updateDiagramsFolioData);
disconnect(diagram, &Diagram::usedTitleBlockTemplateChanged,
this, &QETProject::usedTitleBlockTemplateChanged);
m_diagrams_list.removeAll(diagram);
updateDiagramsFolioData();
emit diagramRemoved(this, diagram);
} }
/** /**
+3
View File
@@ -75,6 +75,8 @@ struct GuideProperties {
*/ */
class QETProject : public QObject class QETProject : public QObject
{ {
friend class AddDiagramCommand;
friend class RemoveDiagramCommand;
Q_OBJECT Q_OBJECT
public : public :
//This enum lists possible states for a particular project. //This enum lists possible states for a particular project.
@@ -257,6 +259,7 @@ class QETProject : public QObject
void writeDefaultPropertiesXml(QDomElement &); void writeDefaultPropertiesXml(QDomElement &);
void writeUsageXml(QDomElement &); void writeUsageXml(QDomElement &);
void addDiagram(Diagram *diagram, int pos = -1); void addDiagram(Diagram *diagram, int pos = -1);
void detachDiagram(Diagram *diagram);
void writeBackup(); void writeBackup();
void init(); void init();
ProjectState openFile(QFile *file); ProjectState openFile(QFile *file);
+68
View File
@@ -0,0 +1,68 @@
/*
Copyright 2006-2026 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#include "adddiagramcommand.h"
#include "../qetproject.h"
#include "../diagram.h"
/**
@brief AddDiagramCommand::AddDiagramCommand
@param project : project the diagram is added to
@param diagram : the already constructed diagram to add (not yet part of any project's diagram list)
@param pos : position to insert the diagram at, -1 to append at the end
@param parent
*/
AddDiagramCommand::AddDiagramCommand(QETProject *project, Diagram *diagram, int pos, QUndoCommand *parent) :
QUndoCommand(parent),
m_project(project),
m_diagram(diagram),
m_position(pos)
{
setText(QObject::tr("Ajouter un folio", "undo command text"));
}
/**
@brief AddDiagramCommand::~AddDiagramCommand
Destroy the diagram only if it is currently detached from the project
(i.e. this add was undone) -- otherwise the project owns it and will
destroy it itself.
*/
AddDiagramCommand::~AddDiagramCommand()
{
if (m_owns_diagram && m_diagram) {
m_diagram->deleteLater();
}
}
void AddDiagramCommand::redo()
{
if (!m_project || !m_diagram) {
return;
}
m_project->addDiagram(m_diagram, m_position);
m_owns_diagram = false;
}
void AddDiagramCommand::undo()
{
if (!m_project || !m_diagram) {
return;
}
m_project->detachDiagram(m_diagram);
m_owns_diagram = true;
}
+52
View File
@@ -0,0 +1,52 @@
/*
Copyright 2006-2026 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ADDDIAGRAMCOMMAND_H
#define ADDDIAGRAMCOMMAND_H
#include <QUndoCommand>
#include <QPointer>
class QETProject;
class Diagram;
/**
@brief The AddDiagramCommand class
Undo/redo support for adding a new folio (diagram) to a project.
Takes ownership of the (already constructed, not yet inserted) diagram:
redo() inserts it into the project, undo() detaches it again without
destroying it. The diagram is only actually destroyed when this command
itself is destroyed while the diagram is detached (i.e. while undone).
*/
class AddDiagramCommand : public QUndoCommand
{
public:
AddDiagramCommand(QETProject *project, Diagram *diagram, int pos = -1, QUndoCommand *parent = nullptr);
~AddDiagramCommand() override;
void undo() override;
void redo() override;
private:
QPointer<QETProject> m_project;
Diagram *m_diagram;
int m_position;
/// true while the diagram is detached from the project (not currently in its diagram list)
bool m_owns_diagram = true;
};
#endif // ADDDIAGRAMCOMMAND_H
@@ -0,0 +1,56 @@
/*
Copyright 2006-2026 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#include "movediagramcommand.h"
#include "../projectview.h"
#include "../qetproject.h"
/**
@brief MoveDiagramCommand::MoveDiagramCommand
@param project_view : project view the diagram's tab belongs to
@param diagram : the diagram to move
@param new_position : target position (may be out of range, e.g. for a
x10/x100 move near either end -- ProjectView::setDiagramPosition() clamps
it the same way the underlying tab bar always has)
@param parent
*/
MoveDiagramCommand::MoveDiagramCommand(ProjectView *project_view, Diagram *diagram, int new_position, QUndoCommand *parent) :
QUndoCommand(parent),
m_project_view(project_view),
m_diagram(diagram),
m_old_position(project_view ? project_view->project()->folioIndex(diagram) : -1),
m_new_position(new_position)
{
setText(QObject::tr("Déplacer un folio", "undo command text"));
}
void MoveDiagramCommand::redo()
{
if (!m_project_view) {
return;
}
m_project_view->setDiagramPosition(m_diagram, m_new_position);
}
void MoveDiagramCommand::undo()
{
if (!m_project_view) {
return;
}
m_project_view->setDiagramPosition(m_diagram, m_old_position);
}
+49
View File
@@ -0,0 +1,49 @@
/*
Copyright 2006-2026 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MOVEDIAGRAMCOMMAND_H
#define MOVEDIAGRAMCOMMAND_H
#include <QUndoCommand>
#include <QPointer>
class ProjectView;
class Diagram;
/**
@brief The MoveDiagramCommand class
Undo/redo support for reordering a folio (diagram) within a project's
tabs. Wraps ProjectView::setDiagramPosition(), which performs the tab
move and the matching (synchronous) project diagram-list reorder in one
step.
*/
class MoveDiagramCommand : public QUndoCommand
{
public:
MoveDiagramCommand(ProjectView *project_view, Diagram *diagram, int new_position, QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
private:
QPointer<ProjectView> m_project_view;
Diagram *m_diagram;
int m_old_position;
int m_new_position;
};
#endif // MOVEDIAGRAMCOMMAND_H
@@ -0,0 +1,67 @@
/*
Copyright 2006-2026 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#include "removediagramcommand.h"
#include "../qetproject.h"
#include "../diagram.h"
/**
@brief RemoveDiagramCommand::RemoveDiagramCommand
@param project : project the diagram is removed from
@param diagram : the diagram to remove, currently part of \p project
@param parent
*/
RemoveDiagramCommand::RemoveDiagramCommand(QETProject *project, Diagram *diagram, QUndoCommand *parent) :
QUndoCommand(parent),
m_project(project),
m_diagram(diagram),
m_position(project ? project->folioIndex(diagram) : -1)
{
setText(QObject::tr("Supprimer un folio", "undo command text"));
}
/**
@brief RemoveDiagramCommand::~RemoveDiagramCommand
Destroy the diagram only if it is currently detached from the project
(i.e. the removal is currently in effect) -- otherwise the project owns
it and will destroy it itself.
*/
RemoveDiagramCommand::~RemoveDiagramCommand()
{
if (m_owns_diagram && m_diagram) {
m_diagram->deleteLater();
}
}
void RemoveDiagramCommand::redo()
{
if (!m_project || !m_diagram) {
return;
}
m_project->detachDiagram(m_diagram);
m_owns_diagram = true;
}
void RemoveDiagramCommand::undo()
{
if (!m_project || !m_diagram) {
return;
}
m_project->addDiagram(m_diagram, m_position);
m_owns_diagram = false;
}
@@ -0,0 +1,53 @@
/*
Copyright 2006-2026 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef REMOVEDIAGRAMCOMMAND_H
#define REMOVEDIAGRAMCOMMAND_H
#include <QUndoCommand>
#include <QPointer>
class QETProject;
class Diagram;
/**
@brief The RemoveDiagramCommand class
Undo/redo support for removing a folio (diagram) from a project.
redo() detaches the diagram from the project (and, reactively, its
ProjectView tab) without destroying it; undo() reinserts it at its
original position. The diagram is only actually destroyed when this
command itself is destroyed while the diagram is detached (i.e. while
the removal is still in effect).
*/
class RemoveDiagramCommand : public QUndoCommand
{
public:
RemoveDiagramCommand(QETProject *project, Diagram *diagram, QUndoCommand *parent = nullptr);
~RemoveDiagramCommand() override;
void undo() override;
void redo() override;
private:
QPointer<QETProject> m_project;
Diagram *m_diagram;
int m_position;
/// true while the diagram is detached from the project (i.e. the removal is in effect)
bool m_owns_diagram = false;
};
#endif // REMOVEDIAGRAMCOMMAND_H