mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-01 07:14:14 +02:00
721bd155f5
A wire read "nan" or "inf" for its position, its segment lengths and its label position without checking them: QString::toDouble() accepts all three as numbers. PR #792 and #996 closed this gap for elements, terminals and texts; wires were left out. The worst case is a segment length: a single "nan" passes the check that the segments add up to the distance between the two terminals, because every comparison with NaN is false. The whole path then turns to NaN: on 2612_ats_singlephase.qet one bad length out of six was saved back as six "nan" vertical segments, and the next save dropped them and routed the wire automatically, so each save changed the file. Now: - a non-finite segment length is skipped like any unreadable one, so the path fails the coherence check and the wire is routed automatically at load, as already happens for "inf"; - a non-finite wire position is read as 0; - a non-finite label position or rotation is ignored, as if the label had not been moved or rotated. Found by the mutation sweep (python3 -m simulator sweep, mutators inject_nan_coordinate / inject_inf_coordinate). The 24 example projects resave byte-identical with and without this change. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
270 lines
7.2 KiB
C++
270 lines
7.2 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 "conductortextitem.h"
|
|
|
|
#include "../diagram.h"
|
|
#include "../diagramcommands.h"
|
|
#include "../qetgraphicsitem/conductor.h"
|
|
|
|
/**
|
|
Constructeur
|
|
@param parent_conductor Conducteur auquel ce texte est rattache
|
|
*/
|
|
ConductorTextItem::ConductorTextItem(Conductor *parent_conductor) :
|
|
DiagramTextItem(parent_conductor),
|
|
parent_conductor_(parent_conductor),
|
|
moved_by_user_(false),
|
|
rotate_by_user_(false)
|
|
{
|
|
setAcceptHoverEvents(true);
|
|
}
|
|
|
|
/**
|
|
Constructeur
|
|
@param text Le texte affiche par le champ de texte
|
|
@param parent_conductor Conducteur auquel ce texte est rattache
|
|
*/
|
|
ConductorTextItem::ConductorTextItem(
|
|
const QString &text, Conductor *parent_conductor) :
|
|
DiagramTextItem(text, parent_conductor),
|
|
parent_conductor_(parent_conductor),
|
|
moved_by_user_(false),
|
|
rotate_by_user_(false)
|
|
{}
|
|
|
|
/**
|
|
Destructeur
|
|
*/
|
|
ConductorTextItem::~ConductorTextItem()
|
|
{
|
|
}
|
|
|
|
/**
|
|
@return le conducteur parent de ce champ de texte, ou 0 si celui-ci n'en a
|
|
pas
|
|
*/
|
|
Conductor *ConductorTextItem::parentConductor() const
|
|
{
|
|
return(parent_conductor_);
|
|
}
|
|
|
|
/**
|
|
@brief ConductorTextItem::fromXml
|
|
Read the properties stored in the xml element given in parameter
|
|
@param e
|
|
*/
|
|
void ConductorTextItem::fromXml(const QDomElement &e) {
|
|
//A non-finite ("nan", "inf") position or rotation is ignored,
|
|
//as if the text had never been moved or rotated
|
|
const qreal x = e.attribute("userx").toDouble();
|
|
const qreal y = e.attribute("usery").toDouble();
|
|
if (e.hasAttribute("userx") && qIsFinite(x) && qIsFinite(y)) {
|
|
setPos(x, y);
|
|
moved_by_user_ = true;
|
|
}
|
|
const qreal rotation = e.attribute("rotation").toDouble();
|
|
if (e.hasAttribute("rotation") && qIsFinite(rotation)) {
|
|
setRotation(rotation);
|
|
rotate_by_user_ = true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
@return true si ce champ de texte a ete explictement deplace par
|
|
l'utilisateur, false sinon
|
|
*/
|
|
bool ConductorTextItem::wasMovedByUser() const
|
|
{
|
|
return(moved_by_user_);
|
|
}
|
|
|
|
/**
|
|
@brief ConductorTextItem::wasRotatedByUser
|
|
@return true if text was explicit moved by user else false
|
|
*/
|
|
bool ConductorTextItem::wasRotatedByUser() const
|
|
{
|
|
return(rotate_by_user_);
|
|
}
|
|
|
|
/**
|
|
@param moved_by_user true pour que la position du texte soit consideree
|
|
comme ayant ete definie par l'utilisateur (et donc soit sauvegardee), false
|
|
pour remettre le texte a sa position originelle
|
|
*/
|
|
void ConductorTextItem::forceMovedByUser(bool moved_by_user) {
|
|
if (moved_by_user == moved_by_user_) return;
|
|
|
|
moved_by_user_ = moved_by_user;
|
|
if (!moved_by_user && parent_conductor_) {
|
|
parent_conductor_ -> calculateTextItemPosition();
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
@brief ConductorTextItem::forceRotateByUser
|
|
@param rotate_by_user true pour que la rotation du texte soit consideree
|
|
comme ayant ete definie par l'utilisateur (et donc soit sauvegardee), false
|
|
pour remettre le texte a sont angle originelle
|
|
*/
|
|
void ConductorTextItem::forceRotateByUser(bool rotate_by_user) {
|
|
if (rotate_by_user == rotate_by_user_) return;
|
|
|
|
rotate_by_user_ = rotate_by_user;
|
|
if (!rotate_by_user && parent_conductor_) {
|
|
parent_conductor_ -> calculateTextItemPosition();
|
|
}
|
|
}
|
|
|
|
/**
|
|
@brief ConductorTextItem::setPos
|
|
@param pos
|
|
*/
|
|
void ConductorTextItem::setPos(const QPointF &pos)
|
|
{
|
|
/*
|
|
* In some condition the conductor text item is outside the border of folio in the left.
|
|
* They cause a margin on the left of folio and in most case this margin is unwanted and annoying the user.
|
|
* If the text is empty and the scene position is outside the border (left and top),
|
|
* we can say that this position, is unwanted by user.
|
|
* So we move this text item to the top left of the bounding rect of parent conductors, because we sure this position is wanted by user.
|
|
*/
|
|
DiagramTextItem::setPos(pos);
|
|
if(toPlainText().isEmpty() && (scenePos().x() < 0 || scenePos().y() < 0))
|
|
{
|
|
Conductor *cond = parentConductor();
|
|
if(cond)
|
|
DiagramTextItem::setPos(cond->boundingRect().topLeft());
|
|
else
|
|
DiagramTextItem::setPos(0,0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
@brief ConductorTextItem::setPos
|
|
@param x
|
|
@param y
|
|
*/
|
|
void ConductorTextItem::setPos(qreal x, qreal y)
|
|
{
|
|
QPointF p(x,y);
|
|
setPos(p);
|
|
}
|
|
|
|
/**
|
|
@brief ConductorTextItem::mousePressEvent
|
|
@param event
|
|
*/
|
|
void ConductorTextItem::mousePressEvent(QGraphicsSceneMouseEvent *event) {
|
|
before_mov_pos_ = pos();
|
|
DiagramTextItem::mousePressEvent(event);
|
|
}
|
|
|
|
/**
|
|
@brief ConductorTextItem::mouseMoveEvent
|
|
@param event
|
|
*/
|
|
void ConductorTextItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
|
|
if (textInteractionFlags() & Qt::TextEditable) QGraphicsTextItem::mouseMoveEvent(event);
|
|
|
|
else if ((flags() & QGraphicsItem::ItemIsMovable) && (event -> buttons() & Qt::LeftButton)) {
|
|
|
|
QPointF intended_pos = event ->scenePos() + m_mouse_to_origin_movement;
|
|
|
|
if (parent_conductor_) {
|
|
if (parent_conductor_->nearShape().contains(intended_pos)) {
|
|
event->modifiers() == Qt::ControlModifier ? setPos(intended_pos) : setPos(Diagram::snapToTextGrid(intended_pos));
|
|
parent_conductor_ -> setHighlighted(Conductor::Normal);
|
|
} else {
|
|
parent_conductor_ -> setHighlighted(Conductor::Alert);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
else event -> ignore();
|
|
}
|
|
|
|
/**
|
|
@brief ConductorTextItem::mouseReleaseEvent
|
|
@param e
|
|
*/
|
|
void ConductorTextItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *e) {
|
|
if (flags() & QGraphicsItem::ItemIsMovable) {
|
|
|
|
if (Diagram *diagram_ptr = diagram()) {
|
|
QPointF applied_movement = pos() - before_mov_pos_;
|
|
|
|
if (!applied_movement.isNull()) {
|
|
//Create an undo object
|
|
MoveConductorsTextsCommand *undo_object = new MoveConductorsTextsCommand(diagram_ptr);
|
|
undo_object -> addTextMovement(this, before_mov_pos_, pos(), moved_by_user_);
|
|
|
|
moved_by_user_ = true;
|
|
|
|
diagram_ptr -> undoStack().push(undo_object);
|
|
}
|
|
|
|
if (parent_conductor_) {
|
|
parent_conductor_ -> setHighlighted(Conductor::None);
|
|
}
|
|
}
|
|
}
|
|
if (!(e -> modifiers() & Qt::ControlModifier)) {
|
|
QGraphicsTextItem::mouseReleaseEvent(e);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
When mouse over element
|
|
change m_mouse_hover to true (used in paint() function )
|
|
@param e QGraphicsSceneHoverEvent
|
|
*/
|
|
void ConductorTextItem::hoverEnterEvent(QGraphicsSceneHoverEvent *e) {
|
|
Q_UNUSED(e);
|
|
|
|
m_mouse_hover = true;
|
|
QString str_ToolTip = toPlainText();
|
|
setToolTip( str_ToolTip );
|
|
update();
|
|
}
|
|
|
|
/**
|
|
When mouse over element leave the position
|
|
change m_mouse_hover to false(used in paint() function )
|
|
@param e QGraphicsSceneHoverEvent
|
|
*/
|
|
void ConductorTextItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *e)
|
|
{
|
|
Q_UNUSED(e);
|
|
m_mouse_hover = false;
|
|
update();
|
|
}
|
|
|
|
/**
|
|
Do nothing default function .
|
|
@param e QGraphicsSceneHoverEvent
|
|
*/
|
|
void ConductorTextItem::hoverMoveEvent(QGraphicsSceneHoverEvent *e)
|
|
{
|
|
QGraphicsTextItem::hoverMoveEvent(e);
|
|
}
|