mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-08-13 10:04:13 +02:00
d95e744494
When multiple elements are pasted or moved in one batch, each call to autoBreakConductors() now receives the shared state from the previous call. This prevents two elements in the same batch from independently claiming the same conductor, which would result in a double-delete on redo(). Requested by ispyisail in PR review.
510 lines
12 KiB
C++
510 lines
12 KiB
C++
/*
|
|
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 "diagramcommands.h"
|
|
|
|
#include "autobreakconductor.h"
|
|
#include "diagram.h"
|
|
#include "qetproject.h"
|
|
#include "qetgraphicsitem/conductortextitem.h"
|
|
#include "qetgraphicsitem/element.h"
|
|
#include "qetgraphicsitem/elementtextitemgroup.h"
|
|
#include "qgimanager.h"
|
|
|
|
/**
|
|
@brief PasteDiagramCommand::PasteDiagramCommand
|
|
Constructor
|
|
@param dia : diagram where we must paste
|
|
@param c : content to paste
|
|
@param parent : parent undo command
|
|
*/
|
|
PasteDiagramCommand::PasteDiagramCommand( Diagram *dia, const DiagramContent &c, QUndoCommand *parent) :
|
|
QUndoCommand(parent),
|
|
content(c),
|
|
diagram(dia),
|
|
filter(DiagramContent::Elements|DiagramContent::TextFields|DiagramContent::Images|DiagramContent::ConductorsToMove | DiagramContent::Shapes),
|
|
first_redo(true)
|
|
{
|
|
setText(QObject::tr("coller %1", "undo caption - %1 is a sentence listing the content to paste").arg(content.sentence(filter)));
|
|
diagram -> qgiManager().manage(content.items(filter));
|
|
}
|
|
|
|
/**
|
|
@brief PasteDiagramCommand::~PasteDiagramCommand
|
|
Destructor
|
|
*/
|
|
PasteDiagramCommand::~PasteDiagramCommand()
|
|
{
|
|
diagram -> qgiManager().release(content.items(filter));
|
|
delete m_break_cmd;
|
|
}
|
|
|
|
/**
|
|
@brief PasteDiagramCommand::undo
|
|
Undo this command
|
|
*/
|
|
void PasteDiagramCommand::undo()
|
|
{
|
|
diagram -> showMe();
|
|
|
|
//Undo auto-break before removing items, so terminals are still on scene
|
|
if (m_break_cmd)
|
|
m_break_cmd->undo();
|
|
|
|
foreach(QGraphicsItem *item, content.items(filter))
|
|
diagram->removeItem(item);
|
|
}
|
|
|
|
/**
|
|
@brief PasteDiagramCommand::redo
|
|
Redo this command
|
|
*/
|
|
void PasteDiagramCommand::redo()
|
|
{
|
|
diagram -> showMe();
|
|
QSettings settings;
|
|
|
|
if (first_redo)
|
|
{
|
|
first_redo = false;
|
|
|
|
//this is the first paste, we do some actions for the new element
|
|
const QList <Element *> elmts_list = content.m_elements;
|
|
for (Element *e : elmts_list)
|
|
{
|
|
//make new uuid, because old uuid are the uuid of the copied element
|
|
e -> newUuid();
|
|
|
|
if (settings.value("diagramcommands/erase-label-on-copy", true).toBool())
|
|
{
|
|
//Reset the information about the label, the comment and location
|
|
DiagramContext dc = e->elementInformations();
|
|
dc.addValue("formula", "");
|
|
dc.addValue("label", "");
|
|
dc.addValue("comment", "");
|
|
dc.addValue("location", "");
|
|
e->setElementInformations(dc);
|
|
|
|
//Reset the text of conductors
|
|
const QList <Conductor *> conductors_list = content.m_conductors_to_move;
|
|
for (Conductor *c : conductors_list)
|
|
{
|
|
ConductorProperties cp = c -> properties();
|
|
cp.text = c->diagram() ? c -> diagram() -> defaultConductorProperties.text : "_";
|
|
c -> setProperties(cp);
|
|
}
|
|
}
|
|
}
|
|
|
|
//Auto-break conductors on first paste. Items are already on the
|
|
//scene at this point (added before this command was created).
|
|
if (diagram->project()->autoBreakConductor())
|
|
{
|
|
m_break_cmd = new QUndoCommand();
|
|
QList<Conductor *> conductors_handled;
|
|
QSet<Terminal *> used_terminals;
|
|
for (Element *e : content.m_elements) {
|
|
autoBreakConductors(diagram, e, m_break_cmd,
|
|
conductors_handled, used_terminals);
|
|
}
|
|
if (m_break_cmd->childCount() == 0) {
|
|
delete m_break_cmd;
|
|
m_break_cmd = nullptr;
|
|
}
|
|
else
|
|
{
|
|
m_break_cmd->redo();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const QList<QGraphicsItem *> qgis_list = content.items(filter);
|
|
for (QGraphicsItem *item : qgis_list) {
|
|
diagram->addItem(item);
|
|
}
|
|
|
|
//Re-execute the stored break commands (items are back on scene)
|
|
if (m_break_cmd)
|
|
m_break_cmd->redo();
|
|
}
|
|
|
|
const QList<QGraphicsItem *> qgis_list = content.items();
|
|
for (QGraphicsItem *qgi : qgis_list)
|
|
qgi -> setSelected(true);
|
|
}
|
|
|
|
/**
|
|
@brief CutDiagramCommand::CutDiagramCommand
|
|
Constructeur
|
|
@param dia Schema dont on coupe des elements et conducteurs
|
|
@param content Contenu coupe
|
|
@param parent QUndoCommand parent
|
|
*/
|
|
CutDiagramCommand::CutDiagramCommand(
|
|
Diagram *dia,
|
|
const DiagramContent &content,
|
|
QUndoCommand *parent
|
|
) :
|
|
DeleteQGraphicsItemCommand(dia, content, parent)
|
|
{
|
|
setText(
|
|
QString(
|
|
QObject::tr(
|
|
"couper %1",
|
|
"undo caption - %1 is a sentence listing the content to cut"
|
|
).arg(content.sentence(DiagramContent::All))
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
@brief CutDiagramCommand::~CutDiagramCommand
|
|
Destructeur
|
|
*/
|
|
CutDiagramCommand::~CutDiagramCommand()
|
|
{
|
|
}
|
|
|
|
/**
|
|
@brief MoveConductorsTextsCommand::MoveConductorsTextsCommand
|
|
Constructeur
|
|
@param diagram Schema sur lequel on deplace des champs de texte
|
|
@param parent QUndoCommand parent
|
|
*/
|
|
MoveConductorsTextsCommand::MoveConductorsTextsCommand(
|
|
Diagram *diagram,
|
|
QUndoCommand *parent
|
|
) :
|
|
QUndoCommand(parent),
|
|
diagram(diagram),
|
|
first_redo(true)
|
|
{
|
|
}
|
|
|
|
/**
|
|
@brief MoveConductorsTextsCommand::~MoveConductorsTextsCommand
|
|
Destructeur
|
|
*/
|
|
MoveConductorsTextsCommand::~MoveConductorsTextsCommand()
|
|
{
|
|
}
|
|
|
|
/**
|
|
@brief MoveConductorsTextsCommand::undo
|
|
annule le deplacement
|
|
*/
|
|
void MoveConductorsTextsCommand::undo()
|
|
{
|
|
diagram -> showMe();
|
|
foreach(ConductorTextItem *cti, texts_to_move_.keys()) {
|
|
QPointF movement = texts_to_move_[cti].first;
|
|
bool was_already_moved = texts_to_move_[cti].second;
|
|
|
|
cti -> forceMovedByUser(was_already_moved);
|
|
if (was_already_moved) {
|
|
cti -> setPos(cti -> pos() - movement);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
@brief MoveConductorsTextsCommand::redo
|
|
refait le deplacement
|
|
*/
|
|
void MoveConductorsTextsCommand::redo()
|
|
{
|
|
diagram -> showMe();
|
|
if (first_redo) {
|
|
first_redo = false;
|
|
} else {
|
|
foreach(ConductorTextItem *cti, texts_to_move_.keys()) {
|
|
QPointF movement = texts_to_move_[cti].first;
|
|
|
|
cti -> forceMovedByUser(true);
|
|
cti -> setPos(cti -> pos() + movement);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
@brief MoveConductorsTextsCommand::addTextMovement
|
|
Ajout un mouvement de champ de texte a cet objet
|
|
@param text_item Champ de texte deplace ;
|
|
si celui-ci est deja connu de l'objet d'annulation,
|
|
il sera ignore
|
|
@param old_pos Position du champ de texte avant le mouvement
|
|
@param new_pos Position du champ de texte apres le mouvement
|
|
@param already_moved true si le champ de texte etait deja a une position
|
|
personnalisee par l'utilisateur, false sinon
|
|
*/
|
|
void MoveConductorsTextsCommand::addTextMovement(ConductorTextItem *text_item,
|
|
const QPointF &old_pos,
|
|
const QPointF &new_pos,
|
|
bool already_moved) {
|
|
// si le champ de texte est deja connu de l'objet d'annulation, il sera ignore
|
|
if (texts_to_move_.contains(text_item)) return;
|
|
|
|
// on memorise le champ de texte,
|
|
//en l'associant au mouvement effectue et a son etat avant le deplacement
|
|
texts_to_move_.insert(text_item, qMakePair(new_pos - old_pos, already_moved));
|
|
|
|
// met a jour la description de l'objet d'annulation
|
|
regenerateTextLabel();
|
|
}
|
|
|
|
/**
|
|
@brief MoveConductorsTextsCommand::regenerateTextLabel
|
|
Genere la description de l'objet d'annulation
|
|
*/
|
|
void MoveConductorsTextsCommand::regenerateTextLabel()
|
|
{
|
|
QString moved_content_sentence = QET::ElementsAndConductorsSentence(0, 0, texts_to_move_.count());
|
|
|
|
setText(
|
|
QString(
|
|
QObject::tr(
|
|
"déplacer %1",
|
|
"undo caption - %1 is a sentence listing the moved content"
|
|
).arg(moved_content_sentence)
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
@brief ChangeDiagramTextCommand::ChangeDiagramTextCommand
|
|
Constructeur
|
|
@param dti Champ de texte modifie
|
|
@param before texte avant
|
|
@param after texte apres
|
|
@param parent QUndoCommand parent
|
|
*/
|
|
ChangeDiagramTextCommand::ChangeDiagramTextCommand(
|
|
DiagramTextItem *dti,
|
|
const QString &before,
|
|
const QString &after,
|
|
QUndoCommand *parent
|
|
) :
|
|
QUndoCommand(QObject::tr("modifier le texte", "undo caption"), parent),
|
|
text_item(dti),
|
|
text_before(before),
|
|
text_after(after),
|
|
first_redo(true),
|
|
diagram(dti->diagram())
|
|
{
|
|
}
|
|
|
|
/**
|
|
@brief ChangeDiagramTextCommand::~ChangeDiagramTextCommand
|
|
destructeur
|
|
*/
|
|
ChangeDiagramTextCommand::~ChangeDiagramTextCommand()
|
|
{
|
|
}
|
|
|
|
/**
|
|
@brief ChangeDiagramTextCommand::undo
|
|
annule la modification de texte
|
|
*/
|
|
void ChangeDiagramTextCommand::undo()
|
|
{
|
|
diagram -> showMe();
|
|
text_item -> setHtml(text_before);
|
|
}
|
|
|
|
/**
|
|
@brief ChangeDiagramTextCommand::redo
|
|
*/
|
|
void ChangeDiagramTextCommand::redo()
|
|
{
|
|
diagram -> showMe();
|
|
text_item->setHtml(text_after);
|
|
}
|
|
|
|
/**
|
|
@brief ChangeConductorCommand::ChangeConductorCommand
|
|
Constructeur
|
|
@param c Conducteur modifie
|
|
@param old_p ancien profil du conducteur
|
|
@param new_p nouveau profil du conducteur
|
|
@param path_t Trajectoire du trajet modifie
|
|
@param parent QUndoCommand parent
|
|
*/
|
|
ChangeConductorCommand::ChangeConductorCommand(
|
|
Conductor *c,
|
|
const ConductorProfile &old_p,
|
|
const ConductorProfile &new_p,
|
|
Qt::Corner path_t,
|
|
QUndoCommand *parent
|
|
) :
|
|
QUndoCommand(QObject::tr("modifier un conducteur", "undo caption"), parent),
|
|
conductor(c),
|
|
old_profile(old_p),
|
|
new_profile(new_p),
|
|
path_type(path_t),
|
|
first_redo(true),
|
|
diagram (c->diagram())
|
|
{
|
|
}
|
|
|
|
/**
|
|
@brief ChangeConductorCommand::~ChangeConductorCommand
|
|
Destructeur
|
|
*/
|
|
ChangeConductorCommand::~ChangeConductorCommand()
|
|
{
|
|
}
|
|
|
|
/**
|
|
@brief ChangeConductorCommand::undo
|
|
Annule la modification du conducteur
|
|
*/
|
|
void ChangeConductorCommand::undo()
|
|
{
|
|
diagram -> showMe();
|
|
conductor -> setProfile(old_profile, path_type);
|
|
conductor -> textItem() -> setPos(text_pos_before_mov_);
|
|
}
|
|
|
|
/**
|
|
@brief ChangeConductorCommand::redo
|
|
Refait la modification du conducteur
|
|
*/
|
|
void ChangeConductorCommand::redo()
|
|
{
|
|
diagram -> showMe();
|
|
if (first_redo) {
|
|
first_redo = false;
|
|
} else {
|
|
conductor -> setProfile(new_profile, path_type);
|
|
conductor -> textItem() -> setPos(text_pos_after_mov_);
|
|
}
|
|
}
|
|
|
|
/**
|
|
@brief ChangeConductorCommand::setConductorTextItemMove
|
|
Integre dans cet objet d'annulation le repositionnement du champ de texte
|
|
du conducteur
|
|
@param pos_before Position du texte avant la modification du conducteur
|
|
@param pos_after Position du texte apres la modification du conducteur
|
|
*/
|
|
void ChangeConductorCommand::setConductorTextItemMove(const QPointF &pos_before, const QPointF &pos_after) {
|
|
text_pos_before_mov_ = pos_before;
|
|
text_pos_after_mov_ = pos_after;
|
|
}
|
|
|
|
/**
|
|
@brief ResetConductorCommand::ResetConductorCommand
|
|
@param cp
|
|
@param parent
|
|
*/
|
|
ResetConductorCommand::ResetConductorCommand(
|
|
const QHash<Conductor *, ConductorProfilesGroup> &cp,
|
|
QUndoCommand *parent
|
|
) :
|
|
QUndoCommand(parent),
|
|
conductors_profiles(cp),
|
|
diagram(cp.keys().first()->diagram())
|
|
{
|
|
setText(
|
|
QObject::tr(
|
|
"Réinitialiser %1",
|
|
"undo caption - %1 is a sentence listing the reset content"
|
|
).arg(QET::ElementsAndConductorsSentence(0, cp.count()))
|
|
);
|
|
}
|
|
|
|
/**
|
|
@brief ResetConductorCommand::~ResetConductorCommand
|
|
*/
|
|
ResetConductorCommand::~ResetConductorCommand()
|
|
{
|
|
}
|
|
|
|
/**
|
|
@brief ResetConductorCommand::undo
|
|
*/
|
|
void ResetConductorCommand::undo()
|
|
{
|
|
diagram -> showMe();
|
|
foreach(Conductor *c, conductors_profiles.keys()) {
|
|
c -> setProfiles(conductors_profiles[c]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
@brief ResetConductorCommand::redo
|
|
*/
|
|
void ResetConductorCommand::redo()
|
|
{
|
|
diagram -> showMe();
|
|
foreach(Conductor *c, conductors_profiles.keys()) {
|
|
c -> textItem() -> forceMovedByUser (false);
|
|
c -> textItem() -> forceRotateByUser (false);
|
|
c -> setProfiles(ConductorProfilesGroup());
|
|
}
|
|
}
|
|
|
|
/**
|
|
@brief ChangeBorderCommand::ChangeBorderCommand
|
|
Constructeur
|
|
@param dia Schema modifie
|
|
@param old_bp Anciennes proprietes du cadre du schema
|
|
@param new_bp Nouvelles proprietes du cadre du schema
|
|
@param parent QUndoCommand parent
|
|
*/
|
|
ChangeBorderCommand::ChangeBorderCommand(Diagram *dia,
|
|
const BorderProperties &old_bp,
|
|
const BorderProperties &new_bp,
|
|
QUndoCommand *parent) :
|
|
QUndoCommand(
|
|
QObject::tr("modifier les dimensions du folio", "undo caption"),
|
|
parent),
|
|
diagram(dia),
|
|
old_properties(old_bp),
|
|
new_properties(new_bp)
|
|
{
|
|
}
|
|
|
|
/**
|
|
@brief ChangeBorderCommand::~ChangeBorderCommand
|
|
Destructeur
|
|
*/
|
|
ChangeBorderCommand::~ChangeBorderCommand()
|
|
{
|
|
}
|
|
|
|
/**
|
|
@brief ChangeBorderCommand::undo
|
|
Annule les changements apportes au schema
|
|
*/
|
|
void ChangeBorderCommand::undo()
|
|
{
|
|
diagram -> showMe();
|
|
diagram -> border_and_titleblock.importBorder(old_properties);
|
|
}
|
|
|
|
/**
|
|
@brief ChangeBorderCommand::redo
|
|
Refait les changements apportes au schema
|
|
*/
|
|
void ChangeBorderCommand::redo()
|
|
{
|
|
diagram -> showMe();
|
|
diagram -> border_and_titleblock.importBorder(new_properties);
|
|
}
|