mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-09-20 23:44:13 +02:00
Compare commits
19 Commits
265aa44320
...
451f8c3296
| Author | SHA1 | Date | |
|---|---|---|---|
| 451f8c3296 | |||
| 43a1f6a1a9 | |||
| edf483d88f | |||
| 2eed3acd3d | |||
| d3424db23d | |||
| 0d722b6033 | |||
| 96fac96b89 | |||
| 1ec4f56a50 | |||
| 839324f231 | |||
| a756bc0722 | |||
| 2269a4641b | |||
| 7cd04f2a14 | |||
| a5fe544415 | |||
| 02e6eceb65 | |||
| 7c941e4697 | |||
| 5dbb9f28de | |||
| c6e7c5d371 | |||
| 7347322221 | |||
| c23999acd1 |
@@ -109,6 +109,7 @@ void SummaryQueryWidget::setQuery(const QString &query)
|
|||||||
if (query.startsWith("SELECT"))
|
if (query.startsWith("SELECT"))
|
||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
|
ui->m_edit_sql_query_cb->setChecked(false);
|
||||||
ui->m_user_query_le->setText(query);
|
ui->m_user_query_le->setText(query);
|
||||||
|
|
||||||
QString select = query;
|
QString select = query;
|
||||||
@@ -129,6 +130,17 @@ void SummaryQueryWidget::setQuery(const QString &query)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//If the query this widget would build from the columns just
|
||||||
|
//parsed above does not match the query as loaded byte-for-byte,
|
||||||
|
//the user wrote it by hand (a join, a subquery, a different
|
||||||
|
//view) and accepting the dialog unmodified must not silently
|
||||||
|
//replace it with a generated one (bugtracker #885).
|
||||||
|
const bool custom_query = query != queryStr();
|
||||||
|
m_custom_query = custom_query ? query : QString();
|
||||||
|
ui->m_edit_sql_query_cb->setChecked(custom_query);
|
||||||
|
ui->m_user_query_le->setEnabled(custom_query);
|
||||||
|
ui->m_info_widget->setDisabled(custom_query);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -691,6 +691,79 @@ QUuid Diagram::uuid()
|
|||||||
return m_uuid;
|
return m_uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Diagram::uuidUsedByOtherDiagram
|
||||||
|
A hand-edited or merged project file can contain two folios with the same
|
||||||
|
uuid. The uuid is used as a key (e.g. in the project database), so the
|
||||||
|
second one must get another one.
|
||||||
|
@param uuid
|
||||||
|
@return true if another diagram of the parent project already uses @p uuid
|
||||||
|
*/
|
||||||
|
bool Diagram::uuidUsedByOtherDiagram(const QUuid &uuid) const
|
||||||
|
{
|
||||||
|
if (!m_project) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto diagrams = m_project->diagrams();
|
||||||
|
for (const Diagram *diagram : diagrams) {
|
||||||
|
if (diagram != this && diagram->m_uuid == uuid) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Diagram::derivedUuid
|
||||||
|
Name-based (version 5) uuid for a folio that has no usable uuid in the
|
||||||
|
file it is loaded from : either the file predates the uuid attribute, or
|
||||||
|
the uuid it carries is already taken by another folio of the project.
|
||||||
|
|
||||||
|
A random uuid would do as an identity, but it would make saving an
|
||||||
|
unmodified legacy project non-reproducible : every load would invent a
|
||||||
|
different one and write it out (see #754). The name is therefore built
|
||||||
|
only from data read from the file itself -- the project title, the
|
||||||
|
position of the folio in the file and its title -- so the same input file
|
||||||
|
always yields the same uuid. It is computed once, when the folio is
|
||||||
|
loaded, and saved from then on : renaming or moving the folio later does
|
||||||
|
not change it.
|
||||||
|
|
||||||
|
@param root : the <diagram> element being loaded
|
||||||
|
@param reason : distinguishes the two cases above, so that they can not
|
||||||
|
produce the same name
|
||||||
|
@return a uuid not used by any other diagram of the project
|
||||||
|
*/
|
||||||
|
QUuid Diagram::derivedUuid(const QDomElement &root, const QString &reason) const
|
||||||
|
{
|
||||||
|
//Fixed namespace for QElectroTech folio uuids, never change it :
|
||||||
|
//doing so would change the uuid given to every legacy folio.
|
||||||
|
static const QUuid folio_namespace(
|
||||||
|
QStringLiteral("{d5951240-154d-44d6-8277-0092a31d1920}"));
|
||||||
|
|
||||||
|
const int index = m_project
|
||||||
|
? m_project->diagrams().indexOf(const_cast<Diagram *>(this))
|
||||||
|
: -1;
|
||||||
|
const QString project_title = root.ownerDocument()
|
||||||
|
.documentElement()
|
||||||
|
.attribute(QStringLiteral("title"));
|
||||||
|
|
||||||
|
const QString base = QStringLiteral("qet-folio\n%1\n%2\n%3\n%4")
|
||||||
|
.arg(reason,
|
||||||
|
project_title,
|
||||||
|
QString::number(index),
|
||||||
|
root.attribute(QStringLiteral("title")));
|
||||||
|
|
||||||
|
//A clash is only possible with a hand-crafted file, but the uuid is a
|
||||||
|
//key : salt the name until it is free. Still deterministic.
|
||||||
|
QUuid uuid = QUuid::createUuidV5(folio_namespace, base);
|
||||||
|
for (int salt = 1 ; uuidUsedByOtherDiagram(uuid) ; ++salt) {
|
||||||
|
uuid = QUuid::createUuidV5(folio_namespace,
|
||||||
|
base + QStringLiteral("\n")
|
||||||
|
+ QString::number(salt));
|
||||||
|
}
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief Diagram::setEventInterface
|
@brief Diagram::setEventInterface
|
||||||
Set event_interface has current interface.
|
Set event_interface has current interface.
|
||||||
@@ -906,6 +979,11 @@ QDomDocument Diagram::toXml(bool whole_content, bool is_copy_command) {
|
|||||||
// schema properties
|
// schema properties
|
||||||
// proprietes du schema
|
// proprietes du schema
|
||||||
if (whole_content) {
|
if (whole_content) {
|
||||||
|
//Persist the folio identity, so that a folio keeps the same uuid
|
||||||
|
//across save/load. Without it every load invents a new one, and
|
||||||
|
//nothing outside the running instance (version control, a lock,
|
||||||
|
//a diff tool...) can tell which folio is which.
|
||||||
|
dom_root.setAttribute(QStringLiteral("uuid"), m_uuid.toString());
|
||||||
border_and_titleblock.titleBlockToXml(dom_root);
|
border_and_titleblock.titleBlockToXml(dom_root);
|
||||||
border_and_titleblock.borderToXml(dom_root);
|
border_and_titleblock.borderToXml(dom_root);
|
||||||
|
|
||||||
@@ -1406,6 +1484,21 @@ bool Diagram::fromXml(QDomElement &document,
|
|||||||
// Read attributes of this diagram
|
// Read attributes of this diagram
|
||||||
if (consider_informations)
|
if (consider_informations)
|
||||||
{
|
{
|
||||||
|
// Restore the persisted folio uuid. Done first, before any item is
|
||||||
|
// loaded, so that everything created below sees the final uuid.
|
||||||
|
// A folio without a usable one (file written before the uuid was
|
||||||
|
// persisted, or uuid already taken by another folio) gets a
|
||||||
|
// deterministic one instead, see derivedUuid().
|
||||||
|
const QUuid persisted_uuid(root.attribute(QStringLiteral("uuid")));
|
||||||
|
if (persisted_uuid.isNull()) {
|
||||||
|
m_uuid = derivedUuid(root, QStringLiteral("legacy"));
|
||||||
|
} else if (uuidUsedByOtherDiagram(persisted_uuid)) {
|
||||||
|
m_uuid = derivedUuid(root, QStringLiteral("duplicate ")
|
||||||
|
+ persisted_uuid.toString());
|
||||||
|
} else {
|
||||||
|
m_uuid = persisted_uuid;
|
||||||
|
}
|
||||||
|
|
||||||
// Load border and titleblock
|
// Load border and titleblock
|
||||||
border_and_titleblock.titleBlockFromXml(root);
|
border_and_titleblock.titleBlockFromXml(root);
|
||||||
border_and_titleblock.borderFromXml(root);
|
border_and_titleblock.borderFromXml(root);
|
||||||
|
|||||||
@@ -136,6 +136,9 @@ class Diagram : public QGraphicsScene
|
|||||||
bool m_freeze_new_elements;
|
bool m_freeze_new_elements;
|
||||||
bool m_freeze_new_conductors_;
|
bool m_freeze_new_conductors_;
|
||||||
QUuid m_uuid = QUuid::createUuid();
|
QUuid m_uuid = QUuid::createUuid();
|
||||||
|
|
||||||
|
bool uuidUsedByOtherDiagram(const QUuid &uuid) const;
|
||||||
|
QUuid derivedUuid(const QDomElement &root, const QString &reason) const;
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include "diagramview.h"
|
#include "diagramview.h"
|
||||||
|
#include "lastusedstyle.h"
|
||||||
#include "qetproject.h"
|
#include "qetproject.h"
|
||||||
#include "QPropertyUndoCommand/qpropertyundocommand.h"
|
#include "QPropertyUndoCommand/qpropertyundocommand.h"
|
||||||
#include "diagramcommands.h"
|
#include "diagramcommands.h"
|
||||||
@@ -143,6 +144,10 @@ DiagramView::DiagramView(Diagram *diagram, QWidget *parent) :
|
|||||||
QPropertyUndoCommand *undo = new QPropertyUndoCommand(edited_conductor, "properties", old_value, new_value);
|
QPropertyUndoCommand *undo = new QPropertyUndoCommand(edited_conductor, "properties", old_value, new_value);
|
||||||
undo->setText(tr("Modifier les propriétés d'un conducteur", "undo caption"));
|
undo->setText(tr("Modifier les propriétés d'un conducteur", "undo caption"));
|
||||||
m_diagram->undoStack().push(undo);
|
m_diagram->undoStack().push(undo);
|
||||||
|
|
||||||
|
// remember it for the next conductor drawn this session,
|
||||||
|
// the way LastUsedStyle already does for shapes (#879)
|
||||||
|
LastUsedStyle::setConductorColor(new_color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
#include "../../qetapp.h"
|
#include "../../qetapp.h"
|
||||||
#include "../../qetinformation.h"
|
#include "../../qetinformation.h"
|
||||||
#include "ui_elementpropertieseditorwidget.h"
|
#include "ui_elementpropertieseditorwidget.h"
|
||||||
#include "../../qetinformation.h"
|
|
||||||
|
|
||||||
#include <QItemDelegate>
|
#include <QItemDelegate>
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
@@ -45,6 +44,7 @@
|
|||||||
#include <QSplitter>
|
#include <QSplitter>
|
||||||
#include <QShortcut>
|
#include <QShortcut>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
|
#include <QRegularExpressionValidator>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief The EditorDelegate class
|
@brief The EditorDelegate class
|
||||||
@@ -64,9 +64,20 @@ class EditorDelegate : public QItemDelegate
|
|||||||
{
|
{
|
||||||
if(index.column() == 1)
|
if(index.column() == 1)
|
||||||
{
|
{
|
||||||
return QItemDelegate::createEditor(parent,
|
const QString key = index.sibling(index.row(), 0)
|
||||||
option,
|
.data(Qt::UserRole).toString();
|
||||||
index);
|
|
||||||
|
if (key == QETInformation::ELMT_WIDTH || key == QETInformation::ELMT_HEIGHT || key == QETInformation::ELMT_DEPTH)
|
||||||
|
{
|
||||||
|
auto *line_edit = new QLineEdit(parent);
|
||||||
|
auto *validator = new QETInformation::NumericInfoValidator(line_edit);
|
||||||
|
line_edit->setValidator(validator);
|
||||||
|
line_edit->setPlaceholderText(tr("ex. 80.5"));
|
||||||
|
line_edit->setToolTip(tr("Nombre décimal avec un point comme séparateur (ex. 80.5)"));
|
||||||
|
return line_edit;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QItemDelegate::createEditor(parent, option, index);
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -658,21 +658,14 @@ void ElementsPanelWidget::duplicateDiagram()
|
|||||||
BorderProperties bp = source_diagram->border_and_titleblock.exportBorder();
|
BorderProperties bp = source_diagram->border_and_titleblock.exportBorder();
|
||||||
new_diagram->border_and_titleblock.importBorder(bp);
|
new_diagram->border_and_titleblock.importBorder(bp);
|
||||||
|
|
||||||
for (QGraphicsItem *item : source_diagram->items()) {
|
// Serialize the whole diagram with is_copy_command=true.
|
||||||
if (Element *elmt = dynamic_cast<Element *>(item)) {
|
// This is the same mechanism as Ctrl+C: toXml(true, true)
|
||||||
source_diagram->correctTextPos(elmt);
|
// internally calls correctTextPos/restoreText for Slave and
|
||||||
}
|
// Report elements only — producing correct text positions
|
||||||
}
|
// in the XML. No manual correctTextPos/restoreText needed.
|
||||||
|
QDomDocument doc = source_diagram->toXml(true, true);
|
||||||
QDomDocument doc = source_diagram->toXml();
|
|
||||||
QDomElement diagram_elmt = doc.documentElement();
|
QDomElement diagram_elmt = doc.documentElement();
|
||||||
|
|
||||||
for (QGraphicsItem *item : source_diagram->items()) {
|
|
||||||
if (Element *elmt = dynamic_cast<Element *>(item)) {
|
|
||||||
source_diagram->restoreText(elmt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
new_diagram->fromXml(diagram_elmt, QPointF(0, 0), false, nullptr);
|
new_diagram->fromXml(diagram_elmt, QPointF(0, 0), false, nullptr);
|
||||||
|
|
||||||
for (QGraphicsItem *item : new_diagram->items()) {
|
for (QGraphicsItem *item : new_diagram->items()) {
|
||||||
@@ -683,7 +676,18 @@ void ElementsPanelWidget::duplicateDiagram()
|
|||||||
// of the project database, so duplicates fail to insert and
|
// of the project database, so duplicates fail to insert and
|
||||||
// silently vanish from nomenclature/summary tables.
|
// silently vanish from nomenclature/summary tables.
|
||||||
elmt->newUuid();
|
elmt->newUuid();
|
||||||
new_diagram->restoreText(elmt);
|
|
||||||
|
// toXml(true, true) applied correctTextPos to Slave and
|
||||||
|
// Report elements, which shifted their text positions to
|
||||||
|
// match the stripped composite text. restoreText()
|
||||||
|
// recalculates the position for the actual resolved text.
|
||||||
|
// Only Slave and Report need this — other element types
|
||||||
|
// were not affected by correctTextPos.
|
||||||
|
if (elmt->linkType() == Element::Slave ||
|
||||||
|
elmt->linkType() & Element::AllReport)
|
||||||
|
{
|
||||||
|
new_diagram->restoreText(elmt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (Conductor *cond = dynamic_cast<Conductor *>(item)) {
|
else if (Conductor *cond = dynamic_cast<Conductor *>(item)) {
|
||||||
// Same reasoning for conductors: conductor.uuid is the PRIMARY
|
// Same reasoning for conductors: conductor.uuid is the PRIMARY
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ QBrush LastUsedStyle::m_shape_brush;
|
|||||||
bool LastUsedStyle::m_has_shape_brush = false;
|
bool LastUsedStyle::m_has_shape_brush = false;
|
||||||
QFont LastUsedStyle::m_text_font;
|
QFont LastUsedStyle::m_text_font;
|
||||||
bool LastUsedStyle::m_has_text_font = false;
|
bool LastUsedStyle::m_has_text_font = false;
|
||||||
|
QColor LastUsedStyle::m_conductor_color;
|
||||||
|
bool LastUsedStyle::m_has_conductor_color = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@return true if a shape pen was set this session
|
@return true if a shape pen was set this session
|
||||||
@@ -104,3 +106,30 @@ void LastUsedStyle::setTextFont(const QFont &font)
|
|||||||
m_text_font = font;
|
m_text_font = font;
|
||||||
m_has_text_font = true;
|
m_has_text_font = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@return true if a conductor color was set this session
|
||||||
|
*/
|
||||||
|
bool LastUsedStyle::hasConductorColor()
|
||||||
|
{
|
||||||
|
return m_has_conductor_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@return the last color applied to a conductor this session
|
||||||
|
*/
|
||||||
|
QColor LastUsedStyle::conductorColor()
|
||||||
|
{
|
||||||
|
return m_conductor_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief LastUsedStyle::setConductorColor
|
||||||
|
Record @a color as the last-used conductor color for this session
|
||||||
|
@param color
|
||||||
|
*/
|
||||||
|
void LastUsedStyle::setConductorColor(const QColor &color)
|
||||||
|
{
|
||||||
|
m_conductor_color = color;
|
||||||
|
m_has_conductor_color = true;
|
||||||
|
}
|
||||||
|
|||||||
+14
-5
@@ -19,20 +19,23 @@
|
|||||||
#define LAST_USED_STYLE_H
|
#define LAST_USED_STYLE_H
|
||||||
|
|
||||||
#include <QBrush>
|
#include <QBrush>
|
||||||
|
#include <QColor>
|
||||||
#include <QFont>
|
#include <QFont>
|
||||||
#include <QPen>
|
#include <QPen>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief The LastUsedStyle class
|
@brief The LastUsedStyle class
|
||||||
Session-scoped "last used" style for new shapes and free text created
|
Session-scoped "last used" style for new shapes, free text and
|
||||||
on the diagram canvas: whatever pen/brush/font was last applied through
|
conductors created on the diagram canvas: whatever pen/brush/font/color
|
||||||
the properties editors becomes the starting point for the next new
|
was last applied through the properties editors becomes the starting
|
||||||
item of that type, the way most drawing tools behave.
|
point for the next new item of that type, the way most drawing tools
|
||||||
|
behave.
|
||||||
|
|
||||||
Deliberately in-memory only, not QSettings-backed: this is a live
|
Deliberately in-memory only, not QSettings-backed: this is a live
|
||||||
"what did I just use" value for the current editing session, not an
|
"what did I just use" value for the current editing session, not an
|
||||||
app-wide default (that's already covered by the Preferences dialog's
|
app-wide default (that's already covered by the Preferences dialog's
|
||||||
font setting, read as the fallback when nothing has been set yet).
|
font setting, read as the fallback when nothing has been set yet, and
|
||||||
|
by the project's default conductor color, read the same way).
|
||||||
*/
|
*/
|
||||||
class LastUsedStyle
|
class LastUsedStyle
|
||||||
{
|
{
|
||||||
@@ -49,6 +52,10 @@ class LastUsedStyle
|
|||||||
static QFont textFont();
|
static QFont textFont();
|
||||||
static void setTextFont(const QFont &font);
|
static void setTextFont(const QFont &font);
|
||||||
|
|
||||||
|
static bool hasConductorColor();
|
||||||
|
static QColor conductorColor();
|
||||||
|
static void setConductorColor(const QColor &color);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
LastUsedStyle() = delete;
|
LastUsedStyle() = delete;
|
||||||
|
|
||||||
@@ -58,6 +65,8 @@ class LastUsedStyle
|
|||||||
static bool m_has_shape_brush;
|
static bool m_has_shape_brush;
|
||||||
static QFont m_text_font;
|
static QFont m_text_font;
|
||||||
static bool m_has_text_font;
|
static bool m_has_text_font;
|
||||||
|
static QColor m_conductor_color;
|
||||||
|
static bool m_has_conductor_color;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // LAST_USED_STYLE_H
|
#endif // LAST_USED_STYLE_H
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include "../qetgraphicsitem/conductor.h"
|
#include "../qetgraphicsitem/conductor.h"
|
||||||
|
#include "../lastusedstyle.h"
|
||||||
#include "../qetproject.h"
|
#include "../qetproject.h"
|
||||||
#include "../QPropertyUndoCommand/qpropertyundocommand.h"
|
#include "../QPropertyUndoCommand/qpropertyundocommand.h"
|
||||||
#include "../autoNum/numerotationcontextcommands.h"
|
#include "../autoNum/numerotationcontextcommands.h"
|
||||||
@@ -127,11 +128,17 @@ Conductor::Conductor(Terminal *p1, Terminal* p2) :
|
|||||||
m_text_item = new ConductorTextItem(m_properties.text, this);
|
m_text_item = new ConductorTextItem(m_properties.text, this);
|
||||||
connect(m_text_item, &ConductorTextItem::textEdited, this, &Conductor::displayedTextChanged);
|
connect(m_text_item, &ConductorTextItem::textEdited, this, &Conductor::displayedTextChanged);
|
||||||
|
|
||||||
//Set the default conductor properties.
|
//Set the default conductor properties. The color, specifically, is
|
||||||
if (p1->diagram())
|
//overridden by the last one applied via the F2 color editor this
|
||||||
setProperties(p1->diagram()->defaultConductorProperties);
|
//session (#879), the same way LastUsedStyle already does for shapes.
|
||||||
else if (p2->diagram())
|
Diagram *dia = p1->diagram() ? p1->diagram() : p2->diagram();
|
||||||
setProperties(p2->diagram()->defaultConductorProperties);
|
if (dia)
|
||||||
|
{
|
||||||
|
ConductorProperties properties = dia->defaultConductorProperties;
|
||||||
|
if (LastUsedStyle::hasConductorColor())
|
||||||
|
properties.color = LastUsedStyle::conductorColor();
|
||||||
|
setProperties(properties);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -86,7 +86,12 @@ Terminal::Terminal(TerminalData* data, Element* e) :
|
|||||||
* Destruction of the terminal, and also docked conductor
|
* Destruction of the terminal, and also docked conductor
|
||||||
*/
|
*/
|
||||||
Terminal::~Terminal() {
|
Terminal::~Terminal() {
|
||||||
qDeleteAll(m_conductors_list);
|
// Each conductor's destructor calls removeConductor() on both its
|
||||||
|
// terminals, which mutates m_conductors_list while qDeleteAll() is
|
||||||
|
// still iterating it. Delete from a snapshot so the live list can
|
||||||
|
// change underneath without affecting the iteration.
|
||||||
|
const QList<Conductor *> conductors_to_delete = m_conductors_list;
|
||||||
|
qDeleteAll(conductors_to_delete);
|
||||||
delete d;
|
delete d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -158,7 +158,10 @@ QStringList QETInformation::elementInfoKeys()
|
|||||||
ELMT_MACHINE_MANUFACTURER_REF,
|
ELMT_MACHINE_MANUFACTURER_REF,
|
||||||
ELMT_SUPPLIER,
|
ELMT_SUPPLIER,
|
||||||
ELMT_QUANTITY,
|
ELMT_QUANTITY,
|
||||||
ELMT_UNITY,
|
ELMT_UNITY,
|
||||||
|
ELMT_WIDTH,
|
||||||
|
ELMT_HEIGHT,
|
||||||
|
ELMT_DEPTH,
|
||||||
ELMT_AUX1,
|
ELMT_AUX1,
|
||||||
ELMT_DESCRIPTION_AUX1,
|
ELMT_DESCRIPTION_AUX1,
|
||||||
ELMT_DESIGNATION_AUX1,
|
ELMT_DESIGNATION_AUX1,
|
||||||
@@ -219,6 +222,45 @@ QString QETInformation::elementInfoToVar(const QString &info)
|
|||||||
return (QString ("%{void}"));
|
return (QString ("%{void}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief QETInformation::numericInfoPattern
|
||||||
|
* @return the pattern used to validate numeric elementInformation
|
||||||
|
* fields (currently width/height/depth): digits with an optional "."
|
||||||
|
* as decimal separator, requiring at least one digit overall so a
|
||||||
|
* lone "." can never be a complete, acceptable value on its own.
|
||||||
|
*/
|
||||||
|
QRegularExpression QETInformation::numericInfoPattern()
|
||||||
|
{
|
||||||
|
return QRegularExpression(QStringLiteral(R"(^[0-9]+$|^[0-9]*\.[0-9]{1,2}$)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief QETInformation::NumericInfoValidator::NumericInfoValidator
|
||||||
|
@param parent
|
||||||
|
*/
|
||||||
|
QETInformation::NumericInfoValidator::NumericInfoValidator(QObject *parent) :
|
||||||
|
QRegularExpressionValidator(numericInfoPattern(), parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief QETInformation::NumericInfoValidator::validate
|
||||||
|
Rewrites any "," in @a input to "." in place, then delegates to
|
||||||
|
the base class for the actual numericInfoPattern() check. @a pos
|
||||||
|
is left untouched by the rewrite itself -- replacing "," with "."
|
||||||
|
never changes the string's length, so the cursor position the
|
||||||
|
caller already tracked stays correct.
|
||||||
|
@param input the text being validated; may be rewritten
|
||||||
|
@param pos the cursor position within @a input
|
||||||
|
@return the resulting validation state
|
||||||
|
*/
|
||||||
|
QValidator::State QETInformation::NumericInfoValidator::validate(QString &input, int &pos) const
|
||||||
|
{
|
||||||
|
if (input.contains(QLatin1Char(',')))
|
||||||
|
input.replace(QLatin1Char(','), QLatin1Char('.'));
|
||||||
|
return QRegularExpressionValidator::validate(input, pos);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief QETInformation::infoToVar
|
* @brief QETInformation::infoToVar
|
||||||
* @param info
|
* @param info
|
||||||
@@ -279,6 +321,9 @@ QString QETInformation::translatedInfoKey(const QString &info)
|
|||||||
else if (info == ELMT_SUPPLIER) return QObject::tr("Fournisseur");
|
else if (info == ELMT_SUPPLIER) return QObject::tr("Fournisseur");
|
||||||
else if (info == ELMT_QUANTITY) return QObject::tr("Quantité");
|
else if (info == ELMT_QUANTITY) return QObject::tr("Quantité");
|
||||||
else if (info == ELMT_UNITY) return QObject::tr("Unité");
|
else if (info == ELMT_UNITY) return QObject::tr("Unité");
|
||||||
|
else if (info == ELMT_WIDTH) return QObject::tr("Largeur [mm]");
|
||||||
|
else if (info == ELMT_HEIGHT) return QObject::tr("Hauteur [mm]");
|
||||||
|
else if (info == ELMT_DEPTH) return QObject::tr("Profondeur [mm]");
|
||||||
else if (info == ELMT_LOCATION) return QObject::tr("Localisation (+)");
|
else if (info == ELMT_LOCATION) return QObject::tr("Localisation (+)");
|
||||||
else if (info == COND_FUNCTION) return QObject::tr("Fonction");
|
else if (info == COND_FUNCTION) return QObject::tr("Fonction");
|
||||||
else if (info == COND_TENSION_PROTOCOL) return QObject::tr("Tension / Protocole");
|
else if (info == COND_TENSION_PROTOCOL) return QObject::tr("Tension / Protocole");
|
||||||
@@ -351,6 +396,9 @@ QStringList QETInformation::elementEditorElementInfoKeys()
|
|||||||
ELMT_SUPPLIER,
|
ELMT_SUPPLIER,
|
||||||
ELMT_QUANTITY,
|
ELMT_QUANTITY,
|
||||||
ELMT_UNITY,
|
ELMT_UNITY,
|
||||||
|
ELMT_WIDTH,
|
||||||
|
ELMT_HEIGHT,
|
||||||
|
ELMT_DEPTH,
|
||||||
ELMT_AUX1,
|
ELMT_AUX1,
|
||||||
ELMT_DESCRIPTION_AUX1,
|
ELMT_DESCRIPTION_AUX1,
|
||||||
ELMT_DESIGNATION_AUX1,
|
ELMT_DESIGNATION_AUX1,
|
||||||
|
|||||||
@@ -18,17 +18,22 @@
|
|||||||
#ifndef QETINFORMATION_H
|
#ifndef QETINFORMATION_H
|
||||||
#define QETINFORMATION_H
|
#define QETINFORMATION_H
|
||||||
|
|
||||||
#include <QStringList>
|
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QRegularExpressionValidator>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inside this namespace you will find all information used in QElectrotech for
|
* Inside this namespace you will find all information used in QElectrotech for
|
||||||
* element, conductor and diagram.
|
* element, conductor and diagram.
|
||||||
* Each information have 3 values :
|
* Each information have 3 values :
|
||||||
* #1 the info key = the key of an information as a QString used in the code (example : label)
|
* #1 the info key = the key of an information as a QString used in the code
|
||||||
* #2 the info key to variable = the key in form of a variable.
|
* (example : label)
|
||||||
* This is used by the user to replace a variable by the string of this variable (example : %{label})
|
* #2 the info key to variable = the key in form of a variable. This is used
|
||||||
* #3 the info key translated to the current local (example label in dutch = Betriebsmittelkennzeichen)
|
* by the user to replace a variable by the string of this variable
|
||||||
|
* (example : %{label})
|
||||||
|
* #3 the info key translated to the current local (example label in dutch =
|
||||||
|
* Betriebsmittelkennzeichen)
|
||||||
*/
|
*/
|
||||||
namespace QETInformation
|
namespace QETInformation
|
||||||
{
|
{
|
||||||
@@ -50,6 +55,9 @@ namespace QETInformation
|
|||||||
static QString ELMT_CURRENT_RATING = "current_rating";
|
static QString ELMT_CURRENT_RATING = "current_rating";
|
||||||
static QString ELMT_NOTES = "notes";
|
static QString ELMT_NOTES = "notes";
|
||||||
static QString ELMT_UNITY = "unity";
|
static QString ELMT_UNITY = "unity";
|
||||||
|
static QString ELMT_WIDTH = "width";
|
||||||
|
static QString ELMT_HEIGHT = "height";
|
||||||
|
static QString ELMT_DEPTH = "depth";
|
||||||
static QString ELMT_PLANT = "plant";
|
static QString ELMT_PLANT = "plant";
|
||||||
static QString ELMT_LOCATION = "location";
|
static QString ELMT_LOCATION = "location";
|
||||||
static QString ELMT_AUX1 = "auxiliary1";
|
static QString ELMT_AUX1 = "auxiliary1";
|
||||||
@@ -161,6 +169,14 @@ namespace QETInformation
|
|||||||
QStringList elementEditorElementInfoKeys();
|
QStringList elementEditorElementInfoKeys();
|
||||||
QString elementInfoToVar(const QString &info);
|
QString elementInfoToVar(const QString &info);
|
||||||
|
|
||||||
|
QRegularExpression numericInfoPattern();
|
||||||
|
class NumericInfoValidator : public QRegularExpressionValidator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit NumericInfoValidator(QObject *parent = nullptr);
|
||||||
|
State validate(QString &input, int &pos) const override;
|
||||||
|
};
|
||||||
|
|
||||||
QStringList terminalElementInfoKeys();
|
QStringList terminalElementInfoKeys();
|
||||||
|
|
||||||
QString infoToVar(const QString &info);
|
QString infoToVar(const QString &info);
|
||||||
|
|||||||
+52
-2
@@ -255,6 +255,38 @@ void QETProject::init()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief QETProject::derivedUuid
|
||||||
|
Name-based (version 5) uuid for a project file that has no uuid yet,
|
||||||
|
because it was written before the uuid was persisted.
|
||||||
|
|
||||||
|
A random uuid would do as an identity, but it would make saving an
|
||||||
|
unmodified legacy project non-reproducible : every load would invent a
|
||||||
|
different one and write it out (see #754). The uuid is therefore derived
|
||||||
|
from the content of the file, so the same file always yields the same
|
||||||
|
uuid, while two different projects practically never share one.
|
||||||
|
Carriage returns are dropped first, so that a checkout with CRLF line
|
||||||
|
endings (Windows, git autocrlf) gives the same uuid as one with LF.
|
||||||
|
|
||||||
|
It is computed once, when the file is loaded, and saved from then on :
|
||||||
|
editing, renaming or moving the project later does not change it.
|
||||||
|
@param content : the raw content of the project file
|
||||||
|
@return the derived uuid
|
||||||
|
*/
|
||||||
|
QUuid QETProject::derivedUuid(const QByteArray &content)
|
||||||
|
{
|
||||||
|
//Fixed namespace for QElectroTech project uuids, never change it :
|
||||||
|
//doing so would change the uuid given to every legacy project.
|
||||||
|
static const QUuid project_namespace(
|
||||||
|
QStringLiteral("{c8c75719-0fea-4b1c-9f4b-2dd179fb2f0c}"));
|
||||||
|
|
||||||
|
QByteArray normalized(content);
|
||||||
|
normalized.replace('\r', QByteArray());
|
||||||
|
return QUuid::createUuidV5(project_namespace,
|
||||||
|
QByteArrayLiteral("qet-project-legacy\n")
|
||||||
|
+ normalized);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief QETProject::openFile
|
@brief QETProject::openFile
|
||||||
@param file
|
@param file
|
||||||
@@ -274,9 +306,11 @@ QETProject::ProjectState QETProject::openFile(QFile *file)
|
|||||||
QFileInfo fi(*file);
|
QFileInfo fi(*file);
|
||||||
setFilePath(fi.absoluteFilePath());
|
setFilePath(fi.absoluteFilePath());
|
||||||
|
|
||||||
//Extract the content of the xml
|
//Extract the content of the xml. The raw bytes are kept : a project
|
||||||
|
//file without a persisted uuid derives its uuid from them.
|
||||||
|
const QByteArray content = file->readAll();
|
||||||
QDomDocument xml_project;
|
QDomDocument xml_project;
|
||||||
if (!xml_project.setContent(file))
|
if (!xml_project.setContent(content))
|
||||||
{
|
{
|
||||||
if(opened_here) {
|
if(opened_here) {
|
||||||
file->close();
|
file->close();
|
||||||
@@ -285,6 +319,17 @@ QETProject::ProjectState QETProject::openFile(QFile *file)
|
|||||||
}
|
}
|
||||||
const qint64 xml_parse_ms = load_timer.elapsed();
|
const qint64 xml_parse_ms = load_timer.elapsed();
|
||||||
|
|
||||||
|
//Restore the persisted project uuid before anything else is built
|
||||||
|
//from the file. The project database already got its connection name
|
||||||
|
//from the uuid created at construction, it does not depend on this.
|
||||||
|
const QDomElement root_elmt = xml_project.documentElement();
|
||||||
|
if (root_elmt.tagName() == QLatin1String("project"))
|
||||||
|
{
|
||||||
|
const QUuid persisted_uuid(root_elmt.attribute(QStringLiteral("uuid")));
|
||||||
|
m_uuid = persisted_uuid.isNull() ? derivedUuid(content)
|
||||||
|
: persisted_uuid;
|
||||||
|
}
|
||||||
|
|
||||||
//Build the project from the xml
|
//Build the project from the xml
|
||||||
readProjectXml(xml_project);
|
readProjectXml(xml_project);
|
||||||
|
|
||||||
@@ -1038,6 +1083,11 @@ QDomDocument QETProject::toXml()
|
|||||||
setTitle(QFileInfo(m_file_path).completeBaseName());
|
setTitle(QFileInfo(m_file_path).completeBaseName());
|
||||||
}
|
}
|
||||||
project_root.setAttribute("title", project_title_);
|
project_root.setAttribute("title", project_title_);
|
||||||
|
//Persist the project identity, so that the project keeps the same
|
||||||
|
//uuid across save/load. Without it every load invents a new one, and
|
||||||
|
//nothing outside the running instance (version control, a cloud or
|
||||||
|
//key-value store, a lock...) can tell which project a file belongs to.
|
||||||
|
project_root.setAttribute(QStringLiteral("uuid"), m_uuid.toString());
|
||||||
xml_doc.appendChild(project_root);
|
xml_doc.appendChild(project_root);
|
||||||
|
|
||||||
// titleblock templates, if any
|
// titleblock templates, if any
|
||||||
|
|||||||
@@ -271,6 +271,7 @@ class QETProject : public QObject
|
|||||||
void writeBackup();
|
void writeBackup();
|
||||||
void init();
|
void init();
|
||||||
ProjectState openFile(QFile *file);
|
ProjectState openFile(QFile *file);
|
||||||
|
static QUuid derivedUuid(const QByteArray &content);
|
||||||
void refresh();
|
void refresh();
|
||||||
|
|
||||||
// attributes
|
// attributes
|
||||||
|
|||||||
@@ -18,7 +18,9 @@
|
|||||||
#include "elementinfopartwidget.h"
|
#include "elementinfopartwidget.h"
|
||||||
|
|
||||||
#include "../SearchAndReplace/searchandreplaceworker.h"
|
#include "../SearchAndReplace/searchandreplaceworker.h"
|
||||||
|
#include "../qetinformation.h"
|
||||||
#include "ui_elementinfopartwidget.h"
|
#include "ui_elementinfopartwidget.h"
|
||||||
|
#include <QRegularExpressionValidator>
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
@@ -43,6 +45,14 @@ ElementInfoPartWidget::ElementInfoPartWidget(
|
|||||||
ui->label_->setText(translated_key);
|
ui->label_->setText(translated_key);
|
||||||
ui->m_erase_text->setVisible(false);
|
ui->m_erase_text->setVisible(false);
|
||||||
|
|
||||||
|
if (key_ == QETInformation::ELMT_WIDTH || key_ == QETInformation::ELMT_HEIGHT || key_ == QETInformation::ELMT_DEPTH)
|
||||||
|
{
|
||||||
|
auto *validator = new QETInformation::NumericInfoValidator(ui->line_edit);
|
||||||
|
ui->line_edit->setValidator(validator);
|
||||||
|
ui->line_edit->setPlaceholderText(tr("ex. 80.5"));
|
||||||
|
ui->line_edit->setToolTip(tr("Nombre décimal avec un point comme séparateur (ex. 80.5)"));
|
||||||
|
}
|
||||||
|
|
||||||
connect(ui->line_edit, &QLineEdit::textEdited,
|
connect(ui->line_edit, &QLineEdit::textEdited,
|
||||||
this, &ElementInfoPartWidget::textEdited);
|
this, &ElementInfoPartWidget::textEdited);
|
||||||
connect(ui->line_edit, &QLineEdit::textChanged,
|
connect(ui->line_edit, &QLineEdit::textChanged,
|
||||||
@@ -67,6 +77,15 @@ QString ElementInfoPartWidget::text() const
|
|||||||
return (ui->line_edit->text());
|
return (ui->line_edit->text());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief ElementInfoPartWidget::hasAcceptableInput
|
||||||
|
@return whether the line edit's current text satisfies its validator
|
||||||
|
*/
|
||||||
|
bool ElementInfoPartWidget::hasAcceptableInput() const
|
||||||
|
{
|
||||||
|
return ui->line_edit->hasAcceptableInput();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief ElementInfoPartWidget::setText
|
@brief ElementInfoPartWidget::setText
|
||||||
Set text to line edit
|
Set text to line edit
|
||||||
|
|||||||
@@ -41,9 +41,9 @@ class ElementInfoPartWidget : public QWidget
|
|||||||
QWidget *parent = nullptr);
|
QWidget *parent = nullptr);
|
||||||
~ElementInfoPartWidget() override;
|
~ElementInfoPartWidget() override;
|
||||||
|
|
||||||
QString key () const
|
QString key () const {return key_;}
|
||||||
{return key_;}
|
|
||||||
QString text () const;
|
QString text () const;
|
||||||
|
bool hasAcceptableInput() const;
|
||||||
void setText (const QString &);
|
void setText (const QString &);
|
||||||
void setPlaceHolderText (const QString &text);
|
void setPlaceHolderText (const QString &text);
|
||||||
void setFocusTolineEdit();
|
void setFocusTolineEdit();
|
||||||
|
|||||||
@@ -395,6 +395,9 @@ DiagramContext ElementInfoWidget::currentInfo() const
|
|||||||
|
|
||||||
for (const auto &eipw : std::as_const(m_eipw_list))
|
for (const auto &eipw : std::as_const(m_eipw_list))
|
||||||
{
|
{
|
||||||
|
if (!eipw->hasAcceptableInput())
|
||||||
|
continue;
|
||||||
|
|
||||||
//add value only if they're something to store
|
//add value only if they're something to store
|
||||||
if (!eipw->text().isEmpty())
|
if (!eipw->text().isEmpty())
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user