diff --git a/cmake/qet_compilation_vars.cmake b/cmake/qet_compilation_vars.cmake index affe12ab9..0b47ffd64 100644 --- a/cmake/qet_compilation_vars.cmake +++ b/cmake/qet_compilation_vars.cmake @@ -735,6 +735,8 @@ set(QET_SRC_FILES ${QET_DIR}/sources/undocommand/addelementtextcommand.cpp ${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.h ${QET_DIR}/sources/undocommand/changeelementdatacommand.cpp @@ -749,6 +751,10 @@ set(QET_SRC_FILES ${QET_DIR}/sources/undocommand/itemmodelcommand.h ${QET_DIR}/sources/undocommand/linkelementcommand.cpp ${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.h ${QET_DIR}/sources/undocommand/rotatetextscommand.cpp diff --git a/sources/projectview.cpp b/sources/projectview.cpp index 740092ac5..2978d9745 100644 --- a/sources/projectview.cpp +++ b/sources/projectview.cpp @@ -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 diff --git a/sources/projectview.h b/sources/projectview.h index 4b9a33e74..2f9b5c92a 100644 --- a/sources/projectview.h +++ b/sources/projectview.h @@ -102,6 +102,7 @@ class ProjectView : public QWidget void changeTabDown(); void changeFirstTab(); void changeLastTab(); + void setDiagramPosition(Diagram *diagram, int new_position); public slots: void removeDiagram(DiagramView *diagram_view, bool silent = false); @@ -168,6 +169,7 @@ class ProjectView : public QWidget void setDisplayFallbackWidget(bool); void adjustReadOnlyState(); void diagramAdded(Diagram *diagram); + void diagramRemoved(Diagram *diagram); // attributes private: diff --git a/sources/qetdiagrameditor.cpp b/sources/qetdiagrameditor.cpp index 88c239d6e..0ce820416 100644 --- a/sources/qetdiagrameditor.cpp +++ b/sources/qetdiagrameditor.cpp @@ -2249,17 +2249,26 @@ void QETDiagramEditor::removeDiagrams(const QList &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 &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 &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 &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 &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 &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 &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 &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(); } } } diff --git a/sources/qetproject.cpp b/sources/qetproject.cpp index f64440a09..3d205edfa 100644 --- a/sources/qetproject.cpp +++ b/sources/qetproject.cpp @@ -34,6 +34,7 @@ #include "TerminalStrip/terminalstrip.h" #include "qetxml.h" #include "qetversion.h" +#include "undocommand/adddiagramcommand.h" #include #include @@ -1348,6 +1349,8 @@ bool QETProject::usesTitleBlockTemplate(const TitleBlockTemplateLocation &locati /** @brief QETProject::addNewDiagram 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 @return the new created diagram */ @@ -1363,14 +1366,15 @@ Diagram *QETProject::addNewDiagram(int pos) diagram->border_and_titleblock.importTitleBlock(defaultTitleBlockProperties()); diagram->defaultConductorProperties = defaultConductorProperties(); - addDiagram(diagram, pos); - emit diagramAdded(this, diagram); + m_undo_stack->push(new AddDiagramCommand(this, diagram, pos)); return(diagram); } /** @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 */ void QETProject::removeDiagram(Diagram *diagram) @@ -1380,13 +1384,8 @@ void QETProject::removeDiagram(Diagram *diagram) return; } - if (m_diagrams_list.removeAll(diagram)) - { - emit diagramRemoved(this, diagram); - diagram->deleteLater(); - } - - updateDiagramsFolioData(); + detachDiagram(diagram); + diagram->deleteLater(); } /** @@ -1930,6 +1929,33 @@ void QETProject::addDiagram(Diagram *diagram, int pos) } 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); } /** diff --git a/sources/qetproject.h b/sources/qetproject.h index 5e1a1a368..dfd5ab8ea 100644 --- a/sources/qetproject.h +++ b/sources/qetproject.h @@ -75,6 +75,8 @@ struct GuideProperties { */ class QETProject : public QObject { + friend class AddDiagramCommand; + friend class RemoveDiagramCommand; Q_OBJECT public : //This enum lists possible states for a particular project. @@ -257,6 +259,7 @@ class QETProject : public QObject void writeDefaultPropertiesXml(QDomElement &); void writeUsageXml(QDomElement &); void addDiagram(Diagram *diagram, int pos = -1); + void detachDiagram(Diagram *diagram); void writeBackup(); void init(); ProjectState openFile(QFile *file); diff --git a/sources/undocommand/adddiagramcommand.cpp b/sources/undocommand/adddiagramcommand.cpp new file mode 100644 index 000000000..3a68e59ec --- /dev/null +++ b/sources/undocommand/adddiagramcommand.cpp @@ -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 . +*/ +#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; +} diff --git a/sources/undocommand/adddiagramcommand.h b/sources/undocommand/adddiagramcommand.h new file mode 100644 index 000000000..04dd18faf --- /dev/null +++ b/sources/undocommand/adddiagramcommand.h @@ -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 . +*/ +#ifndef ADDDIAGRAMCOMMAND_H +#define ADDDIAGRAMCOMMAND_H + +#include +#include + +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 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 diff --git a/sources/undocommand/movediagramcommand.cpp b/sources/undocommand/movediagramcommand.cpp new file mode 100644 index 000000000..0ed006329 --- /dev/null +++ b/sources/undocommand/movediagramcommand.cpp @@ -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 . +*/ +#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); +} diff --git a/sources/undocommand/movediagramcommand.h b/sources/undocommand/movediagramcommand.h new file mode 100644 index 000000000..6f0e7aebc --- /dev/null +++ b/sources/undocommand/movediagramcommand.h @@ -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 . +*/ +#ifndef MOVEDIAGRAMCOMMAND_H +#define MOVEDIAGRAMCOMMAND_H + +#include +#include + +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 m_project_view; + Diagram *m_diagram; + int m_old_position; + int m_new_position; +}; + +#endif // MOVEDIAGRAMCOMMAND_H diff --git a/sources/undocommand/removediagramcommand.cpp b/sources/undocommand/removediagramcommand.cpp new file mode 100644 index 000000000..ad9d375c0 --- /dev/null +++ b/sources/undocommand/removediagramcommand.cpp @@ -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 . +*/ +#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; +} diff --git a/sources/undocommand/removediagramcommand.h b/sources/undocommand/removediagramcommand.h new file mode 100644 index 000000000..3cc70fda6 --- /dev/null +++ b/sources/undocommand/removediagramcommand.h @@ -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 . +*/ +#ifndef REMOVEDIAGRAMCOMMAND_H +#define REMOVEDIAGRAMCOMMAND_H + +#include +#include + +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 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