mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-10 22:24:13 +02:00
9136c605f4
"nan", "inf" and "-inf" parse as numbers, and IndependentTextItem:: fromXml() used them as they came for x, y and rotation. A project with such a value kept it: --resave wrote x="nan" back, and the SVG export drew the text at matrix(1,0,0,1,nan,95), which is not valid SVG. Conductors and element texts already load a non-finite position as 0. Load a non-finite x, y or rotation of a free text as 0, the same way. No file-format change: finite values load and save as before. Tests: tst_freetextnonfinite runs the binary's --resave and --export-svg on a fixture with x="nan", y="inf", x="-inf", rotation="nan" and a finite text (61.7, 61.3, 37 degrees, kept as they are). The four non-finite cases and the export check fail on master. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Beat Hangartner <beat@hangartners.ch>
173 lines
5.5 KiB
C++
173 lines
5.5 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 "independenttextitem.h"
|
|
#include "../shownkinds.h"
|
|
|
|
#include "../diagram.h"
|
|
#include "../diagramcommands.h"
|
|
#include "../lastusedstyle.h"
|
|
#include "../qet.h"
|
|
#include "../qetapp.h"
|
|
#include "../utils/qetutils.h"
|
|
|
|
#include <QDomElement>
|
|
#include <QScopedPointer>
|
|
#include <QSettings>
|
|
#include <QtCore/qnumeric.h>
|
|
|
|
/**
|
|
Constructeur
|
|
@param parent_diagram Le schema auquel est rattache le champ de texte
|
|
*/
|
|
IndependentTextItem::IndependentTextItem() :
|
|
DiagramTextItem(nullptr)
|
|
{
|
|
ShownKinds::tag(this, ShownKinds::FreeTexts);
|
|
wrapAtWords();
|
|
//Start from the font last applied to a text item this session,
|
|
//falling back to the app-wide Preferences default otherwise.
|
|
setFont(LastUsedStyle::hasTextFont() ? LastUsedStyle::textFont()
|
|
: QETApp::indiTextsItemFont());
|
|
QSettings settings;
|
|
setRotation(settings.value("diagrameditor/independent_text_rotation", 0).toInt());
|
|
}
|
|
|
|
/**
|
|
@brief IndependentTextItem::IndependentTextItem
|
|
Constructeur
|
|
@param text Le texte affiche par le champ de texte
|
|
*/
|
|
IndependentTextItem::IndependentTextItem(const QString &text) :
|
|
DiagramTextItem(text, nullptr)
|
|
{
|
|
ShownKinds::tag(this, ShownKinds::FreeTexts);
|
|
wrapAtWords();
|
|
}
|
|
|
|
/**
|
|
@brief IndependentTextItem::wrapAtWords
|
|
A text with a width wraps between words only: a word longer than the
|
|
width goes past it rather than being cut, as for the texts of symbols.
|
|
*/
|
|
void IndependentTextItem::wrapAtWords()
|
|
{
|
|
QTextOption option = document()->defaultTextOption();
|
|
option.setWrapMode(QTextOption::WordWrap);
|
|
document()->setDefaultTextOption(option);
|
|
}
|
|
|
|
/// Destructeur
|
|
IndependentTextItem::~IndependentTextItem()
|
|
{
|
|
}
|
|
|
|
/**
|
|
Permet de lire le texte a mettre dans le champ a partir d'un element XML.
|
|
Cette methode se base sur la position du champ pour assigner ou non la
|
|
valeur a ce champ.
|
|
@param e L'element XML representant le champ de texte
|
|
*/
|
|
void IndependentTextItem::fromXml(const QDomElement &e) {
|
|
const QUuid uuid(e.attribute(QStringLiteral("uuid")));
|
|
if (!uuid.isNull() && uuid != m_uuid) setUuid(uuid);
|
|
//"nan" and "inf" parse as numbers; a non-finite position or
|
|
//rotation is kept at 0, as for conductors and element texts
|
|
const qreal x = e.attribute("x").toDouble();
|
|
const qreal y = e.attribute("y").toDouble();
|
|
setPos(qIsFinite(x) ? x : 0, qIsFinite(y) ? y : 0);
|
|
setHtml(e.attribute("text"));
|
|
const qreal rotation = e.attribute("rotation").toDouble();
|
|
setRotation(qIsFinite(rotation) ? rotation : 0);
|
|
if (e.hasAttribute("font"))
|
|
{
|
|
QFont font;
|
|
QETUtils::fontFromString(font, e.attribute("font"));
|
|
setFont(font);
|
|
}
|
|
//Optional: absent for a text with the automatic width, the only
|
|
//kind older versions know (they show such a text unwrapped).
|
|
//Read after the text, setHtml() sets a width of its own.
|
|
setTextWidth(e.attribute(QStringLiteral("text_width"), QStringLiteral("-1")).toDouble());
|
|
}
|
|
|
|
/**
|
|
@param document Le document XML a utiliser
|
|
@return L'element XML representant ce champ de texte
|
|
*/
|
|
QDomElement IndependentTextItem::toXml(QDomDocument &document) const
|
|
{
|
|
QDomElement result = document.createElement("input");
|
|
result.setAttribute("uuid", m_uuid.toString());
|
|
result.setAttribute("x", QString("%1").arg(pos().x()));
|
|
result.setAttribute("y", QString("%1").arg(pos().y()));
|
|
result.setAttribute("text", toHtml());
|
|
result.setAttribute("rotation", QString::number(QET::correctAngle(rotation())));
|
|
result.setAttribute("font", QETUtils::fontToString(font()));
|
|
//Only when set, so a text with the automatic width is saved as before
|
|
if (m_text_width > 0)
|
|
result.setAttribute("text_width", QString::number(m_text_width));
|
|
|
|
return(result);
|
|
}
|
|
|
|
/**
|
|
@brief IndependentTextItem::setTextWidth
|
|
Set the width of this text (-1 = automatic width): the text wraps to
|
|
it, its top-left corner stays in place.
|
|
@param width
|
|
*/
|
|
void IndependentTextItem::setTextWidth(qreal width)
|
|
{
|
|
if (!qIsFinite(width) || width <= 0)
|
|
width = -1;
|
|
if (qFuzzyCompare(width, m_text_width))
|
|
return;
|
|
|
|
qreal document_width = width;
|
|
//The automatic width of a text with centred or right-aligned
|
|
//lines, as setHtml() gives it
|
|
if (width < 0 && m_non_left_alignment)
|
|
{
|
|
QScopedPointer<QTextDocument> natural(document()->clone());
|
|
natural->setTextWidth(-1);
|
|
document_width = natural->idealWidth() + 40.0;
|
|
}
|
|
|
|
document()->setTextWidth(document_width);
|
|
m_text_width = width;
|
|
emit textWidthChanged(width);
|
|
}
|
|
|
|
/**
|
|
@brief IndependentTextItem::textResizeHandlesWanted
|
|
@return true when this text is selected and not being typed in: the
|
|
corner handles then change its width.
|
|
*/
|
|
bool IndependentTextItem::textResizeHandlesWanted() const
|
|
{
|
|
return isSelected() && !isEditing();
|
|
}
|
|
|
|
void IndependentTextItem::focusOutEvent(QFocusEvent *event)
|
|
{
|
|
DiagramTextItem::focusOutEvent(event);
|
|
if (diagram() && (m_previous_html_text != this->toHtml())) {
|
|
diagram()->undoStack().push(new ChangeDiagramTextCommand(this, m_previous_html_text, this->toHtml()));
|
|
}
|
|
}
|