/* Copyright 2006-2025 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 "deleteqgraphicsitemcommand.h" #include "../diagram.h" #include "addgraphicsobjectcommand.h" #include "../qetdiagrameditor.h" #include "../qetgraphicsitem/ViewItem/qetgraphicstableitem.h" #include "../qetgraphicsitem/conductor.h" #include "../qetgraphicsitem/conductortextitem.h" #include "../qetgraphicsitem/dynamicelementtextitem.h" #include "../qetgraphicsitem/element.h" #include "../qetgraphicsitem/elementtextitemgroup.h" #include "../qetgraphicsitem/terminal.h" #include "addelementtextcommand.h" /** @brief DeleteQGraphicsItemCommand::DeleteQGraphicsItemCommand @param diagram : diagram where this undo work @param content : content to remove @param parent : parent undo */ DeleteQGraphicsItemCommand::DeleteQGraphicsItemCommand( Diagram *diagram, const DiagramContent &content, QUndoCommand *parent) : QUndoCommand(parent), m_removed_contents(content), m_diagram(diagram) { //If parent element of a dynamic element text item is also in m_removed_content, //we remove it, because when the element will be removed from the scene every child's will also be removed. const QSet elmt_set = m_removed_contents.m_element_texts; for(DynamicElementTextItem *deti : elmt_set) { if (m_removed_contents.m_elements.contains(deti->parentElement())) m_removed_contents.m_element_texts.remove(deti); } //When removing a deti we must know its parent item, for re-adding deti as child of the parent //when undoing this command for(DynamicElementTextItem *deti : m_removed_contents.m_element_texts) { if(deti->parentGroup()) m_grp_texts_hash.insert(deti, deti->parentGroup()); else m_elmt_text_hash.insert(deti, deti->parentElement()); } //If parent element of ElementTextItemGroup is also in m_removed_content, //we remove it, because when the element will be removed from the scene every child's will also be removed. const QSet group_set = m_removed_contents.m_texts_groups; for(ElementTextItemGroup *group : group_set) { if(m_removed_contents.m_elements.contains(group->parentElement())) m_removed_contents.m_texts_groups.remove(group); } //The deletion of the groups is not managed by this undo, but by a RemoveTextsGroupCommand for(ElementTextItemGroup *group : m_removed_contents.m_texts_groups) { new RemoveTextsGroupCommand(group->parentElement(), group, this); } m_removed_contents.m_texts_groups.clear(); setPotentialsOfRemovedElements(); //Get all linkeds table of removed table. for (auto table : m_removed_contents.m_tables) { //Table is already managed, jump to next loop if (m_table_scene_hash.keys().contains(table)) continue; auto first_table = table; //The first table if the table is linked to another while(first_table->previousTable()) first_table = first_table->previousTable(); auto current_table = first_table; m_table_scene_hash.insert(first_table, first_table->scene()); while (current_table->nextTable()) { current_table = current_table->nextTable(); m_table_scene_hash.insert(current_table, current_table->scene()); } } setText(QString(QObject::tr( "supprimer %1", "undo caption - %1 is a sentence listing the removed content")) .arg(m_removed_contents.sentence(DiagramContent::All))); //Table is now managed by m_table_scene_hash, //we clear the tables of m_removed_content m_removed_contents.m_tables.clear(); m_diagram->qgiManager().manage(m_removed_contents.items(DiagramContent::All)); } DeleteQGraphicsItemCommand::~DeleteQGraphicsItemCommand() { m_diagram->qgiManager().release(m_removed_contents.items(DiagramContent::All)); } /** @brief DeleteQGraphicsItemCommand::setPotentialsOfRemovedElements This function creates new conductors (if needed) for conserve the electrical potentials present at the terminals of each removed elements. */ void DeleteQGraphicsItemCommand::setPotentialsOfRemovedElements() { for (Element *elmt : m_removed_contents.m_elements) { //a list of terminals who have at least two conductors docked in. QList terminals_list; for (Terminal *t : elmt->terminals()) { if (t->conductors().size() >= 2) { terminals_list.append(t); } } if (terminals_list.isEmpty()) { continue; } for (Terminal *t : terminals_list) { //All new created conductors will be docked to hub_terminal Terminal *hub_terminal = nullptr; QList terminals_to_connect_list; for (Conductor *c : t->conductors()) { Terminal *other_terminal = c->terminal1 == t ? c->terminal2 : c->terminal1; if (m_removed_contents.items(DiagramContent::Elements).contains(other_terminal->parentElement())) { other_terminal = terminalInSamePotential(other_terminal, c); if (other_terminal == nullptr) { continue; } } terminals_to_connect_list.append(other_terminal); if (hub_terminal == nullptr) { hub_terminal = other_terminal; } //hub_terminal must be the terminal the more at top left of the diagram. else if (other_terminal->scenePos().x() < hub_terminal->scenePos().x()) { hub_terminal = other_terminal; } else if (other_terminal->scenePos().x() == hub_terminal->scenePos().x()) { if (other_terminal->scenePos().y() < hub_terminal->scenePos().y()) { hub_terminal = other_terminal; } } } terminals_to_connect_list.removeAll(hub_terminal); if (hub_terminal == nullptr || terminals_to_connect_list.isEmpty()) { continue; } ConductorProperties properties = hub_terminal->conductors().first()->properties(); for (Terminal *t : terminals_to_connect_list) { //If a conductor was already created between these two terminals //in this undo command, from another removed element, we do nothing bool exist_ = false; for (QPair pair : m_connected_terminals) { if (pair.first == hub_terminal && pair.second == t) { exist_ = true; continue; } else if (pair.first == t && pair.second == hub_terminal) { exist_ = true; continue; } } if (exist_ == false) { #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove m_connected_terminals.append(qMakePair(hub_terminal, t)); #else #if TODO_LIST #pragma message("@TODO remove code for QT 6 or later") #endif qDebug()<<"Help code for QT 6 or later"; #endif Conductor *new_cond = new Conductor(hub_terminal, t); new_cond->setProperties(properties); new AddGraphicsObjectCommand(new_cond, t->diagram(), QPointF(), this); } } } } } /** @brief DeleteQGraphicsItemCommand::terminalInSamePotential Return a terminal at the same potential of terminal, by traveling through the conductors connected to terminal only if the owner element of the terminal is not delete by this undo command. Return nullptr if a terminal can't be found. @param terminal - terminal from search @param conductor_to_exclude - a conductor to exlcude from search. @return */ Terminal *DeleteQGraphicsItemCommand::terminalInSamePotential( Terminal *terminal, Conductor *conductor_to_exclude) { QList conductor_list = terminal->conductors(); conductor_list.removeAll(conductor_to_exclude); for(Conductor *c : conductor_list) { Terminal *other_terminal = c->terminal1 == terminal ? c->terminal2 : c->terminal1; if(!m_removed_contents.items(DiagramContent::Elements).contains(other_terminal->parentElement())) { return other_terminal; } } //No one of direct conductor of terminal are docked to an element which is not removed for(Conductor *c : conductor_list) { Terminal *other_terminal = c->terminal1 == terminal ? c->terminal2 : c->terminal1; Terminal *terminal_to_return = terminalInSamePotential(other_terminal, c); if (terminal_to_return != nullptr) { return terminal_to_return; } } return nullptr; } /** @brief DeleteQGraphicsItemCommand::undo Undo this command */ void DeleteQGraphicsItemCommand::undo() { m_diagram->showMe(); for(QGraphicsItem *item : m_removed_contents.items()) m_diagram->addItem(item); //We relink element after every element was added to diagram for(Element *e : m_removed_contents.m_elements) for(Element *elmt : m_link_hash[e]) e->linkToElement(elmt); for(DynamicElementTextItem *deti : m_removed_contents.m_element_texts) { if(m_elmt_text_hash.keys().contains(deti)) m_elmt_text_hash.value(deti)->addDynamicTextItem(deti); else if (m_grp_texts_hash.keys().contains(deti)) { Element *elmt = m_grp_texts_hash.value(deti)->parentElement(); elmt->addDynamicTextItem(deti); elmt->addTextToGroup(deti, m_grp_texts_hash.value(deti)); } } for (auto table : m_table_scene_hash.keys()) { if (!m_table_scene_hash.value(table).isNull()) { m_table_scene_hash.value(table)->addItem(table); } } QUndoCommand::undo(); } /** @brief DeleteQGraphicsItemCommand::redo Redo the delete command */ void DeleteQGraphicsItemCommand::redo() { m_diagram -> showMe(); for(Conductor *c : m_removed_contents.conductors(DiagramContent::AnyConductor)) { //If option one text per folio is enable, and the text item of //current conductor is visible (that mean the conductor have the single displayed text) //We call adjustTextItemPosition to other conductor at the same potential to keep //a visible text on this potential. if (m_diagram -> defaultConductorProperties.m_one_text_per_folio && c -> textItem() -> isVisible()) { QList conductor_list; conductor_list << c -> relatedPotentialConductors(false).values(); if (conductor_list.count()) conductor_list.first() -> calculateTextItemPosition(); } } for(Element *e : m_removed_contents.m_elements) { //Get linked element, for relink it at undo if (!e->linkedElements().isEmpty()) m_link_hash.insert(e, e->linkedElements()); } for(DynamicElementTextItem *deti : m_removed_contents.m_element_texts) { if(deti->parentGroup() && deti->parentGroup()->parentElement()) deti->parentGroup()->parentElement()->removeTextFromGroup(deti, deti->parentGroup()); deti->parentElement()->removeDynamicTextItem(deti); deti->setParentItem(nullptr); } for (auto table : m_table_scene_hash.keys()) { if (!m_table_scene_hash.value(table).isNull()) { m_table_scene_hash.value(table)->removeItem(table); } } for(QGraphicsItem *item : m_removed_contents.items()) m_diagram->removeItem(item); QUndoCommand::redo(); }