/*
Copyright 2006-2017 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 "linkelementcommand.h"
#include "element.h"
#include "diagram.h"
#include "conductorautonumerotation.h"
#include "conductor.h"
#include "potentialselectordialog.h"
/**
* @brief LinkElementCommand::LinkElementCommand
* Constructor
* @param element_ : element where we work the link / unlink
* @param parent : parent undo
*/
LinkElementCommand::LinkElementCommand(Element *element_, QUndoCommand *parent):
QUndoCommand(parent),
m_element(element_),
m_first_redo (true)
{
m_linked_before = m_linked_after = m_element->linkedElements();
setText(QObject::tr("Éditer les référence croisé", "edite the cross reference"));
}
/**
* @brief LinkElementCommand::mergeWith
* @param other try to merge this command with other
* @return true if merge with success else false
*/
bool LinkElementCommand::mergeWith(const QUndoCommand *other)
{
if (id() != other->id() || other->childCount()) return false;
LinkElementCommand const *undo = static_cast (other);
if (m_element != undo->m_element) return false;
m_linked_after = undo->m_linked_after;
return true;
}
/**
* @brief LinkElementCommand::isLinkable
* @param element_a
* @param element_b
* @param already_linked
* @return true if element_a and element_b can be linked between them.
* There is few condition to be linked :
* 1- element_a and element_b must be linkable type. (Ex : A is master and B is slave 'OK', A and B is master 'KO')
* 2- For element type slave and report (no matter if element is 'A' or 'B'), the element must be free (not connected to an element)
* 3- we can override the section 2 by set already_linked to true. In this case, if slave or report is already
* linked to the other element ('A' or 'B') return true, but if linked to another element (not 'A' or 'B') return false
*/
bool LinkElementCommand::isLinkable(Element *element_a, Element *element_b, bool already_linked)
{
switch(element_a->linkType())
{
case Element::Simple: return false;
case Element::NextReport:
{
//Type isn't good
if (element_b->linkType() != Element::PreviousReport) return false;
//two report is free
if (element_a->isFree() && element_b->isFree()) return true;
//Reports aren't free but are already linked between them and and already_linked is true
if (element_a->linkedElements().contains(element_b) && already_linked) return true;
return false;
}
case Element::PreviousReport:
{
//Type isn't good
if (element_b->linkType() != Element::NextReport) return false;
//two report is free
if (element_a->isFree() && element_b->isFree()) return true;
//Reports aren't free but are already linked between them and and already_linked is true
if (element_a->linkedElements().contains(element_b) && already_linked) return true;
return false;
}
case Element::Master:
{
//Type isn't good
if (element_b->linkType() != Element::Slave) return false;
//element_b is free
if (element_b->isFree()) return true;
//element_b isn't free but already linked to element_a and already_linked is true
if (element_a->linkedElements().contains(element_b) && already_linked) return true;
return false;
}
case Element::Slave:
{
//Type isn't good
if (element_b->linkType() != Element::Master) return false;
//Element_a is free
if (element_a->isFree()) return true;
//element_a isn't free but already linked to element_b and already_linked is true;
if (element_b->linkedElements().contains(element_a) && already_linked) return true;
return false;
}
case Element::Terminale: return false;
default: return false;
}
}
/**
* @brief LinkElementCommand::setLink
* Replace all linked elements of edited element by elements stored in @element_list
* This method do several check to know if element can be linked or not.
* @param element_list
*/
void LinkElementCommand::setLink(const QList& element_list)
{
m_linked_after.clear();
setUpNewLink(element_list, true);
}
/**
* @brief LinkElementCommand::setLink
* This is an overloaded function.
* @param element_
*/
void LinkElementCommand::setLink(Element *element_)
{
QList list;
list << element_;
setLink(list);
}
/**
* @brief LinkElementCommand::unlink
* Unlink all elements of element_list from the edited element.
* @param element_list
*/
void LinkElementCommand::unlink(QList element_list)
{
foreach(Element *elmt, element_list)
m_linked_after.removeAll(elmt);
}
/**
* @brief LinkElementCommand::unlinkAll
* Unlink all element of the edited element
*/
void LinkElementCommand::unlinkAll() {
m_linked_after.clear();
}
/**
* @brief LinkElementCommand::undo
* Undo this command
*/
void LinkElementCommand::undo()
{
if(m_element->diagram()) m_element->diagram()->showMe();
makeLink(m_linked_before);
QUndoCommand::undo();
}
/**
* @brief LinkElementCommand::redo
* Redo this command
*/
void LinkElementCommand::redo()
{
if(m_element->diagram()) m_element->diagram()->showMe();
makeLink(m_linked_after);
//If the action is to link two reports together, we check if the conductors
//of the new potential have the same text, function, and protocol.
//if not, a dialog ask what do to.
if (m_first_redo && (m_element->linkType() & Element::AllReport) \
&& m_element->conductors().size() \
&& m_linked_after.size() && m_linked_after.first()->conductors().size())
{
//fill list of potential
QSet c_list = m_element->conductors().first()->relatedPotentialConductors();
c_list << m_element->conductors().first();
//fill list of text
QStringList str_txt;
QStringList str_funct;
QStringList str_tens;
for (const Conductor *c : c_list)
{
str_txt << c->properties().text;
str_funct << c->properties().m_function;
str_tens << c->properties().m_tension_protocol;
}
//check text list, isn't same in potential, ask user what to do
if (!QET::eachStrIsEqual(str_txt) || !QET::eachStrIsEqual(str_funct) || !QET::eachStrIsEqual(str_tens))
{
PotentialSelectorDialog psd(m_element, this);
psd.exec();
}
m_first_redo = false;
}
QUndoCommand::redo();
}
/**
* @brief LinkElementCommand::setUpNewLink
* Update the content of m_link_after with the content of @element_list.
* Each linkable element (know via the static method isLinkable) is added to m_linked_after
* @already_link is used for the static method isLinkable.
* @param element_list
* @param already_link
*/
void LinkElementCommand::setUpNewLink(const QList &element_list, bool already_link)
{
//m_element is a master we can connect several element to it
//if m_element isn't master (may be a report or slave) we can connect only one element
if (m_element->linkType() == Element::Master || element_list.size() == 1)
{
foreach(Element *elmt, element_list)
if (isLinkable(m_element, elmt, already_link))
m_linked_after << elmt;
}
else
{
qDebug() << "LinkElementCommand::setUpNewLink : try to link several elements to a report element or slave element,"
" only the first element of the list will be taken to be linked";
foreach(Element *elmt, element_list)
if (isLinkable(m_element, elmt, already_link))
{
m_linked_after << elmt;
return;
}
}
}
/**
* @brief LinkElementCommand::makeLink
* Make the link between m_element and element_list;
* This method unlink elements if needed.
* @param element_list
*/
void LinkElementCommand::makeLink(const QList &element_list)
{
//List is empty, that mean m_element must be free, so we unlink all elements
if (element_list.isEmpty())
{
m_element->unlinkAllElements();
return;
}
//We link all element from element_list
foreach(Element *elmt, element_list)
m_element->linkToElement(elmt);
//At this point may be there are unwanted linked elements to m_element. We must to unlink it.
//Elements from @element_list are wanted so we compare @element_list to current linked element of @m_element
QList to_unlink = m_element->linkedElements();
foreach(Element *elmt, element_list)
to_unlink.removeAll(elmt);
//All elements stored in to_unlink is unwanted we unlink it from m_element
if (!to_unlink.isEmpty())
foreach(Element *elmt, to_unlink)
m_element->unlinkElement(elmt);
}