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:
ispyisail
2026-10-03 07:44:54 +13:00
460 changed files with 116374 additions and 59539 deletions
@@ -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");
}
+2 -4
View File
@@ -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>
+5 -10
View File
@@ -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>
+8 -1
View File
@@ -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(
+342
View File
@@ -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;
}
+77
View File
@@ -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
+70 -6
View File
@@ -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);
+1 -1
View File
@@ -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);
+6 -12
View File
@@ -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>
+4 -8
View File
@@ -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>
+1
View File
@@ -42,6 +42,7 @@ void DiagramContext::add(DiagramContext other)
*/
void DiagramContext::remove(const QString &key) {
m_content.remove(key);
m_content_show.remove(key);
}
/**
+7 -1
View File
@@ -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_;
+11 -26
View File
@@ -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));
+60
View File
@@ -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
+2 -4
View File
@@ -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>
+3
View File
@@ -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();
}
+31 -62
View File
@@ -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>&amp;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>&amp;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>&amp;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>&amp;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>&amp;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&amp;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&amp;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&amp;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&amp;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>&amp;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>À &amp;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 &amp;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>
+2 -2
View File
@@ -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();
+10 -20
View File
@@ -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>
+88 -2
View File
@@ -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;
}
+1
View File
@@ -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,
+1 -2
View File
@@ -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");
+126
View File
@@ -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);
+6
View File
@@ -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;
+6
View File
@@ -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();
+1 -2
View File
@@ -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>
+4 -2
View File
@@ -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);
+2 -4
View File
@@ -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>
+3
View File
@@ -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( "..." );
-3
View File
@@ -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>
+4 -8
View File
@@ -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>
+2 -4
View File
@@ -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>
+2 -4
View File
@@ -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>
+21 -1
View File
@@ -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();
+6
View File
@@ -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
+3 -6
View File
@@ -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>
+9 -2
View File
@@ -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);
}