mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-05 10:34:13 +02:00
Merge remote-tracking branch 'upstream/master' into feature/mcp-folio-presets
# Conflicts: # misc/qet-mcp/README.md # misc/qet-mcp/qet_mcp.py
This commit is contained in:
@@ -709,12 +709,20 @@ pugi::xml_document ElementsLocation::pugiXml() const
|
||||
#endif
|
||||
if (!m_project)
|
||||
{
|
||||
//Read through QFile, not pugi's load_file(): on Windows load_file()
|
||||
//fails once the full path reaches MAX_PATH (260 characters), while
|
||||
//QFile handles long paths.
|
||||
QFile file(m_file_system_path);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
return docu;
|
||||
}
|
||||
const QByteArray data = file.readAll();
|
||||
#ifndef Q_OS_LINUX
|
||||
if (docu.load_file(m_file_system_path.toStdWString().c_str())) {
|
||||
if (docu.load_buffer(data.constData(), data.size())) {
|
||||
docu.save(m_string_stream);
|
||||
}
|
||||
#else
|
||||
docu.load_file(m_file_system_path.toStdWString().c_str());
|
||||
docu.load_buffer(data.constData(), data.size());
|
||||
#endif
|
||||
}
|
||||
else
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QPainter>
|
||||
#include <QPixmap>
|
||||
#include <QStyle>
|
||||
@@ -178,7 +179,12 @@ QString FileElementCollectionItem::localName()
|
||||
bool readable = false;
|
||||
QString str(fileSystemPath() % "/qet_directory");
|
||||
pugi::xml_document docu;
|
||||
if (docu.load_file(str.toStdWString().c_str()))
|
||||
// QFile rather than pugi's load_file(), which fails on
|
||||
// Windows once the full path reaches 260 characters.
|
||||
QFile file(str);
|
||||
const QByteArray data = file.open(QIODevice::ReadOnly)
|
||||
? file.readAll() : QByteArray();
|
||||
if (!data.isEmpty() && docu.load_buffer(data.constData(), data.size()))
|
||||
{
|
||||
if (QString(docu.document_element().name())
|
||||
== "qet-directory")
|
||||
|
||||
@@ -157,6 +157,16 @@ QDomElement XmlElementCollection::root() const
|
||||
*/
|
||||
QDomElement XmlElementCollection::importCategory() const
|
||||
{
|
||||
//Found by name: a project may hold other top-level categories,
|
||||
//and before the "import" one (lmdg.qet has "k_elem" first). Every
|
||||
//lookup of an imported element is under "import/", so filing it
|
||||
//anywhere else made the next placement of the same symbol fail.
|
||||
for (QDomElement category = root().firstChildElement("category") ;
|
||||
!category.isNull() ;
|
||||
category = category.nextSiblingElement("category")) {
|
||||
if (category.attribute("name") == QLatin1String("import"))
|
||||
return category;
|
||||
}
|
||||
return root().firstChildElement("category");
|
||||
}
|
||||
|
||||
|
||||
@@ -36,8 +36,7 @@
|
||||
<string>Ajouter une ligne</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="list-add" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/list-add.png</normaloff>:/ico/16x16/list-add.png</iconset>
|
||||
<iconset theme="list-add"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -48,8 +47,7 @@
|
||||
<string>Copier dans le presse papier</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="edit-paste" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/edit-paste.png</normaloff>:/ico/16x16/edit-paste.png</iconset>
|
||||
<iconset theme="edit-paste"/>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
|
||||
@@ -180,8 +180,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="start" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/22x22/start.png</normaloff>:/ico/22x22/start.png</iconset>
|
||||
<iconset theme="start"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -106,8 +106,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="go-bottom" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/go-bottom.png</normaloff>:/ico/16x16/go-bottom.png</iconset>
|
||||
<iconset theme="go-bottom"/>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
@@ -123,8 +122,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="go-top" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/go-top.png</normaloff>:/ico/16x16/go-top.png</iconset>
|
||||
<iconset theme="go-top"/>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
@@ -140,8 +138,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="view-refresh" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/view-refresh.png</normaloff>:/ico/16x16/view-refresh.png</iconset>
|
||||
<iconset theme="view-refresh"/>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
|
||||
@@ -84,8 +84,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="dialog-ok" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/22x22/dialog-ok.png</normaloff>:/ico/22x22/dialog-ok.png</iconset>
|
||||
<iconset theme="dialog-ok"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -63,8 +63,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="dialog-ok" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/dialog-ok.png</normaloff>:/ico/16x16/dialog-ok.png</iconset>
|
||||
<iconset theme="dialog-ok"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -54,8 +54,7 @@
|
||||
</widget>
|
||||
<action name="m_add_terminal_strip">
|
||||
<property name="icon">
|
||||
<iconset theme="list-add" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/list-add.png</normaloff>:/ico/16x16/list-add.png</iconset>
|
||||
<iconset theme="list-add"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Ajouter un bornier</string>
|
||||
@@ -66,8 +65,7 @@
|
||||
</action>
|
||||
<action name="m_remove_terminal">
|
||||
<property name="icon">
|
||||
<iconset theme="list-remove" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/list-remove.png</normaloff>:/ico/16x16/list-remove.png</iconset>
|
||||
<iconset theme="list-remove"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Supprimer le bornier</string>
|
||||
@@ -78,8 +76,7 @@
|
||||
</action>
|
||||
<action name="m_reload">
|
||||
<property name="icon">
|
||||
<iconset theme="view-refresh" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/view-refresh.png</normaloff>:/ico/16x16/view-refresh.png</iconset>
|
||||
<iconset theme="view-refresh"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Recharger</string>
|
||||
|
||||
@@ -73,8 +73,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="view-refresh" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/view-refresh.png</normaloff>:/ico/16x16/view-refresh.png</iconset>
|
||||
<iconset theme="view-refresh"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -161,8 +160,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="view-refresh" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/view-refresh.png</normaloff>:/ico/16x16/view-refresh.png</iconset>
|
||||
<iconset theme="view-refresh"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -238,8 +236,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="view-refresh" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/view-refresh.png</normaloff>:/ico/16x16/view-refresh.png</iconset>
|
||||
<iconset theme="view-refresh"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -319,8 +316,7 @@
|
||||
<string>Configurer</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="configure" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/configure.png</normaloff>:/ico/16x16/configure.png</iconset>
|
||||
<iconset theme="configure"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -63,8 +63,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="edit-delete" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/edit-delete.png</normaloff>:/ico/16x16/edit-delete.png</iconset>
|
||||
<iconset theme="edit-delete"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -136,8 +135,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="list-remove" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/22x22/list-remove.png</normaloff>:/ico/22x22/list-remove.png</iconset>
|
||||
<iconset theme="list-remove"/>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
@@ -153,8 +151,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="list-add" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/22x22/list-add.png</normaloff>:/ico/22x22/list-add.png</iconset>
|
||||
<iconset theme="list-add"/>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
@@ -170,8 +167,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="arrow-left" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/arrow-left.png</normaloff>:/ico/16x16/arrow-left.png</iconset>
|
||||
<iconset theme="arrow-left"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -184,8 +180,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="arrow-right" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/arrow-right.png</normaloff>:/ico/16x16/arrow-right.png</iconset>
|
||||
<iconset theme="arrow-right"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -627,6 +627,12 @@ QHash<Element *, int> folioIndex(QETProject &project)
|
||||
/// From-to wiring list: one row per conductor, each endpoint resolved to its
|
||||
/// element label and terminal name.
|
||||
///
|
||||
/// Most symbols leave their terminals unnamed, so each end also carries the
|
||||
/// terminal's index -- the one the scripting API's addConductor() takes,
|
||||
/// empty for two terminals at one point -- and its uuid, the one the project
|
||||
/// file names the terminal by. These come last, after the columns the list
|
||||
/// has always had, so a reader of those is not disturbed.
|
||||
///
|
||||
/// Reads wiring_list_view out of the project database. --export-cables produces
|
||||
/// the same logical list from the document XML instead, and the two are meant
|
||||
/// to agree: running both and diffing them is a direct check that the database
|
||||
@@ -639,7 +645,8 @@ int exportWiring(QETProject &project, const QString &output)
|
||||
|
||||
static const QStringList columns {
|
||||
"wire_number", "from_element_label", "from_terminal",
|
||||
"to_element_label", "to_terminal", "diagram_position", "conductor_uuid"
|
||||
"to_element_label", "to_terminal", "diagram_position", "conductor_uuid",
|
||||
"from_terminal_index", "from_terminal_uuid", "to_terminal_index", "to_terminal_uuid"
|
||||
};
|
||||
|
||||
QSqlQuery query = project.dataBase()->newQuery(
|
||||
|
||||
@@ -0,0 +1,342 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#include "conductorrouter.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
using ConductorRouter::Direction;
|
||||
|
||||
constexpr qreal eps = 1e-6;
|
||||
///Larger than any folio needs: a 2000 x 2000 grid
|
||||
constexpr int max_nodes = 4000000;
|
||||
|
||||
///What each thing costs, in scene units of length. A bend is worth
|
||||
///three grid steps of extra wire, so the search prefers a slightly
|
||||
///longer route to a more crooked one, as a person drawing would.
|
||||
constexpr qreal bend_steps = 3.0;
|
||||
///Running along another wire costs this much more per unit of length
|
||||
constexpr qreal along_factor = 3.0;
|
||||
///Each crossing of another wire costs two grid steps
|
||||
constexpr qreal cross_steps = 2.0;
|
||||
|
||||
QPointF step(Direction d)
|
||||
{
|
||||
switch (d) {
|
||||
case Direction::North: return {0, -1};
|
||||
case Direction::East: return {1, 0};
|
||||
case Direction::South: return {0, 1};
|
||||
case Direction::West: return {-1, 0};
|
||||
}
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
Direction opposite(Direction d)
|
||||
{
|
||||
return static_cast<Direction>((static_cast<int>(d) + 2) % 4);
|
||||
}
|
||||
|
||||
bool strictlyInside(const QPointF &p, const QRectF &r)
|
||||
{
|
||||
return p.x() > r.left() + eps && p.x() < r.right() - eps
|
||||
&& p.y() > r.top() + eps && p.y() < r.bottom() - eps;
|
||||
}
|
||||
|
||||
bool insideAny(const QPointF &p, const QList<QRectF> &rects)
|
||||
{
|
||||
for (const QRectF &r : rects)
|
||||
if (strictlyInside(p, r)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
///A wire segment, kept as the fixed coordinate and the range along the
|
||||
///other axis
|
||||
struct Span { qreal at, from, to; };
|
||||
|
||||
///The first point of a route after a terminal: one grid step out in the
|
||||
///terminal's direction, snapped to the grid the way
|
||||
///Conductor::extendTerminal() snaps it, then on until it is clear of
|
||||
///every obstacle -- of the terminal's own symbol above all.
|
||||
bool exitPoint(const QPointF &dock, Direction d, const ConductorRouter::Request &r,
|
||||
const QList<QRectF> &obstacles, QPointF &out)
|
||||
{
|
||||
const QPointF s = step(d);
|
||||
QPointF p = dock;
|
||||
if (s.x() != 0)
|
||||
p.setX(std::round((dock.x() + s.x() * r.grid) / r.grid) * r.grid);
|
||||
else
|
||||
p.setY(std::round((dock.y() + s.y() * r.grid) / r.grid) * r.grid);
|
||||
for (int i = 0; i < 200; ++i) {
|
||||
if (!insideAny(p, obstacles)) {
|
||||
out = p;
|
||||
return true;
|
||||
}
|
||||
p += s * r.grid;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
///Sorted, without duplicates: the grid lines inside [low, high], plus
|
||||
///the extra coordinates (the two exit points, which need not be on the
|
||||
///grid when a terminal is not)
|
||||
QVector<qreal> axis(qreal low, qreal high, qreal grid, std::initializer_list<qreal> extra)
|
||||
{
|
||||
QVector<qreal> v;
|
||||
for (qreal c = std::ceil(low / grid) * grid; c <= high + eps; c += grid)
|
||||
v << c;
|
||||
for (qreal c : extra) v << c;
|
||||
std::sort(v.begin(), v.end());
|
||||
v.erase(std::unique(v.begin(), v.end(),
|
||||
[](qreal a, qreal b) { return std::abs(a - b) < eps; }),
|
||||
v.end());
|
||||
return v;
|
||||
}
|
||||
|
||||
int indexOf(const QVector<qreal> &v, qreal c)
|
||||
{
|
||||
auto it = std::lower_bound(v.begin(), v.end(), c - eps);
|
||||
return (it != v.end() && std::abs(*it - c) < eps) ? int(it - v.begin()) : -1;
|
||||
}
|
||||
|
||||
///[first, last) of the sorted v: the values strictly between low and high
|
||||
std::pair<int, int> openRange(const QVector<qreal> &v, qreal low, qreal high)
|
||||
{
|
||||
const int first = int(std::upper_bound(v.begin(), v.end(), low) - v.begin());
|
||||
const int last = int(std::lower_bound(v.begin(), v.end(), high) - v.begin());
|
||||
return {first, std::max(first, last)};
|
||||
}
|
||||
|
||||
///The cost of every edge along one axis, or infinity where an obstacle
|
||||
///blocks it. Edge (k, i) runs from pos[i] to pos[i + 1] at the fixed
|
||||
///coordinate lines[k], and is stored at [k * pos.size() + i];
|
||||
///horizontal says which axis pos is. Each obstacle and wire visits only
|
||||
///the edges it can touch, so the cost grows with the folio, not with
|
||||
///the folio times everything drawn on it.
|
||||
std::vector<qreal> edgeCosts(bool horizontal,
|
||||
const QVector<qreal> &pos, const QVector<qreal> &lines,
|
||||
const QList<QRectF> &obstacles,
|
||||
const QVector<Span> &along, const QVector<Span> &across,
|
||||
qreal grid)
|
||||
{
|
||||
const int np = pos.size(), nl = lines.size();
|
||||
std::vector<qreal> cost(size_t(np) * nl, std::numeric_limits<qreal>::infinity());
|
||||
for (int k = 0; k < nl; ++k)
|
||||
for (int i = 0; i + 1 < np; ++i)
|
||||
cost[size_t(k) * np + i] = pos[i + 1] - pos[i];
|
||||
|
||||
// The edges i that can overlap (low, high): pos[i + 1] > low and
|
||||
// pos[i] < high. The exact test is applied to each one found.
|
||||
const auto edges = [&](qreal low, qreal high) {
|
||||
const int first = int(std::upper_bound(pos.begin(), pos.end(), low) - pos.begin()) - 1;
|
||||
const int last = int(std::lower_bound(pos.begin(), pos.end(), high) - pos.begin());
|
||||
return std::pair<int, int>{std::max(0, first), std::min(np - 1, last)};
|
||||
};
|
||||
|
||||
for (const QRectF &r : obstacles) {
|
||||
const qreal lo = horizontal ? r.left() : r.top();
|
||||
const qreal hi = horizontal ? r.right() : r.bottom();
|
||||
const qreal flo = horizontal ? r.top() : r.left();
|
||||
const qreal fhi = horizontal ? r.bottom(): r.right();
|
||||
const auto [k0, k1] = openRange(lines, flo, fhi);
|
||||
const auto [i0, i1] = edges(lo, hi);
|
||||
for (int k = k0; k < k1; ++k) {
|
||||
if (!(lines[k] > flo + eps && lines[k] < fhi - eps)) continue;
|
||||
for (int i = i0; i < i1; ++i)
|
||||
if (std::min(pos[i + 1], hi) - std::max(pos[i], lo) > eps)
|
||||
cost[size_t(k) * np + i] = std::numeric_limits<qreal>::infinity();
|
||||
}
|
||||
}
|
||||
for (const Span &s : along) {
|
||||
const int k0 = int(std::lower_bound(lines.begin(), lines.end(), s.at - 0.5) - lines.begin());
|
||||
const auto [i0, i1] = edges(s.from, s.to);
|
||||
for (int k = k0; k < nl && lines[k] <= s.at + 0.5; ++k) {
|
||||
if (std::abs(s.at - lines[k]) > 0.5) continue;
|
||||
for (int i = i0; i < i1; ++i) {
|
||||
const qreal overlap = std::min(pos[i + 1], s.to) - std::max(pos[i], s.from);
|
||||
if (overlap > eps) cost[size_t(k) * np + i] += overlap * along_factor;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const Span &s : across) {
|
||||
// Half-open (a, b], so a crossing on a grid node is counted
|
||||
// once, by the edge that ends on it; and only through the
|
||||
// other wire's interior, not at its end, which is a junction.
|
||||
const auto [k0, k1] = openRange(lines, s.from, s.to);
|
||||
const auto [i0, i1] = edges(s.at - 1.0, s.at + 1.0);
|
||||
for (int k = k0; k < k1; ++k) {
|
||||
if (!(lines[k] > s.from + 0.5 && lines[k] < s.to - 0.5)) continue;
|
||||
for (int i = i0; i < i1; ++i)
|
||||
if (s.at > pos[i] + eps && s.at <= pos[i + 1] + eps)
|
||||
cost[size_t(k) * np + i] += cross_steps * grid;
|
||||
}
|
||||
}
|
||||
return cost;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ConductorRouter::Result ConductorRouter::route(const Request &r)
|
||||
{
|
||||
Result result;
|
||||
if (r.grid <= 0) {
|
||||
result.error = QStringLiteral("the grid step must be positive");
|
||||
return result;
|
||||
}
|
||||
|
||||
QList<QRectF> obstacles;
|
||||
for (const QRectF &o : r.obstacles)
|
||||
obstacles << o.normalized().adjusted(-r.margin, -r.margin, r.margin, r.margin);
|
||||
|
||||
QPointF s1, s2;
|
||||
if (!exitPoint(r.start, r.start_direction, r, obstacles, s1)
|
||||
|| !exitPoint(r.end, r.end_direction, r, obstacles, s2)) {
|
||||
result.error = QStringLiteral("a terminal has no way out of the symbols around it");
|
||||
return result;
|
||||
}
|
||||
|
||||
// The area searched: everything involved, with room to go round
|
||||
// it, kept on the folio when there is one.
|
||||
QRectF box = QRectF(s1, s2).normalized();
|
||||
for (const QRectF &o : obstacles) box = box.united(o);
|
||||
box.adjust(-3 * r.grid, -3 * r.grid, 3 * r.grid, 3 * r.grid);
|
||||
if (r.bounds.isValid()) box = box.intersected(r.bounds);
|
||||
box = box.united(QRectF(s1, s2).normalized());
|
||||
|
||||
const QVector<qreal> xs = axis(box.left(), box.right(), r.grid, {s1.x(), s2.x()});
|
||||
const QVector<qreal> ys = axis(box.top(), box.bottom(), r.grid, {s1.y(), s2.y()});
|
||||
const int nx = xs.size(), ny = ys.size();
|
||||
if (qint64(nx) * ny > max_nodes) {
|
||||
result.error = QStringLiteral("the area to search is too large");
|
||||
return result;
|
||||
}
|
||||
|
||||
QVector<Span> horizontal_wires, vertical_wires;
|
||||
for (const QVector<QPointF> &w : r.wires) {
|
||||
for (int i = 0; i + 1 < w.size(); ++i) {
|
||||
const QPointF a = w.at(i), b = w.at(i + 1);
|
||||
if (std::abs(a.y() - b.y()) < 0.5 && std::abs(a.x() - b.x()) > eps)
|
||||
horizontal_wires << Span{a.y(), std::min(a.x(), b.x()), std::max(a.x(), b.x())};
|
||||
else if (std::abs(a.x() - b.x()) < 0.5 && std::abs(a.y() - b.y()) > eps)
|
||||
vertical_wires << Span{a.x(), std::min(a.y(), b.y()), std::max(a.y(), b.y())};
|
||||
}
|
||||
}
|
||||
|
||||
// The cost of the edge from each node to the next one east,
|
||||
// h_cost[j * nx + i], and south, v_cost[i * ny + j]; infinity
|
||||
// where blocked or none.
|
||||
const qreal inf = std::numeric_limits<qreal>::infinity();
|
||||
const std::vector<qreal> h_cost = edgeCosts(true, xs, ys, obstacles,
|
||||
horizontal_wires, vertical_wires, r.grid);
|
||||
const std::vector<qreal> v_cost = edgeCosts(false, ys, xs, obstacles,
|
||||
vertical_wires, horizontal_wires, r.grid);
|
||||
|
||||
const int start_node = indexOf(ys, s1.y()) * nx + indexOf(xs, s1.x());
|
||||
const int goal_node = indexOf(ys, s2.y()) * nx + indexOf(xs, s2.x());
|
||||
const qreal bend = bend_steps * r.grid;
|
||||
// The wire arrives at the second terminal moving opposite to the
|
||||
// way it points.
|
||||
const Direction arrival = opposite(r.end_direction);
|
||||
|
||||
// Dijkstra over (node, direction of travel): the direction is what
|
||||
// lets a bend be charged for.
|
||||
const size_t states = size_t(nx) * ny * 4;
|
||||
std::vector<qreal> dist(states, inf);
|
||||
std::vector<int> previous(states, -1);
|
||||
using Entry = std::pair<qreal, int>;
|
||||
std::priority_queue<Entry, std::vector<Entry>, std::greater<Entry>> queue;
|
||||
const int first = start_node * 4 + int(r.start_direction);
|
||||
dist[size_t(first)] = 0;
|
||||
queue.push({0, first});
|
||||
|
||||
qreal best = inf;
|
||||
int best_state = -1;
|
||||
while (!queue.empty()) {
|
||||
const auto [cost, state] = queue.top();
|
||||
queue.pop();
|
||||
if (cost > dist[size_t(state)] || cost >= best) {
|
||||
if (cost >= best) break;
|
||||
continue;
|
||||
}
|
||||
const int node = state / 4;
|
||||
const Direction d = static_cast<Direction>(state % 4);
|
||||
if (node == goal_node && d != r.end_direction) {
|
||||
const qreal total = cost + (d == arrival ? 0 : bend);
|
||||
if (total < best) { best = total; best_state = state; }
|
||||
}
|
||||
const int i = node % nx, j = node / nx;
|
||||
for (int k = 0; k < 4; ++k) {
|
||||
const Direction nd = static_cast<Direction>(k);
|
||||
if (nd == opposite(d)) continue;
|
||||
int ni = i, nj = j;
|
||||
qreal edge = inf;
|
||||
switch (nd) {
|
||||
case Direction::East: if (i + 1 < nx) { ni = i + 1; edge = h_cost[size_t(j) * nx + i]; } break;
|
||||
case Direction::West: if (i > 0) { ni = i - 1; edge = h_cost[size_t(j) * nx + ni]; } break;
|
||||
case Direction::South: if (j + 1 < ny) { nj = j + 1; edge = v_cost[size_t(i) * ny + j]; } break;
|
||||
case Direction::North: if (j > 0) { nj = j - 1; edge = v_cost[size_t(i) * ny + nj]; } break;
|
||||
}
|
||||
if (edge == inf) continue;
|
||||
const qreal next_cost = cost + edge + (nd == d ? 0 : bend);
|
||||
const int next = (nj * nx + ni) * 4 + k;
|
||||
if (next_cost < dist[size_t(next)]) {
|
||||
dist[size_t(next)] = next_cost;
|
||||
previous[size_t(next)] = state;
|
||||
queue.push({next_cost, next});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (best_state < 0) {
|
||||
result.error = QStringLiteral("no path around the symbols was found");
|
||||
return result;
|
||||
}
|
||||
|
||||
QList<QPointF> chain;
|
||||
for (int s = best_state; s >= 0; s = previous[size_t(s)]) {
|
||||
const int node = s / 4;
|
||||
chain.prepend(QPointF(xs[node % nx], ys[node / nx]));
|
||||
}
|
||||
|
||||
// Keep only the corners. The two exit points stay even when
|
||||
// straight on, as Conductor::generateConductorPath() keeps them:
|
||||
// the segment from a terminal to its exit point is the one the
|
||||
// application holds fixed when a wire is edited by hand.
|
||||
QList<QPointF> corners;
|
||||
for (int k = 0; k < chain.size(); ++k) {
|
||||
const QPointF p = chain.at(k);
|
||||
if (k > 0 && k + 1 < chain.size()) {
|
||||
const QPointF a = chain.at(k - 1), b = chain.at(k + 1);
|
||||
const bool straight = (std::abs(a.x() - p.x()) < eps && std::abs(b.x() - p.x()) < eps)
|
||||
|| (std::abs(a.y() - p.y()) < eps && std::abs(b.y() - p.y()) < eps);
|
||||
if (straight) continue;
|
||||
}
|
||||
corners << p;
|
||||
}
|
||||
|
||||
result.points << r.start;
|
||||
for (const QPointF &p : corners)
|
||||
if (result.points.last() != p) result.points << p;
|
||||
if (result.points.last() != r.end) result.points << r.end;
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#ifndef CONDUCTORROUTER_H
|
||||
#define CONDUCTORROUTER_H
|
||||
|
||||
#include <QList>
|
||||
#include <QPointF>
|
||||
#include <QRectF>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
|
||||
/**
|
||||
@brief The ConductorRouter namespace
|
||||
Finds a path for a conductor that goes around obstacles -- the bounding
|
||||
rectangles of the symbols on the folio -- instead of the default two or
|
||||
three straight segments, which run through whatever lies between the two
|
||||
terminals. Kept free of any QGraphicsItem so the geometry can be tested
|
||||
on its own.
|
||||
|
||||
The path is made of horizontal and vertical segments only, like every
|
||||
conductor. It leaves each terminal in the terminal's own direction,
|
||||
runs on the folio grid, and is the cheapest one found by a search that
|
||||
charges for length, for every bend, and for running along or crossing
|
||||
the wires already drawn.
|
||||
*/
|
||||
namespace ConductorRouter
|
||||
{
|
||||
///The direction a terminal points, outward from its symbol.
|
||||
///Same order as Qet::Orientation.
|
||||
enum class Direction { North, East, South, West };
|
||||
|
||||
struct Request
|
||||
{
|
||||
QPointF start; ///< the first terminal's docking point
|
||||
Direction start_direction = Direction::North;
|
||||
QPointF end; ///< the second terminal's docking point
|
||||
Direction end_direction = Direction::North;
|
||||
///Areas no segment may cross. A margin is added to each.
|
||||
QList<QRectF> obstacles;
|
||||
///The other wires on the folio, each as its list of points.
|
||||
///Running along one or crossing one costs extra.
|
||||
QList<QVector<QPointF>> wires;
|
||||
///The route stays inside this, when it is valid (the folio).
|
||||
QRectF bounds;
|
||||
qreal grid = 10.0;
|
||||
///Added around each obstacle, so a route keeps clear of it
|
||||
qreal margin = 5.0;
|
||||
};
|
||||
|
||||
struct Result
|
||||
{
|
||||
///From start to end, both included; empty if none was found
|
||||
QList<QPointF> points;
|
||||
///Why there is no route, when points is empty
|
||||
QString error;
|
||||
};
|
||||
|
||||
Result route(const Request &request);
|
||||
}
|
||||
|
||||
#endif // CONDUCTORROUTER_H
|
||||
@@ -180,8 +180,37 @@ struct DocumentTerminal
|
||||
QString uuid;
|
||||
QString name;
|
||||
bool master_label = false;
|
||||
QVariant index;
|
||||
};
|
||||
|
||||
//The index each of @p points has in Element::terminals() -- the index
|
||||
//the scripting API's addConductor() and conductor calls take -- or a
|
||||
//null QVariant for a point another terminal shares. Element::
|
||||
//parseTerminal() sorts the list top to bottom, then left to right, on
|
||||
//each terminal's position in its definition, and the sort is not
|
||||
//stable, so which of two terminals at the same point comes first is
|
||||
//not defined: no index is given rather than one that can change.
|
||||
QList<QVariant> terminalIndexes(const QList<QPointF> &points)
|
||||
{
|
||||
QList<int> order;
|
||||
for (int i = 0 ; i < points.size() ; ++i) {
|
||||
order << i;
|
||||
}
|
||||
std::stable_sort(order.begin(), order.end(), [&points](int a, int b) {
|
||||
if (points.at(a).y() == points.at(b).y()) {
|
||||
return points.at(a).x() < points.at(b).x();
|
||||
}
|
||||
return points.at(a).y() < points.at(b).y();
|
||||
});
|
||||
QList<QVariant> indexes(points.size());
|
||||
for (int i = 0 ; i < order.size() ; ++i) {
|
||||
if (points.count(points.at(order.at(i))) == 1) {
|
||||
indexes[order.at(i)] = i;
|
||||
}
|
||||
}
|
||||
return indexes;
|
||||
}
|
||||
|
||||
struct DocumentDefinition
|
||||
{
|
||||
QString type;
|
||||
@@ -391,6 +420,25 @@ bool projectDataBase::populateFromDocument(const QDomDocument &document, QString
|
||||
data.fromXml(definition);
|
||||
read.type = data.typeToString();
|
||||
read.sub_type = data.masterTypeToString();
|
||||
//Every terminal the element is built with counts towards
|
||||
//the indexes, as Element::parseTerminal() keeps every one
|
||||
//whose position reads, in every <description>.
|
||||
QList<QDomElement> parsed;
|
||||
QList<QPointF> points;
|
||||
for (QDomElement d = definition.firstChildElement(QStringLiteral("description")) ;
|
||||
!d.isNull() ; d = d.nextSiblingElement(QStringLiteral("description"))) {
|
||||
for (QDomElement t = d.firstChildElement(QStringLiteral("terminal")) ;
|
||||
!t.isNull() ; t = t.nextSiblingElement(QStringLiteral("terminal")))
|
||||
{
|
||||
qreal x, y;
|
||||
if (QET::attributeIsAReal(t, QStringLiteral("x"), &x)
|
||||
&& QET::attributeIsAReal(t, QStringLiteral("y"), &y)) {
|
||||
parsed << t;
|
||||
points << QPointF(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
const QList<QVariant> indexes = terminalIndexes(points);
|
||||
const QDomElement description = definition.firstChildElement(QStringLiteral("description"));
|
||||
for (QDomElement t = description.firstChildElement(QStringLiteral("terminal")) ;
|
||||
!t.isNull() ; t = t.nextSiblingElement(QStringLiteral("terminal")))
|
||||
@@ -403,6 +451,10 @@ bool projectDataBase::populateFromDocument(const QDomDocument &document, QString
|
||||
terminal.uuid = terminal_uuid.toString();
|
||||
terminal.name = t.attribute(QStringLiteral("name"));
|
||||
terminal.master_label = t.attribute(QStringLiteral("use_master_label")) == QLatin1String("true");
|
||||
const int place = parsed.indexOf(t);
|
||||
if (place >= 0) {
|
||||
terminal.index = indexes.at(place);
|
||||
}
|
||||
read.terminals.insert(terminal_uuid, terminal);
|
||||
}
|
||||
known = definitions.insert(type, read);
|
||||
@@ -582,8 +634,8 @@ bool projectDataBase::populateFromDocument(const QDomDocument &document, QString
|
||||
for (const auto &end : {std::make_pair(conductor.element1, conductor.terminal1),
|
||||
std::make_pair(conductor.element2, conductor.terminal2)}) {
|
||||
const DocumentElement &owner = elements.at(element_index.value(end.first));
|
||||
insertTerminal(end.second, end.first,
|
||||
owner.terminals.value(QUuid(end.second)).name);
|
||||
const DocumentTerminal &terminal = owner.terminals.value(QUuid(end.second));
|
||||
insertTerminal(end.second, end.first, terminal.name, terminal.index);
|
||||
}
|
||||
m_insert_conductor_query.bindValue(QStringLiteral(":uuid"), conductor.uuid);
|
||||
m_insert_conductor_query.bindValue(QStringLiteral(":diagram_uuid"), conductor.diagram_uuid);
|
||||
@@ -1560,6 +1612,7 @@ bool projectDataBase::createDataBase()
|
||||
"uuid VARCHAR(50) NOT NULL, "
|
||||
"element_uuid VARCHAR(50) NOT NULL,"
|
||||
"name VARCHAR(50),"
|
||||
"terminal_index INTEGER,"
|
||||
"PRIMARY KEY (uuid, element_uuid),"
|
||||
"FOREIGN KEY (element_uuid) REFERENCES element (uuid)"
|
||||
")");
|
||||
@@ -1812,7 +1865,11 @@ void projectDataBase::createWiringListView()
|
||||
"t2.element_uuid AS to_element_uuid,"
|
||||
"ei2.label AS to_element_label,"
|
||||
"t2.name AS to_terminal,"
|
||||
"d.pos AS diagram_position"
|
||||
"d.pos AS diagram_position,"
|
||||
"t1.uuid AS from_terminal_uuid,"
|
||||
"t1.terminal_index AS from_terminal_index,"
|
||||
"t2.uuid AS to_terminal_uuid,"
|
||||
"t2.terminal_index AS to_terminal_index"
|
||||
" FROM conductor c"
|
||||
" JOIN terminal t1 ON c.terminal1_uuid = t1.uuid AND c.terminal1_element_uuid = t1.element_uuid"
|
||||
" JOIN terminal t2 ON c.terminal2_uuid = t2.uuid AND c.terminal2_element_uuid = t2.element_uuid"
|
||||
@@ -1999,9 +2056,15 @@ void projectDataBase::populateConductorTable()
|
||||
*/
|
||||
void projectDataBase::insertTerminal(Terminal *terminal)
|
||||
{
|
||||
const QList<Terminal *> terminals = terminal->parentElement()->terminals();
|
||||
QList<QPointF> points;
|
||||
for (const Terminal *t : terminals) {
|
||||
points << terminal->parentElement()->mapFromScene(t->dockConductor());
|
||||
}
|
||||
insertTerminal(terminal->stableUuid().toString(),
|
||||
terminal->parentElement()->uuid().toString(),
|
||||
terminal->name());
|
||||
terminal->name(),
|
||||
terminalIndexes(points).value(terminals.indexOf(terminal)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2009,11 +2072,12 @@ void projectDataBase::insertTerminal(Terminal *terminal)
|
||||
insertTerminal(Terminal *) from values rather than a live terminal.
|
||||
*/
|
||||
void projectDataBase::insertTerminal(const QString &uuid, const QString &element_uuid,
|
||||
const QString &name)
|
||||
const QString &name, const QVariant &index)
|
||||
{
|
||||
m_insert_terminal_query.bindValue(":uuid", uuid);
|
||||
m_insert_terminal_query.bindValue(":element_uuid", element_uuid);
|
||||
m_insert_terminal_query.bindValue(":name", name);
|
||||
m_insert_terminal_query.bindValue(":terminal_index", index);
|
||||
if (!m_insert_terminal_query.exec()) {
|
||||
qDebug() << "projectDataBase::insertTerminal insert error : " << m_insert_terminal_query.lastError();
|
||||
}
|
||||
@@ -2146,7 +2210,7 @@ void projectDataBase::prepareQuery()
|
||||
|
||||
//INSERT TERMINAL
|
||||
m_insert_terminal_query = QSqlQuery(m_data_base);
|
||||
m_insert_terminal_query.prepare("INSERT OR IGNORE INTO terminal (uuid, element_uuid, name) VALUES (:uuid, :element_uuid, :name)");
|
||||
m_insert_terminal_query.prepare("INSERT OR IGNORE INTO terminal (uuid, element_uuid, name, terminal_index) VALUES (:uuid, :element_uuid, :name, :terminal_index)");
|
||||
|
||||
//INSERT CONDUCTOR
|
||||
m_insert_conductor_query = QSqlQuery(m_data_base);
|
||||
|
||||
@@ -150,7 +150,7 @@ class projectDataBase : public QObject
|
||||
void watchConductor(Conductor *conductor);
|
||||
void insertTerminal(Terminal *terminal);
|
||||
void insertTerminal(const QString &uuid, const QString &element_uuid,
|
||||
const QString &name);
|
||||
const QString &name, const QVariant &index);
|
||||
void prepareQuery();
|
||||
static QHash<QString, QString> elementInfoToString(
|
||||
Element *elmt);
|
||||
|
||||
@@ -88,8 +88,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="go-up" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/go-up.png</normaloff>:/ico/16x16/go-up.png</iconset>
|
||||
<iconset theme="go-up"/>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
@@ -105,8 +104,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="list-add" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/list-add.png</normaloff>:/ico/16x16/list-add.png</iconset>
|
||||
<iconset theme="list-add"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -119,8 +117,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="list-remove" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/list-remove.png</normaloff>:/ico/16x16/list-remove.png</iconset>
|
||||
<iconset theme="list-remove"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -133,8 +130,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="go-down" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/go-down.png</normaloff>:/ico/16x16/go-down.png</iconset>
|
||||
<iconset theme="go-down"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -353,8 +349,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="folder-open" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/folder-open.png</normaloff>:/ico/16x16/folder-open.png</iconset>
|
||||
<iconset theme="folder-open"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -380,8 +375,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="document-save" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/document-save.png</normaloff>:/ico/16x16/document-save.png</iconset>
|
||||
<iconset theme="document-save"/>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
|
||||
@@ -76,8 +76,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="go-up" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/go-up.png</normaloff>:/ico/16x16/go-up.png</iconset>
|
||||
<iconset theme="go-up"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -87,8 +86,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="list-add" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/list-add.png</normaloff>:/ico/16x16/list-add.png</iconset>
|
||||
<iconset theme="list-add"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -98,8 +96,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="list-remove" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/list-remove.png</normaloff>:/ico/16x16/list-remove.png</iconset>
|
||||
<iconset theme="list-remove"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -109,8 +106,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="go-down" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/go-down.png</normaloff>:/ico/16x16/go-down.png</iconset>
|
||||
<iconset theme="go-down"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -42,6 +42,7 @@ void DiagramContext::add(DiagramContext other)
|
||||
*/
|
||||
void DiagramContext::remove(const QString &key) {
|
||||
m_content.remove(key);
|
||||
m_content_show.remove(key);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -206,7 +206,13 @@ void ElementPrimitiveDecorator::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
// When moving the selection, consider the position of the first selected item
|
||||
QPointF current_position = scene_pos - mouse_offset_;
|
||||
QPointF rounded_current_position = snapConstPointToGrid(current_position);
|
||||
// Snap to the editor's grid, as a single part does (bugtracker
|
||||
// #112); Ctrl keeps the fine 1-unit step.
|
||||
ElementScene *element_scene = qobject_cast<ElementScene *>(scene());
|
||||
QPointF rounded_current_position =
|
||||
(element_scene && !(event->modifiers() & Qt::ControlModifier))
|
||||
? element_scene->snapToGrid(current_position)
|
||||
: snapConstPointToGrid(current_position);
|
||||
movement = rounded_current_position - decorated_items_.at(0) -> toItem() -> scenePos();
|
||||
|
||||
QRectF bounding_rect = modified_bounding_rect_;
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
*/
|
||||
#include "elementview.h"
|
||||
|
||||
#include "elementviewgrid.h"
|
||||
|
||||
#include "../qetapp.h"
|
||||
#include "UndoCommand/pastepartscommand.h"
|
||||
#include "ui/qetelementeditor.h"
|
||||
@@ -535,29 +537,12 @@ void ElementView::drawBackground(QPainter *p, const QRectF &r) {
|
||||
|
||||
// choisit la granularite de la grille en fonction du zoom en cours
|
||||
// selects the grid granularity according to the current zoom level
|
||||
int drawn_x_grid = 1;//scene_ -> xGrid();
|
||||
int drawn_y_grid = 1;//scene_ -> yGrid();
|
||||
bool draw_grid = true;
|
||||
bool draw_cross = false;
|
||||
const ElementViewGrid grid = ElementViewGrid::forZoom(zoom_factor);
|
||||
const int grid_step = grid.step;
|
||||
const bool draw_grid = grid.draw_grid;
|
||||
const bool draw_cross = grid.draw_cross;
|
||||
|
||||
if (zoom_factor < 1.0) { //< no grid
|
||||
draw_grid = false;
|
||||
} else if (zoom_factor < 4.0) { //< grid 10*10
|
||||
drawn_x_grid *= 10;
|
||||
drawn_y_grid *= 10;
|
||||
}else if (zoom_factor < 8.0) { //< grid 5*5
|
||||
drawn_x_grid *= 5;
|
||||
drawn_y_grid *= 5;
|
||||
draw_cross = true;
|
||||
} else if (zoom_factor < 10.0) { //< grid 2*2
|
||||
drawn_x_grid *= 2;
|
||||
drawn_y_grid *= 2;
|
||||
draw_cross = true;
|
||||
} else { //< grid 1*1
|
||||
draw_cross = true;
|
||||
}
|
||||
|
||||
m_scene->setGrid(drawn_x_grid, drawn_y_grid);
|
||||
m_scene->setGrid(grid_step, grid_step);
|
||||
|
||||
if (draw_grid) {
|
||||
// draw the dots of the grid
|
||||
@@ -587,12 +572,12 @@ void ElementView::drawBackground(QPainter *p, const QRectF &r) {
|
||||
qreal limit_y = r.y() + r.height();
|
||||
|
||||
int g_x = (int)ceil(r.x());
|
||||
while (g_x % drawn_x_grid) ++ g_x;
|
||||
while (g_x % grid_step) ++ g_x;
|
||||
int g_y = (int)ceil(r.y());
|
||||
while (g_y % drawn_y_grid) ++ g_y;
|
||||
while (g_y % grid_step) ++ g_y;
|
||||
|
||||
for (int gx = g_x ; gx < limit_x ; gx += drawn_x_grid) {
|
||||
for (int gy = g_y ; gy < limit_y ; gy += drawn_y_grid) {
|
||||
for (int gx = g_x ; gx < limit_x ; gx += grid_step) {
|
||||
for (int gy = g_y ; gy < limit_y ; gy += grid_step) {
|
||||
if (draw_cross) {
|
||||
if (!(gx % 10) && !(gy % 10)) {
|
||||
p -> drawLine(QLineF(gx - (pen.width()/4.0), gy, gx + (pen.width()/4.0), gy));
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
#ifndef ELEMENTVIEWGRID_H
|
||||
#define ELEMENTVIEWGRID_H
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
/**
|
||||
The symbol editor's grid at a given zoom: the step parts snap to, and
|
||||
how the grid is drawn. The step gets finer as the view zooms in, so a
|
||||
small detail can be placed precisely.
|
||||
|
||||
Below 100 % the grid is not drawn (its dots would merge into a grey
|
||||
wash), but parts still snap every 10 units, as at 100 %. Snapping to 1
|
||||
unit there left everything dragged while zoomed out off the grid, with
|
||||
nothing on screen to show it (bugtracker #112).
|
||||
*/
|
||||
struct ElementViewGrid
|
||||
{
|
||||
int step = 10;
|
||||
bool draw_grid = true;
|
||||
bool draw_cross = false;
|
||||
|
||||
static ElementViewGrid forZoom(qreal zoom_factor)
|
||||
{
|
||||
ElementViewGrid grid;
|
||||
if (zoom_factor < 1.0) {
|
||||
grid.draw_grid = false;
|
||||
} else if (zoom_factor < 4.0) {
|
||||
grid.step = 10;
|
||||
} else if (zoom_factor < 8.0) {
|
||||
grid.step = 5;
|
||||
grid.draw_cross = true;
|
||||
} else if (zoom_factor < 10.0) {
|
||||
grid.step = 2;
|
||||
grid.draw_cross = true;
|
||||
} else {
|
||||
grid.step = 1;
|
||||
grid.draw_cross = true;
|
||||
}
|
||||
return grid;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // ELEMENTVIEWGRID_H
|
||||
@@ -48,8 +48,7 @@
|
||||
</layout>
|
||||
<action name="m_add_point_action">
|
||||
<property name="icon">
|
||||
<iconset theme="list-add" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/list-add.png</normaloff>:/ico/16x16/list-add.png</iconset>
|
||||
<iconset theme="list-add"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Ajouter un point</string>
|
||||
@@ -57,8 +56,7 @@
|
||||
</action>
|
||||
<action name="m_remove_point_action">
|
||||
<property name="icon">
|
||||
<iconset theme="list-remove" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/list-remove.png</normaloff>:/ico/16x16/list-remove.png</iconset>
|
||||
<iconset theme="list-remove"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Supprimer ce point</string>
|
||||
|
||||
@@ -765,6 +765,9 @@ void QETElementEditor::updateSelectionFromPartsList()
|
||||
}
|
||||
m_parts_list -> blockSignals(false);
|
||||
m_elmt_scene -> blockSignals(false);
|
||||
//selectionChanged was blocked above, so the selection decorator must be
|
||||
//updated by hand, otherwise dragging moves only the part under the cursor
|
||||
m_elmt_scene -> managePrimitivesGroups();
|
||||
updateInformations();
|
||||
updateAction();
|
||||
}
|
||||
|
||||
@@ -14,8 +14,7 @@
|
||||
<string>QElectroTech - Éditeur d'élément</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset theme="qet" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/qet.png</normaloff>:/ico/16x16/qet.png</iconset>
|
||||
<iconset theme="qet"/>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget"/>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
@@ -55,8 +54,7 @@
|
||||
<string>Coller depuis...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="edit-paste" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/edit-paste.png</normaloff>:/ico/16x16/edit-paste.png</iconset>
|
||||
<iconset theme="edit-paste"/>
|
||||
</property>
|
||||
<addaction name="m_paste_from_file_action"/>
|
||||
<addaction name="m_paste_from_element_action"/>
|
||||
@@ -202,8 +200,7 @@
|
||||
</widget>
|
||||
<action name="m_select_all_act">
|
||||
<property name="icon">
|
||||
<iconset theme="edit-select-all" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/edit-select-all.png</normaloff>:/ico/16x16/edit-select-all.png</iconset>
|
||||
<iconset theme="edit-select-all"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Tout sélectionner</string>
|
||||
@@ -211,8 +208,7 @@
|
||||
</action>
|
||||
<action name="m_new_action">
|
||||
<property name="icon">
|
||||
<iconset theme="document-new" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/22x22/document-new.png</normaloff>:/ico/22x22/document-new.png</iconset>
|
||||
<iconset theme="document-new"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Nouveau</string>
|
||||
@@ -220,8 +216,7 @@
|
||||
</action>
|
||||
<action name="m_open_action">
|
||||
<property name="icon">
|
||||
<iconset theme="folder-open" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/22x22/folder-open.png</normaloff>:/ico/22x22/folder-open.png</iconset>
|
||||
<iconset theme="folder-open"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Ouvrir</string>
|
||||
@@ -229,8 +224,7 @@
|
||||
</action>
|
||||
<action name="m_open_from_file_action">
|
||||
<property name="icon">
|
||||
<iconset theme="folder-open" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/22x22/folder-open.png</normaloff>:/ico/22x22/folder-open.png</iconset>
|
||||
<iconset theme="folder-open"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Ouvrir depuis un fichier</string>
|
||||
@@ -238,8 +232,7 @@
|
||||
</action>
|
||||
<action name="m_save_action">
|
||||
<property name="icon">
|
||||
<iconset theme="document-save" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/22x22/document-save.png</normaloff>:/ico/22x22/document-save.png</iconset>
|
||||
<iconset theme="document-save"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Enregistrer</string>
|
||||
@@ -247,8 +240,7 @@
|
||||
</action>
|
||||
<action name="m_save_as_action">
|
||||
<property name="icon">
|
||||
<iconset theme="document-save-as" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/22x22/document-save-as.png</normaloff>:/ico/22x22/document-save-as.png</iconset>
|
||||
<iconset theme="document-save-as"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enregistrer sous</string>
|
||||
@@ -256,8 +248,7 @@
|
||||
</action>
|
||||
<action name="m_save_as_file_action">
|
||||
<property name="icon">
|
||||
<iconset theme="document-save" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/22x22/document-save.png</normaloff>:/ico/22x22/document-save.png</iconset>
|
||||
<iconset theme="document-save"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enregistrer dans un fichier</string>
|
||||
@@ -265,8 +256,7 @@
|
||||
</action>
|
||||
<action name="m_export_svg_action">
|
||||
<property name="icon">
|
||||
<iconset theme="document-export" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/22x22/document-export.png</normaloff>:/ico/22x22/document-export.png</iconset>
|
||||
<iconset theme="document-export"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Exporter en SVG</string>
|
||||
@@ -274,8 +264,7 @@
|
||||
</action>
|
||||
<action name="m_reload_action">
|
||||
<property name="icon">
|
||||
<iconset theme="view-refresh" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/22x22/view-refresh.png</normaloff>:/ico/22x22/view-refresh.png</iconset>
|
||||
<iconset theme="view-refresh"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Recharger</string>
|
||||
@@ -283,8 +272,7 @@
|
||||
</action>
|
||||
<action name="m_quit_action">
|
||||
<property name="icon">
|
||||
<iconset theme="application-exit" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/application-exit.png</normaloff>:/ico/16x16/application-exit.png</iconset>
|
||||
<iconset theme="application-exit"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Fermer cet éditeur</string>
|
||||
@@ -295,8 +283,7 @@
|
||||
</action>
|
||||
<action name="m_deselect_all_action">
|
||||
<property name="icon">
|
||||
<iconset theme="edit-select-none" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/edit-select-none.png</normaloff>:/ico/16x16/edit-select-none.png</iconset>
|
||||
<iconset theme="edit-select-none"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Désélectionner tout</string>
|
||||
@@ -304,8 +291,7 @@
|
||||
</action>
|
||||
<action name="m_cut_action">
|
||||
<property name="icon">
|
||||
<iconset theme="edit-cut" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/edit-cut.png</normaloff>:/ico/16x16/edit-cut.png</iconset>
|
||||
<iconset theme="edit-cut"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Co&uper</string>
|
||||
@@ -313,8 +299,7 @@
|
||||
</action>
|
||||
<action name="m_copy_action">
|
||||
<property name="icon">
|
||||
<iconset theme="edit-copy" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/edit-copy.png</normaloff>:/ico/16x16/edit-copy.png</iconset>
|
||||
<iconset theme="edit-copy"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Cop&ier</string>
|
||||
@@ -322,8 +307,7 @@
|
||||
</action>
|
||||
<action name="m_paste_action">
|
||||
<property name="icon">
|
||||
<iconset theme="edit-paste" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/edit-paste.png</normaloff>:/ico/16x16/edit-paste.png</iconset>
|
||||
<iconset theme="edit-paste"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>C&oller</string>
|
||||
@@ -331,8 +315,7 @@
|
||||
</action>
|
||||
<action name="m_paste_in_area_action">
|
||||
<property name="icon">
|
||||
<iconset theme="edit-paste" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/edit-paste.png</normaloff>:/ico/16x16/edit-paste.png</iconset>
|
||||
<iconset theme="edit-paste"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>C&oller dans la zone</string>
|
||||
@@ -348,8 +331,7 @@
|
||||
</action>
|
||||
<action name="m_paste_from_element_action">
|
||||
<property name="icon">
|
||||
<iconset theme="element" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/element.png</normaloff>:/ico/16x16/element.png</iconset>
|
||||
<iconset theme="element"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Un élément</string>
|
||||
@@ -357,8 +339,7 @@
|
||||
</action>
|
||||
<action name="m_revert_selection_action">
|
||||
<property name="icon">
|
||||
<iconset theme="edit-select-invert" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/edit-select-invert.png</normaloff>:/ico/16x16/edit-select-invert.png</iconset>
|
||||
<iconset theme="edit-select-invert"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Inverser la sélection</string>
|
||||
@@ -366,8 +347,7 @@
|
||||
</action>
|
||||
<action name="m_delete_action">
|
||||
<property name="icon">
|
||||
<iconset theme="edit-delete" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/22x22/edit-delete.png</normaloff>:/ico/22x22/edit-delete.png</iconset>
|
||||
<iconset theme="edit-delete"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Supprimer</string>
|
||||
@@ -383,8 +363,7 @@
|
||||
</action>
|
||||
<action name="m_edit_author_action">
|
||||
<property name="icon">
|
||||
<iconset theme="preferences-desktop-user" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/preferences-desktop-user.png</normaloff>:/ico/16x16/preferences-desktop-user.png</iconset>
|
||||
<iconset theme="preferences-desktop-user"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Éditer les informations sur l'auteur</string>
|
||||
@@ -400,8 +379,7 @@
|
||||
</action>
|
||||
<action name="m_zoom_in_action">
|
||||
<property name="icon">
|
||||
<iconset theme="zoom-in" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/22x22/zoom-in.png</normaloff>:/ico/22x22/zoom-in.png</iconset>
|
||||
<iconset theme="zoom-in"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Zoom avant</string>
|
||||
@@ -409,8 +387,7 @@
|
||||
</action>
|
||||
<action name="m_zoom_out_action">
|
||||
<property name="icon">
|
||||
<iconset theme="zoom-out" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/22x22/zoom-out.png</normaloff>:/ico/22x22/zoom-out.png</iconset>
|
||||
<iconset theme="zoom-out"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Zoom arrière</string>
|
||||
@@ -434,8 +411,7 @@
|
||||
</action>
|
||||
<action name="m_about_qet_action">
|
||||
<property name="icon">
|
||||
<iconset theme="qet" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/qet.png</normaloff>:/ico/16x16/qet.png</iconset>
|
||||
<iconset theme="qet"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>À &propos de QElectroTech</string>
|
||||
@@ -446,8 +422,7 @@
|
||||
</action>
|
||||
<action name="m_online_manual">
|
||||
<property name="icon">
|
||||
<iconset theme="help-contents" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/help-contents.png</normaloff>:/ico/16x16/help-contents.png</iconset>
|
||||
<iconset theme="help-contents"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Manuel en ligne</string>
|
||||
@@ -458,8 +433,7 @@
|
||||
</action>
|
||||
<action name="m_youtube_action">
|
||||
<property name="icon">
|
||||
<iconset theme="kdenlive-show-video" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/kdenlive-show-video.png</normaloff>:/ico/16x16/kdenlive-show-video.png</iconset>
|
||||
<iconset theme="kdenlive-show-video"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Chaine Youtube</string>
|
||||
@@ -470,8 +444,7 @@
|
||||
</action>
|
||||
<action name="m_donate_action">
|
||||
<property name="icon">
|
||||
<iconset theme="help-donate" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/help-donate.png</normaloff>:/ico/16x16/help-donate.png</iconset>
|
||||
<iconset theme="help-donate"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Soutenir le projet par un don</string>
|
||||
@@ -482,8 +455,7 @@
|
||||
</action>
|
||||
<action name="m_about_qt_action">
|
||||
<property name="icon">
|
||||
<iconset theme="qt" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/qt.png</normaloff>:/ico/16x16/qt.png</iconset>
|
||||
<iconset theme="qt"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>À propos de &Qt</string>
|
||||
@@ -510,8 +482,7 @@
|
||||
</action>
|
||||
<action name="m_mirror_action">
|
||||
<property name="icon">
|
||||
<iconset theme="mirror" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/mirror.png</normaloff>:/ico/16x16/mirror.png</iconset>
|
||||
<iconset theme="mirror"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Mirror</string>
|
||||
@@ -519,8 +490,7 @@
|
||||
</action>
|
||||
<action name="m_flip_action">
|
||||
<property name="icon">
|
||||
<iconset theme="flip" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/flip.png</normaloff>:/ico/16x16/flip.png</iconset>
|
||||
<iconset theme="flip"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Flip</string>
|
||||
@@ -536,8 +506,7 @@
|
||||
</action>
|
||||
<action name="m_import_scaled_element">
|
||||
<property name="icon">
|
||||
<iconset theme="folder-open" resource="../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/22x22/folder-open.png</normaloff>:/ico/22x22/folder-open.png</iconset>
|
||||
<iconset theme="folder-open"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>importer un élément à redimensionner</string>
|
||||
|
||||
@@ -255,9 +255,9 @@ bool ElementsPanel::matchesFilter(const QTreeWidgetItem *item,
|
||||
*/
|
||||
void ElementsPanel::reload()
|
||||
{
|
||||
QIcon system_icon(":/ico/16x16/qet.png");
|
||||
QIcon system_icon = QIcon::fromTheme("qet");
|
||||
QIcon company_icon = QET::Icons::Company;
|
||||
QIcon user_icon(":/ico/16x16/go-home.png");
|
||||
QIcon user_icon = QIcon::fromTheme("go-home");
|
||||
|
||||
// load the common title block templates collection
|
||||
TitleBlockTemplatesCollection *common_tbt_collection = QETApp::commonTitleBlockTemplatesCollection();
|
||||
|
||||
@@ -134,8 +134,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="dialog-ok" resource="../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/dialog-ok.png</normaloff>:/ico/16x16/dialog-ok.png</iconset>
|
||||
<iconset theme="dialog-ok"/>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
@@ -305,8 +304,7 @@
|
||||
</widget>
|
||||
<action name="m_adjust_width_action">
|
||||
<property name="icon">
|
||||
<iconset theme="view_fit_width" resource="../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/22x22/view_fit_width.png</normaloff>:/ico/22x22/view_fit_width.png</iconset>
|
||||
<iconset theme="view_fit_width"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Ajuster la largeur</string>
|
||||
@@ -314,8 +312,7 @@
|
||||
</action>
|
||||
<action name="m_adjust_page_action">
|
||||
<property name="icon">
|
||||
<iconset theme="view-fit-window" resource="../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/22x22/view-fit-window.png</normaloff>:/ico/22x22/view-fit-window.png</iconset>
|
||||
<iconset theme="view-fit-window"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Ajuster la page</string>
|
||||
@@ -323,8 +320,7 @@
|
||||
</action>
|
||||
<action name="m_zoom_out_action">
|
||||
<property name="icon">
|
||||
<iconset theme="zoom-out" resource="../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/zoom-out.png</normaloff>:/ico/16x16/zoom-out.png</iconset>
|
||||
<iconset theme="zoom-out"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Zoom arrière</string>
|
||||
@@ -332,8 +328,7 @@
|
||||
</action>
|
||||
<action name="m_zoom_in_action">
|
||||
<property name="icon">
|
||||
<iconset theme="zoom-in" resource="../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/zoom-in.png</normaloff>:/ico/16x16/zoom-in.png</iconset>
|
||||
<iconset theme="zoom-in"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Zoom avant</string>
|
||||
@@ -365,8 +360,7 @@
|
||||
</action>
|
||||
<action name="m_previous_page_action">
|
||||
<property name="icon">
|
||||
<iconset theme="arrow-left" resource="../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/arrow-left.png</normaloff>:/ico/16x16/arrow-left.png</iconset>
|
||||
<iconset theme="arrow-left"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Page précédente</string>
|
||||
@@ -374,8 +368,7 @@
|
||||
</action>
|
||||
<action name="m_next_page_action">
|
||||
<property name="icon">
|
||||
<iconset theme="arrow-right" resource="../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/arrow-right.png</normaloff>:/ico/16x16/arrow-right.png</iconset>
|
||||
<iconset theme="arrow-right"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Page suivante</string>
|
||||
@@ -391,8 +384,7 @@
|
||||
</action>
|
||||
<action name="m_display_single_page_action">
|
||||
<property name="icon">
|
||||
<iconset theme="single_page" resource="../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/22x22/single_page.png</normaloff>:/ico/22x22/single_page.png</iconset>
|
||||
<iconset theme="single_page"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Afficher une seule page</string>
|
||||
@@ -400,8 +392,7 @@
|
||||
</action>
|
||||
<action name="m_display_two_page_action">
|
||||
<property name="icon">
|
||||
<iconset theme="two_pages" resource="../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/22x22/two_pages.png</normaloff>:/ico/22x22/two_pages.png</iconset>
|
||||
<iconset theme="two_pages"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Afficher deux pages</string>
|
||||
@@ -417,8 +408,7 @@
|
||||
</action>
|
||||
<action name="m_page_setup">
|
||||
<property name="icon">
|
||||
<iconset theme="document-print-frame" resource="../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/48x48/document-print-frame.png</normaloff>:/ico/48x48/document-print-frame.png</iconset>
|
||||
<iconset theme="document-print-frame"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>mise en page</string>
|
||||
|
||||
@@ -62,8 +62,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="arrow-right" resource="../../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/arrow-right.png</normaloff>:/ico/16x16/arrow-right.png</iconset>
|
||||
<iconset theme="arrow-right"/>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
@@ -178,8 +177,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="arrow-left" resource="../../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/arrow-left.png</normaloff>:/ico/16x16/arrow-left.png</iconset>
|
||||
<iconset theme="arrow-left"/>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
|
||||
@@ -20,8 +20,7 @@
|
||||
<string>Requête</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="edit-rename" resource="../../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/edit-rename.png</normaloff>:/ico/16x16/edit-rename.png</iconset>
|
||||
<iconset theme="edit-rename"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -31,8 +30,7 @@
|
||||
<string>Recharger</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="view-refresh" resource="../../../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/view-refresh.png</normaloff>:/ico/16x16/view-refresh.png</iconset>
|
||||
<iconset theme="view-refresh"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -1389,6 +1389,46 @@ bool Conductor::moveSegment(int index, qreal dx, qreal dy)
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Conductor::setPathPoints
|
||||
Replace this conductor's path by the one through @p scene_points, and
|
||||
push one undo step for it -- the same ChangeConductorCommand a manual
|
||||
handle drag or moveSegment() pushes, so the path is saved as a
|
||||
modified one and survives a reload.
|
||||
@param scene_points in scene coordinates, from terminal1's docking
|
||||
point to terminal2's, every segment horizontal or vertical
|
||||
@return false, changing nothing, if the points do not make such a path
|
||||
*/
|
||||
bool Conductor::setPathPoints(const QList<QPointF> &scene_points)
|
||||
{
|
||||
if (scene_points.size() < 2 || !terminal1 || !terminal2) return false;
|
||||
const auto near = [](const QPointF &a, const QPointF &b) {
|
||||
return qAbs(a.x() - b.x()) < 0.5 && qAbs(a.y() - b.y()) < 0.5;
|
||||
};
|
||||
if (!near(scene_points.first(), terminal1->dockConductor()) ||
|
||||
!near(scene_points.last(), terminal2->dockConductor()))
|
||||
return false;
|
||||
QList<QPointF> points;
|
||||
for (int i = 0; i < scene_points.size(); ++i) {
|
||||
const QPointF p = scene_points.at(i);
|
||||
if (i && qAbs(p.x() - scene_points.at(i - 1).x()) > 0.01
|
||||
&& qAbs(p.y() - scene_points.at(i - 1).y()) > 0.01)
|
||||
return false;
|
||||
points << mapFromScene(p);
|
||||
}
|
||||
// A two-point path has nothing to segment between: let the
|
||||
// application draw it, as for a conductor with no profile.
|
||||
if (points.size() < 3) return false;
|
||||
|
||||
before_mov_text_pos_ = m_text_item->pos();
|
||||
pointsToSegments(points);
|
||||
modified_path = true;
|
||||
segmentsToPath();
|
||||
calculateTextItemPosition();
|
||||
saveProfile();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Conductor::length
|
||||
@return the length of this conductor
|
||||
@@ -2305,6 +2345,38 @@ QPointF Conductor::movePointIntoPolygon(const QPointF &point, const QPainterPath
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
/// The two ends of @p conductor on the folio, smaller first, compared
|
||||
/// by x then y.
|
||||
QPair<QPointF, QPointF> conductorEnds(const Conductor *conductor)
|
||||
{
|
||||
auto less = [](const QPointF &a, const QPointF &b) {
|
||||
return a.x() < b.x() || (a.x() == b.x() && a.y() < b.y());
|
||||
};
|
||||
QPointF a = conductor->terminal1 ? conductor->terminal1->dockConductor() : QPointF();
|
||||
QPointF b = conductor->terminal2 ? conductor->terminal2->dockConductor() : QPointF();
|
||||
if (less(b, a))
|
||||
std::swap(a, b);
|
||||
return qMakePair(a, b);
|
||||
}
|
||||
|
||||
/// True if @p a comes before @p b in an order that is the same in every
|
||||
/// run: by where the conductor's ends are on the folio, then by uuid.
|
||||
/// Not by uuid first: a project generated again by a script gets new
|
||||
/// uuids each time, and a file with no conductor uuids gets new ones on
|
||||
/// every load, while the drawing is the same.
|
||||
bool stableConductorLess(const Conductor *a, const Conductor *b)
|
||||
{
|
||||
const auto ea = conductorEnds(a), eb = conductorEnds(b);
|
||||
const qreal ka[4] = {ea.first.x(), ea.first.y(), ea.second.x(), ea.second.y()};
|
||||
const qreal kb[4] = {eb.first.x(), eb.first.y(), eb.second.x(), eb.second.y()};
|
||||
for (int i = 0; i < 4; ++i)
|
||||
if (ka[i] != kb[i])
|
||||
return ka[i] < kb[i];
|
||||
return a->uuid() < b->uuid();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief longestConductorInPotential
|
||||
@param conductor : a conductor in the potential to search
|
||||
@@ -2313,10 +2385,24 @@ QPointF Conductor::movePointIntoPolygon(const QPointF &point, const QPainterPath
|
||||
*/
|
||||
Conductor * longestConductorInPotential(Conductor *conductor, bool all_diagram) {
|
||||
Conductor *longest_conductor = conductor;
|
||||
//Search the longest conductor
|
||||
qreal longest_length = conductor->length();
|
||||
//Search the longest conductor.
|
||||
//Conductors of equal length are common (a symmetrical layout), and the
|
||||
//set is iterated in pointer order, which changes from run to run: so a
|
||||
//tie is broken by an order that does not, or the potential's text lands
|
||||
//on a different conductor each time the same file is opened or exported.
|
||||
foreach (Conductor *c, conductor -> relatedPotentialConductors(all_diagram))
|
||||
if (c -> length() > longest_conductor -> length())
|
||||
{
|
||||
const qreal length = c->length();
|
||||
if (qAbs(length - longest_length) < 1e-6) {
|
||||
if (stableConductorLess(c, longest_conductor))
|
||||
longest_conductor = c;
|
||||
}
|
||||
else if (length > longest_length) {
|
||||
longest_conductor = c;
|
||||
longest_length = length;
|
||||
}
|
||||
}
|
||||
|
||||
return longest_conductor;
|
||||
}
|
||||
|
||||
@@ -116,6 +116,7 @@ class Conductor : public QGraphicsObject
|
||||
QVector <QPointF> handlerPoints() const;
|
||||
const QList<ConductorSegment *> segmentsList() const;
|
||||
bool moveSegment(int index, qreal dx, qreal dy);
|
||||
bool setPathPoints(const QList<QPointF> &scene_points);
|
||||
|
||||
void setPropertyToPotential(
|
||||
const ConductorProperties &property,
|
||||
|
||||
@@ -427,8 +427,7 @@ void QET::Icons::initIcons()
|
||||
ConfigureToolbars = QIcon::fromTheme("configure-toolbars");
|
||||
IC_CopyFile = QIcon::fromTheme("item-copy");
|
||||
DiagramAdd = QIcon::fromTheme("folio-new");
|
||||
Diagram.addFile(":/ico/16x16/diagram.png");
|
||||
Diagram.addFile(":/ico/diagram.png");
|
||||
Diagram = QIcon::fromTheme("diagram");
|
||||
DiagramBg = QIcon::fromTheme("diagram_bg");
|
||||
DiagramDelete = QIcon::fromTheme("folio-delete");
|
||||
DialogCancel = QIcon::fromTheme("dialog-cancel");
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
#include "../qetgraphicsitem/conductor.h"
|
||||
#include "../qetgraphicsitem/conductortextitem.h"
|
||||
#include "../conductorsegment.h"
|
||||
#include "../conductorrouter.h"
|
||||
#include "../qetgraphicsitem/diagramimageitem.h"
|
||||
|
||||
// See diagrameventaddpdf.h: a missing QtPdf module (or Qt < 6.4) is not
|
||||
@@ -88,6 +89,7 @@
|
||||
#include <QSqlError>
|
||||
#include <QSqlQuery>
|
||||
#include <QSqlRecord>
|
||||
#include <QDir>
|
||||
#include <QDomDocument>
|
||||
#include <QFileInfo>
|
||||
#include <QFont>
|
||||
@@ -679,6 +681,17 @@ QString QetScriptApi::addElement(int folioIndex, const QString &locationPath, do
|
||||
const QString import_path = location.isFileSystem()
|
||||
? QStringLiteral("import/") + location.collectionPath(false)
|
||||
: location.collectionPath(false);
|
||||
// An element file that exists but cannot be read gives a null
|
||||
// uuid(), which the collision check below would misreport as "a
|
||||
// different element" -- say what actually went wrong instead.
|
||||
if (location.isFileSystem()
|
||||
&& location.pugiXml().document_element().empty()) {
|
||||
const QString file = QDir::toNativeSeparators(
|
||||
QFileInfo(location.fileSystemPath()).absoluteFilePath());
|
||||
log(QStringLiteral("qet.addElement: could not read element '%1' (file '%2', "
|
||||
"%3 characters)").arg(locationPath, file).arg(file.size()));
|
||||
return QString();
|
||||
}
|
||||
const ElementsLocation existing(import_path, m_project);
|
||||
if (existing.exist() && existing.uuid() != location.uuid()) {
|
||||
log(QStringLiteral("qet.addElement: '%1' would collide with a different element "
|
||||
@@ -1196,6 +1209,119 @@ bool QetScriptApi::moveConductorSegment(int folioIndex, const QString &elementUu
|
||||
return conductor->moveSegment(segmentIndex, dx, dy);
|
||||
}
|
||||
|
||||
namespace {
|
||||
ConductorRouter::Direction routerDirection(Qet::Orientation o)
|
||||
{
|
||||
switch (o) {
|
||||
case Qet::North: return ConductorRouter::Direction::North;
|
||||
case Qet::East: return ConductorRouter::Direction::East;
|
||||
case Qet::South: return ConductorRouter::Direction::South;
|
||||
case Qet::West: return ConductorRouter::Direction::West;
|
||||
}
|
||||
return ConductorRouter::Direction::North;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
/**
|
||||
@brief QetScriptApi::applyRoute
|
||||
Redraw @p conductor around the symbols on its folio (ConductorRouter),
|
||||
as one undo step through Conductor::setPathPoints() -- the same
|
||||
ChangeConductorCommand a handle drag pushes, so the path is saved and
|
||||
survives a reload. Obstacles are every element's own rectangle, its
|
||||
texts left out; the other conductors are not obstacles but cost extra
|
||||
to run along or cross.
|
||||
@return "routed", or "no-route" with the reason logged when there is
|
||||
no such path -- the conductor then keeps the path it had. Not a
|
||||
failure: the wire exists and joins the right terminals either way.
|
||||
*/
|
||||
QString QetScriptApi::applyRoute(Conductor *conductor, const QString &caller)
|
||||
{
|
||||
Diagram *diagram = conductor->diagram();
|
||||
if (!diagram) return QString();
|
||||
|
||||
ConductorRouter::Request request;
|
||||
request.start = conductor->terminal1->dockConductor();
|
||||
request.start_direction = routerDirection(conductor->terminal1->orientation());
|
||||
request.end = conductor->terminal2->dockConductor();
|
||||
request.end_direction = routerDirection(conductor->terminal2->orientation());
|
||||
request.grid = Diagram::xGrid;
|
||||
request.bounds = diagram->border_and_titleblock.insideBorderRect();
|
||||
for (Element *e : diagram->elements())
|
||||
request.obstacles << e->mapRectToScene(e->boundingRect());
|
||||
for (Conductor *other : diagram->conductors()) {
|
||||
if (other == conductor) continue;
|
||||
QVector<QPointF> wire;
|
||||
const QList<ConductorSegment *> segs = other->segmentsList();
|
||||
for (ConductorSegment *seg : segs) {
|
||||
if (wire.isEmpty()) wire << other->mapToScene(seg->firstPoint());
|
||||
wire << other->mapToScene(seg->secondPoint());
|
||||
}
|
||||
request.wires << wire;
|
||||
}
|
||||
|
||||
const ConductorRouter::Result route = ConductorRouter::route(request);
|
||||
if (route.points.isEmpty()) {
|
||||
log(QStringLiteral("qet.%1: %2; the conductor keeps its path")
|
||||
.arg(caller, route.error));
|
||||
return QStringLiteral("no-route");
|
||||
}
|
||||
if (!conductor->setPathPoints(route.points)) {
|
||||
log(QStringLiteral("qet.%1: the route found does not join the two terminals; "
|
||||
"the conductor keeps its path").arg(caller));
|
||||
return QStringLiteral("no-route");
|
||||
}
|
||||
return QStringLiteral("routed");
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QetScriptApi::routeConductor
|
||||
Redraw the conductor on a terminal so it goes around the symbols in
|
||||
its way instead of through them -- see applyRoute(). Addressed as the
|
||||
other conductor calls address one.
|
||||
@return "routed", "no-route" (path unchanged, reason logged), or an
|
||||
empty string if there is no such conductor or the project is read-only
|
||||
*/
|
||||
QString QetScriptApi::routeConductor(int folioIndex, const QString &elementUuid,
|
||||
int terminalIndex)
|
||||
{
|
||||
if (!m_project) return QString();
|
||||
if (m_project->isReadOnly()) {
|
||||
log(QStringLiteral("qet.routeConductor: project is read-only"));
|
||||
return QString();
|
||||
}
|
||||
Conductor *conductor = findConductor(folioIndex, elementUuid, terminalIndex,
|
||||
QStringLiteral("routeConductor"));
|
||||
if (!conductor) return QString();
|
||||
return applyRoute(conductor, QStringLiteral("routeConductor"));
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QetScriptApi::routeConductorBetween
|
||||
routeConductor() for the conductor joining two given terminals, which
|
||||
names it even where either terminal carries other conductors too --
|
||||
the case just after addConductor() onto a terminal already wired.
|
||||
*/
|
||||
QString QetScriptApi::routeConductorBetween(int folioIndex,
|
||||
const QString &elementUuidA, int terminalIndexA,
|
||||
const QString &elementUuidB, int terminalIndexB)
|
||||
{
|
||||
if (!m_project) return QString();
|
||||
if (m_project->isReadOnly()) {
|
||||
log(QStringLiteral("qet.routeConductorBetween: project is read-only"));
|
||||
return QString();
|
||||
}
|
||||
const QString caller = QStringLiteral("routeConductorBetween");
|
||||
Terminal *a = findTerminal(folioIndex, elementUuidA, terminalIndexA, caller);
|
||||
Terminal *b = findTerminal(folioIndex, elementUuidB, terminalIndexB, caller);
|
||||
if (!a || !b) return QString();
|
||||
for (Conductor *c : a->conductors()) {
|
||||
if (c->terminal1 == b || c->terminal2 == b)
|
||||
return applyRoute(c, caller);
|
||||
}
|
||||
log(QStringLiteral("qet.%1: no conductor joins those two terminals").arg(caller));
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString QetScriptApi::elementLinkType(int folioIndex, const QString &elementUuid) const
|
||||
{
|
||||
Element *element = findElement(folioIndex, elementUuid);
|
||||
|
||||
@@ -429,6 +429,11 @@ class QetScriptApi : public QObject
|
||||
Q_INVOKABLE bool moveConductorSegment(int folioIndex, const QString &elementUuid,
|
||||
int terminalIndex, int segmentIndex,
|
||||
double dx, double dy);
|
||||
Q_INVOKABLE QString routeConductor(int folioIndex, const QString &elementUuid,
|
||||
int terminalIndex);
|
||||
Q_INVOKABLE QString routeConductorBetween(int folioIndex,
|
||||
const QString &elementUuidA, int terminalIndexA,
|
||||
const QString &elementUuidB, int terminalIndexB);
|
||||
|
||||
// -- cross-references: master/slave and report links --
|
||||
Q_INVOKABLE QString elementLinkType(int folioIndex, const QString &elementUuid) const;
|
||||
@@ -608,6 +613,7 @@ class QetScriptApi : public QObject
|
||||
Element *findElement(int folioIndex, const QString &elementUuid) const;
|
||||
Terminal *findTerminal(int folioIndex, const QString &elementUuid, int terminalIndex,
|
||||
const QString &caller);
|
||||
QString applyRoute(Conductor *conductor, const QString &caller);
|
||||
Conductor *findConductor(int folioIndex, const QString &elementUuid, int terminalIndex,
|
||||
const QString &caller);
|
||||
QList<IndependentTextItem *> sortedTexts(int folioIndex) const;
|
||||
|
||||
@@ -126,6 +126,12 @@ ConductorProperties ConductorPropertiesWidget::properties() const
|
||||
properties_.m_bicolor = ui->m_color_2_gb->isChecked();
|
||||
properties_.m_color_2 = ui->m_color_2_kpb->color();
|
||||
properties_.m_dash_size = ui->m_dash_size_sb->value();
|
||||
//The box starts at 2, so the default size 1 shows as 2: keep the
|
||||
//stored size unless the box shows something else.
|
||||
if (properties_.m_dash_size == qBound(ui->m_dash_size_sb->minimum(),
|
||||
m_properties.m_dash_size,
|
||||
ui->m_dash_size_sb->maximum()))
|
||||
properties_.m_dash_size = m_properties.m_dash_size;
|
||||
properties_.style = ui -> m_line_style_cb->itemData(ui->m_line_style_cb->currentIndex()).value<QPen>().style();
|
||||
properties_.m_formula = ui->m_formula_le->text();
|
||||
properties_.text = ui -> m_text_le -> text();
|
||||
|
||||
@@ -233,8 +233,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="configure" resource="../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/configure.png</normaloff>:/ico/16x16/configure.png</iconset>
|
||||
<iconset theme="configure"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -233,8 +233,10 @@ void NewDiagramPage::applyConf()
|
||||
pg.color = g.color;
|
||||
proj_guides.append(pg);
|
||||
}
|
||||
m_project->setDefaultGuides(proj_guides);
|
||||
modified_project = true;
|
||||
if (m_project->defaultGuides() != proj_guides) {
|
||||
m_project->setDefaultGuides(proj_guides);
|
||||
modified_project = true;
|
||||
}
|
||||
|
||||
if (modified_project) {
|
||||
m_project -> setModified(modified_project);
|
||||
|
||||
@@ -29,8 +29,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="folder-open" resource="../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/folder-open.png</normaloff>:/ico/16x16/folder-open.png</iconset>
|
||||
<iconset theme="folder-open"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -40,8 +39,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="document-save" resource="../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/document-save.png</normaloff>:/ico/16x16/document-save.png</iconset>
|
||||
<iconset theme="document-save"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "dialogwaiting.h"
|
||||
#include "ui_dialogwaiting.h"
|
||||
#include "../qet.h"
|
||||
#include <QIcon>
|
||||
#include <QPushButton>
|
||||
|
||||
|
||||
@@ -32,6 +33,8 @@ DialogWaiting::DialogWaiting(QWidget *parent) :
|
||||
ui(new Ui::DialogWaiting)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->label->setPixmap(QIcon::fromTheme("user-away-extended")
|
||||
.pixmap(ui->label->maximumSize()));
|
||||
QET::trackDialogGeometry(this);
|
||||
setTitle( "..." );
|
||||
setDetail( "..." );
|
||||
|
||||
@@ -62,9 +62,6 @@
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="../../qelectrotech.qrc">:/ico/48x48/user-away-extended.png</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
|
||||
@@ -25,8 +25,7 @@
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="document-save" resource="../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/document-save.png</normaloff>:/ico/16x16/document-save.png</iconset>
|
||||
<iconset theme="document-save"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -39,8 +38,7 @@
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="folder-open" resource="../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/folder-open.png</normaloff>:/ico/16x16/folder-open.png</iconset>
|
||||
<iconset theme="folder-open"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -79,8 +77,7 @@
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="object-group" resource="../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/object-group.png</normaloff>:/ico/16x16/object-group.png</iconset>
|
||||
<iconset theme="object-group"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -93,8 +90,7 @@
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="list-remove" resource="../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/list-remove.png</normaloff>:/ico/16x16/list-remove.png</iconset>
|
||||
<iconset theme="list-remove"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -136,8 +136,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="go-up" resource="../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/go-up.png</normaloff>:/ico/16x16/go-up.png</iconset>
|
||||
<iconset theme="go-up"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -150,8 +149,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="go-down" resource="../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/go-down.png</normaloff>:/ico/16x16/go-down.png</iconset>
|
||||
<iconset theme="go-down"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -33,8 +33,7 @@
|
||||
<string>Télechargement</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="edit-download" resource="../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/edit-download.png</normaloff>:/ico/16x16/edit-download.png</iconset>
|
||||
<iconset theme="edit-download"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -44,8 +43,7 @@
|
||||
<string>Dossier installation</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="folder-open" resource="../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/folder-open.png</normaloff>:/ico/16x16/folder-open.png</iconset>
|
||||
<iconset theme="folder-open"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -123,6 +123,8 @@ void TitleBlockPropertiesWidget::setProperties(
|
||||
ui -> m_folio_le -> setText (properties.folio);
|
||||
ui -> m_display_at_cb -> setCurrentIndex(properties.display_at == Qt::BottomEdge ? 0 : 1);
|
||||
ui->auto_page_cb->setCurrentText(properties.auto_page_num);
|
||||
m_auto_page_num = properties.auto_page_num;
|
||||
m_auto_page_num_picked = false;
|
||||
|
||||
//About date
|
||||
ui -> m_date_now_pb -> setDisabled(true);
|
||||
@@ -166,6 +168,7 @@ void TitleBlockPropertiesWidget::setProperties(
|
||||
// Show the saved custom values, plus any of the template's custom variables
|
||||
// that aren't defined yet, so the user only fills in the missing ones (#271).
|
||||
DiagramContext context = properties.context;
|
||||
m_context_keys = context.keys();
|
||||
addTemplateVariables(context, index);
|
||||
m_dcw -> setContext(context);
|
||||
}
|
||||
@@ -215,8 +218,22 @@ TitleBlockProperties TitleBlockPropertiesWidget::properties() const
|
||||
}
|
||||
|
||||
prop.context = m_dcw -> context();
|
||||
//The template's custom variables are only offered for filling in
|
||||
//(#271): one left empty is not part of the properties.
|
||||
const QList<QString> keys = prop.context.keys();
|
||||
for (const QString &key : keys) {
|
||||
if (!m_context_keys.contains(key)
|
||||
&& prop.context.value(key).toString().isEmpty())
|
||||
prop.context.remove(key);
|
||||
}
|
||||
|
||||
prop.auto_page_num = ui->auto_page_cb->currentText();
|
||||
//The combo box cannot show "no folio numbering": it shows its
|
||||
//placeholder entry, or the project's first numbering. Return what
|
||||
//was set unless the user picked something else.
|
||||
if (!m_auto_page_num_picked)
|
||||
prop.auto_page_num = m_auto_page_num;
|
||||
else if (ui->auto_page_cb->currentText() != tr("Créer un Folio Numérotation Auto"))
|
||||
prop.auto_page_num = ui->auto_page_cb->currentText();
|
||||
|
||||
return prop;
|
||||
}
|
||||
@@ -376,6 +393,9 @@ void TitleBlockPropertiesWidget::initDialog(
|
||||
foreach (QString str, keys_2) { ui -> auto_page_cb -> addItem(str); }
|
||||
if (ui->auto_page_cb->currentText()==nullptr)
|
||||
ui->auto_page_cb->addItem(tr("Créer un Folio Numérotation Auto"));
|
||||
//activated() is only emitted for a choice the user makes
|
||||
connect(ui->auto_page_cb, qOverload<int>(&QComboBox::activated),
|
||||
this, [this]() { m_auto_page_num_picked = true; });
|
||||
}
|
||||
else{
|
||||
ui->auto_page_cb->hide();
|
||||
|
||||
@@ -100,11 +100,17 @@ class TitleBlockPropertiesWidget : public QWidget
|
||||
private:
|
||||
Ui::TitleBlockPropertiesWidget *ui;
|
||||
DiagramContextWidget *m_dcw;
|
||||
/// keys of the context given to setProperties()
|
||||
QStringList m_context_keys;
|
||||
QAction *m_tbt_edit, *m_tbt_duplicate;
|
||||
QMenu *m_tbt_menu;
|
||||
QList <TitleBlockTemplatesCollection *> m_tbt_collection_list;
|
||||
QList <QET::QetCollection> m_map_index_to_collection_type;
|
||||
QList <QString> keys_2;
|
||||
/// auto_page_num as given to setProperties(), returned
|
||||
/// unchanged unless the user picks another folio numbering
|
||||
QString m_auto_page_num;
|
||||
bool m_auto_page_num_picked = false;
|
||||
};
|
||||
|
||||
#endif // TITLEBLOCKPROPERTIESWIDGET_H
|
||||
|
||||
@@ -119,8 +119,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="edit-rename" resource="../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/22x22/edit-rename.png</normaloff>:/ico/22x22/edit-rename.png</iconset>
|
||||
<iconset theme="edit-rename"/>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
@@ -208,8 +207,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="configure" resource="../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/16x16/configure.png</normaloff>:/ico/16x16/configure.png</iconset>
|
||||
<iconset theme="configure"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -303,8 +301,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="start" resource="../../qelectrotech.qrc">
|
||||
<normaloff>:/ico/22x22/start.png</normaloff>:/ico/22x22/start.png</iconset>
|
||||
<iconset theme="start"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -151,8 +151,15 @@ void XRefPropertiesWidget::saveProperties(int index) {
|
||||
xrp.setPrefix("switch", ui->m_switch_prefix_le->text());
|
||||
xrp.setMasterLabel(ui->m_master_le->text());
|
||||
xrp.setSlaveLabel(ui->m_slave_le->text());
|
||||
xrp.setOffset(ui->m_offset_sb->value());
|
||||
xrp.setSlaveOffset(ui->m_slave_offset_sb->value());
|
||||
//The boxes cannot show a value below their minimum (the offset's
|
||||
//minimum is its "Default" entry, standing for the stored 0): keep
|
||||
//the stored value unless the box shows something else.
|
||||
if (ui->m_offset_sb->value() != qBound(ui->m_offset_sb->minimum(), xrp.offset(),
|
||||
ui->m_offset_sb->maximum()))
|
||||
xrp.setOffset(ui->m_offset_sb->value());
|
||||
if (ui->m_slave_offset_sb->value() != qBound(ui->m_slave_offset_sb->minimum(), xrp.slaveOffset(),
|
||||
ui->m_slave_offset_sb->maximum()))
|
||||
xrp.setSlaveOffset(ui->m_slave_offset_sb->value());
|
||||
|
||||
m_properties.insert(type, xrp);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user