mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-10 22:24:13 +02:00
9771ca96c9
English has been the source language of the program for a while now
(upstream: "Make English the source language ..."), the cable feature
came in with French ones: about 200 translatable strings over 30 files,
the two settings pages built from .ui files, four comments and one test
which asserted the old wording (blocking review comment by ispyisail).
The wording follows what the code base already uses where it exists:
Plant (=) and Location (+) as qetinformation.cpp names those element
information, Designation for the field the cable form calls its BMK,
Length, Sheet for folio, Automatic numbering, Cross-reference, and Right
aligned / Centred / Left aligned the way cabletextformatdialog.cpp
already spells the three alignment entries.
Two things are more than a word-for-word swap:
- xrefpropertieswidget.cpp:291 wrote the label of the master field on
every type change with a hardcoded tr("Maitre"), so the English
"Master" coming from the .ui was overwritten for every type, cable or
not: a real bug next to being French. The wording now comes back from
the .ui and only a cable renames the field to "Text:", so the two
cannot drift apart again.
- tst_cabletypelist kept asserting that a new catalogue is *labelled*
in French. That label line is the translated header and reads English
now. The test still reads files with French headers on purpose:
headerAliasMap() keeps the five old French labels as aliases next to
the English ones, so catalogues written by earlier builds, including
the one the reviewer reads her own types from, keep loading.
The .ts files are deliberately not regenerated here. Upstream
regenerates them on master, as it did for the language change itself,
and a translation is keyed on the source string: while the sources were
French a German user saw French in the cable dialogs, and an English
source string with no translation yet shows as it is.
Build clean, ctest 92/93, the one failure being the pre-existing
headless tst_menubarkeyboard.
495 lines
13 KiB
C++
495 lines
13 KiB
C++
/*
|
|
Copyright 2006-2026 The QElectroTech Team
|
|
This file is part of QElectroTech.
|
|
|
|
QElectroTech is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
QElectroTech is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "editcablecommand.h"
|
|
|
|
#include "cablemanager.h"
|
|
#include "cablepart.h"
|
|
#include "../diagram.h"
|
|
#include "../qetproject.h"
|
|
|
|
#include <QCoreApplication>
|
|
|
|
#include <utility>
|
|
|
|
/**
|
|
@brief BatchCommand::BatchCommand
|
|
@param first step to run first, kept by this command
|
|
@param second step to run right after it, kept by this command
|
|
*/
|
|
BatchCommand::BatchCommand(QUndoCommand *first, QUndoCommand *second) :
|
|
m_steps({first, second})
|
|
{
|
|
m_steps.removeAll(nullptr);
|
|
setText(QCoreApplication::translate("BatchCommand", "Delete"));
|
|
}
|
|
|
|
/**
|
|
@brief BatchCommand::BatchCommand
|
|
@param steps every step of one single gesture, in the order the
|
|
user did them, kept by this command
|
|
@param text what the gesture was, as the undo history shows it
|
|
*/
|
|
BatchCommand::BatchCommand(const QList<QUndoCommand *> &steps, const QString &text) :
|
|
m_steps(steps)
|
|
{
|
|
m_steps.removeAll(nullptr);
|
|
setText(text);
|
|
}
|
|
|
|
/**
|
|
@brief BatchCommand::~BatchCommand
|
|
The steps it holds go with it: they were never given to anything else.
|
|
*/
|
|
BatchCommand::~BatchCommand()
|
|
{
|
|
for (QUndoCommand *step : std::as_const(m_steps)) {
|
|
delete step;
|
|
}
|
|
}
|
|
|
|
/**
|
|
@brief BatchCommand::redo
|
|
Every step, in the order the user did them.
|
|
*/
|
|
void BatchCommand::redo()
|
|
{
|
|
for (QUndoCommand *step : std::as_const(m_steps)) {
|
|
if (step) step->redo();
|
|
}
|
|
}
|
|
|
|
/**
|
|
@brief BatchCommand::undo
|
|
Every step taken back in the reverse order, so what one step changed
|
|
is still there when the next one is undone.
|
|
*/
|
|
void BatchCommand::undo()
|
|
{
|
|
for (auto it = m_steps.crbegin(); it != m_steps.crend(); ++it) {
|
|
if (*it) (*it)->undo();
|
|
}
|
|
}
|
|
|
|
/**
|
|
@brief MoveCablePartCommand::MoveCablePartCommand
|
|
@param cable the cable the section belongs to
|
|
@param part the line being dragged
|
|
@param before_p1
|
|
@param before_p2
|
|
@param after_p1
|
|
@param after_p2
|
|
@param before_cores where every colour label stood before the drag
|
|
@param after_cores where it stands after it
|
|
*/
|
|
MoveCablePartCommand::MoveCablePartCommand(Cable *cable,
|
|
CablePart *part,
|
|
const QPointF &before_p1,
|
|
const QPointF &before_p2,
|
|
const QPointF &after_p1,
|
|
const QPointF &after_p2,
|
|
const QList<CableCore> &before_cores,
|
|
const QList<CableCore> &after_cores) :
|
|
m_cable(cable),
|
|
m_part(part),
|
|
m_before_p1(before_p1),
|
|
m_before_p2(before_p2),
|
|
m_after_p1(after_p1),
|
|
m_after_p2(after_p2),
|
|
m_before_cores(before_cores),
|
|
m_after_cores(after_cores)
|
|
{
|
|
setText(QCoreApplication::translate("MoveCablePartCommand", "Move a cable"));
|
|
}
|
|
|
|
void MoveCablePartCommand::undo()
|
|
{
|
|
apply(m_before_p1, m_before_p2, m_before_cores);
|
|
}
|
|
|
|
void MoveCablePartCommand::redo()
|
|
{
|
|
apply(m_after_p1, m_after_p2, m_after_cores);
|
|
}
|
|
|
|
/**
|
|
@brief MoveCablePartCommand::apply
|
|
Put the line where it belongs and the colour labels with it. The
|
|
positions are written last: they are stored in scene coordinates, so
|
|
they only make sense once the line itself is back in place.
|
|
@param p1
|
|
@param p2
|
|
@param cores
|
|
*/
|
|
void MoveCablePartCommand::apply(const QPointF &p1,
|
|
const QPointF &p2,
|
|
const QList<CableCore> &cores)
|
|
{
|
|
if (m_part) {
|
|
m_part->setLine(p1, p2);
|
|
}
|
|
if (m_cable) {
|
|
m_cable->setCores(cores);
|
|
}
|
|
}
|
|
|
|
/**
|
|
@brief ChangeCablePropertiesCommand::ChangeCablePropertiesCommand
|
|
@param cable
|
|
@param before every field as it was before the edit
|
|
@param after every field as the user left it
|
|
*/
|
|
ChangeCablePropertiesCommand::ChangeCablePropertiesCommand(Cable *cable,
|
|
const CableProperties &before,
|
|
const CableProperties &after) :
|
|
m_cable(cable),
|
|
m_before(before),
|
|
m_after(after)
|
|
{
|
|
setText(QCoreApplication::translate("ChangeCablePropertiesCommand",
|
|
"Edit the properties of a cable"));
|
|
}
|
|
|
|
void ChangeCablePropertiesCommand::undo()
|
|
{
|
|
if (m_cable) {
|
|
m_cable->setProperties(m_before);
|
|
}
|
|
}
|
|
|
|
void ChangeCablePropertiesCommand::redo()
|
|
{
|
|
if (m_cable) {
|
|
m_cable->setProperties(m_after);
|
|
}
|
|
}
|
|
|
|
/**
|
|
@brief RenumberCablesCommand::RenumberCablesCommand
|
|
@param cables the cables to number, in the order they are numbered in
|
|
@param designations the number each of them gets
|
|
@param project the project whose numbering rule moves along with
|
|
the numbering
|
|
@param num_key the name that rule is kept under in the project
|
|
@param num_before where its counters stood before the numbering ran
|
|
@param num_after where the numbering leaves them
|
|
*/
|
|
RenumberCablesCommand::RenumberCablesCommand(const QList<Cable *> &cables,
|
|
const QStringList &designations,
|
|
QETProject *project,
|
|
const QString &num_key,
|
|
const NumerotationContext &num_before,
|
|
const NumerotationContext &num_after) :
|
|
m_project(project),
|
|
m_num_key(num_key),
|
|
m_num_before(num_before),
|
|
m_num_after(num_after)
|
|
{
|
|
for (int i = 0; i < cables.size() && i < designations.size(); ++i)
|
|
{
|
|
Cable *cable = cables.at(i);
|
|
if (!cable) continue;
|
|
|
|
m_cables.append(cable);
|
|
m_before.append(cable->designation());
|
|
m_by_hand_before.append(cable->designationByHand());
|
|
m_after.append(designations.at(i));
|
|
//A number the rule has just handed out is the rule's, so
|
|
//nothing here stays marked as having been typed in
|
|
m_by_hand_after.append(false);
|
|
}
|
|
|
|
setText(QCoreApplication::translate("RenumberCablesCommand",
|
|
"Renumber %n cable(s)",
|
|
"", m_cables.size()));
|
|
}
|
|
|
|
void RenumberCablesCommand::undo()
|
|
{
|
|
apply(false);
|
|
}
|
|
|
|
void RenumberCablesCommand::redo()
|
|
{
|
|
apply(true);
|
|
}
|
|
|
|
/**
|
|
@brief RenumberCablesCommand::apply
|
|
@param use_after true to write the numbers the rule handed out,
|
|
false to put the ones back which were there before
|
|
*/
|
|
void RenumberCablesCommand::apply(bool use_after)
|
|
{
|
|
for (int i = 0; i < m_cables.size(); ++i)
|
|
{
|
|
Cable *cable = m_cables.at(i).data();
|
|
if (!cable) continue;
|
|
|
|
//Where the name comes from is written first: a number the
|
|
//rule has just handed out is the rule's whatever the cable
|
|
//was called before, and undo has to say so again
|
|
cable->setDesignationByHand(use_after ? m_by_hand_after.at(i)
|
|
: m_by_hand_before.at(i));
|
|
cable->setDesignation(use_after ? m_after.at(i) : m_before.at(i));
|
|
}
|
|
|
|
//Where the numbering rule itself stands moves along with the
|
|
//numbering: once it has run, its counters are where the last
|
|
//number was taken from, so the next cable drawn goes on from
|
|
//there rather than skipping over every number in between. Undo
|
|
//puts them back like the names, since numbering a project over
|
|
//is one single act.
|
|
if (m_project && !m_num_key.isEmpty())
|
|
{
|
|
const NumerotationContext context = use_after ? m_num_after
|
|
: m_num_before;
|
|
//An empty rule is never written: losing the rule altogether
|
|
//is not something numbering may do, not even on the way back
|
|
if (!context.isEmpty())
|
|
{
|
|
m_project->addCableAutoNum(m_num_key, context);
|
|
m_project->setCurrentCableAutoNum(m_num_key);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
@brief ChangeCableTypeCommand::ChangeCableTypeCommand
|
|
@param cable
|
|
@param before the type, the count, the colours and the cores as
|
|
they were before the change
|
|
@param after the very same four as the chosen type says
|
|
*/
|
|
ChangeCableTypeCommand::ChangeCableTypeCommand(Cable *cable,
|
|
const CableTypeState &before,
|
|
const CableTypeState &after) :
|
|
m_cable(cable),
|
|
m_before(before),
|
|
m_after(after)
|
|
{
|
|
setText(QCoreApplication::translate("ChangeCableTypeCommand",
|
|
"Change the cable type"));
|
|
}
|
|
|
|
void ChangeCableTypeCommand::undo()
|
|
{
|
|
apply(m_before);
|
|
}
|
|
|
|
void ChangeCableTypeCommand::redo()
|
|
{
|
|
apply(m_after);
|
|
}
|
|
|
|
/**
|
|
@brief ChangeCableTypeCommand::apply
|
|
One block in one pass: name, colours, count, then the cores. The
|
|
count has to stand before the cores, since a cable with fewer cores
|
|
cannot hold the others -- and the other way round, undoing a type
|
|
with fewer cores has to open the room before the cores come back
|
|
into it.
|
|
@param state the block to write
|
|
*/
|
|
void ChangeCableTypeCommand::apply(const CableTypeState &state)
|
|
{
|
|
if (m_cable) {
|
|
m_cable->applyTypeState(state);
|
|
}
|
|
}
|
|
|
|
/**
|
|
@brief ChangeCableCoresCommand::ChangeCableCoresCommand
|
|
@param cable
|
|
@param before every core the cable wired before the change
|
|
@param after every core it wires after it
|
|
@param text what undo says about this step, empty for a core which
|
|
is merely moved
|
|
*/
|
|
ChangeCableCoresCommand::ChangeCableCoresCommand(Cable *cable,
|
|
const QList<CableCore> &before,
|
|
const QList<CableCore> &after,
|
|
const QString &text) :
|
|
m_cable(cable),
|
|
m_before(before),
|
|
m_after(after)
|
|
{
|
|
setText(text.isEmpty()
|
|
? QCoreApplication::translate("ChangeCableCoresCommand",
|
|
"Move a cable core")
|
|
: text);
|
|
}
|
|
|
|
void ChangeCableCoresCommand::undo()
|
|
{
|
|
apply(m_before);
|
|
}
|
|
|
|
void ChangeCableCoresCommand::redo()
|
|
{
|
|
apply(m_after);
|
|
}
|
|
|
|
/**
|
|
@brief ChangeCableCoresCommand::apply
|
|
Write one whole set of assignments in place of another. The cable
|
|
says it changed, which is what makes the project work out the cable
|
|
field of every conductor it feeds again, so the drawing follows.
|
|
@param cores
|
|
*/
|
|
void ChangeCableCoresCommand::apply(const QList<CableCore> &cores)
|
|
{
|
|
if (m_cable) {
|
|
m_cable->setCores(cores);
|
|
}
|
|
}
|
|
|
|
/**
|
|
@brief RemoveCableCommand::RemoveCableCommand
|
|
@param diagram the folios the lines are drawn on
|
|
@param parts the lines the user asked to delete
|
|
*/
|
|
RemoveCableCommand::RemoveCableCommand(Diagram *diagram, const QList<CablePart *> &parts) :
|
|
m_diagram(diagram)
|
|
{
|
|
setText(QCoreApplication::translate("RemoveCableCommand", "Delete a cable"));
|
|
|
|
for (CablePart *part : parts)
|
|
{
|
|
if (!part) continue;
|
|
|
|
Entry entry;
|
|
entry.part = part;
|
|
entry.cable = part->cable();
|
|
entry.data.uuid = part->partUuid();
|
|
entry.data.diagram = m_diagram ? m_diagram->uuid() : QUuid();
|
|
entry.data.p1 = part->firstPoint();
|
|
entry.data.p2 = part->secondPoint();
|
|
m_entries.append(entry);
|
|
|
|
if (entry.cable && !m_cables.contains(entry.cable)) {
|
|
m_cables.append(entry.cable);
|
|
m_cores.insert(entry.cable.data(), entry.cable->usedCores());
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
@brief RemoveCableCommand::~RemoveCableCommand
|
|
Whatever the stack leaves behind has to be cleaned up here: a cable
|
|
which is not held by the project any more, and a line which never
|
|
made it back onto a folio, belong to this command.
|
|
*/
|
|
RemoveCableCommand::~RemoveCableCommand()
|
|
{
|
|
QETProject *proj = project();
|
|
for (const QPointer<Cable> &cable : std::as_const(m_cables))
|
|
{
|
|
if (cable && (!proj || !proj->cables().contains(cable))) {
|
|
delete cable;
|
|
}
|
|
}
|
|
for (const Entry &entry : std::as_const(m_entries))
|
|
{
|
|
if (entry.part && !entry.part->scene()) {
|
|
delete entry.part;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
@brief RemoveCableCommand::project
|
|
@return the project this command works in, or nullptr
|
|
*/
|
|
QETProject *RemoveCableCommand::project() const
|
|
{
|
|
return m_diagram ? m_diagram->project() : nullptr;
|
|
}
|
|
|
|
/**
|
|
@brief RemoveCableCommand::redo
|
|
Take the lines off their folios. Each cable loses the sections which
|
|
are gone together with the cores drawn on them, and a cable which has
|
|
no section left anywhere leaves the project: a cable nothing draws is
|
|
no cable of this project.
|
|
*/
|
|
void RemoveCableCommand::redo()
|
|
{
|
|
QETProject *proj = project();
|
|
|
|
for (const Entry &entry : std::as_const(m_entries))
|
|
{
|
|
if (entry.cable) {
|
|
entry.cable->removePart(entry.data.uuid);
|
|
}
|
|
if (entry.part && entry.part->scene() && m_diagram) {
|
|
m_diagram->removeItem(entry.part);
|
|
}
|
|
}
|
|
|
|
if (proj)
|
|
{
|
|
for (const QPointer<Cable> &cable : std::as_const(m_cables))
|
|
{
|
|
if (cable && cable->parts().isEmpty() && proj->cables().contains(cable)) {
|
|
proj->removeCable(cable);
|
|
}
|
|
}
|
|
}
|
|
|
|
CableManager::refreshLabels(proj);
|
|
}
|
|
|
|
/**
|
|
@brief RemoveCableCommand::undo
|
|
Put the lines back on their folios, the sections back in their cable
|
|
and the cores back on those sections: exactly the state the delete
|
|
action took away.
|
|
*/
|
|
void RemoveCableCommand::undo()
|
|
{
|
|
QETProject *proj = project();
|
|
|
|
if (proj)
|
|
{
|
|
for (const QPointer<Cable> &cable : std::as_const(m_cables))
|
|
{
|
|
if (cable && !proj->cables().contains(cable)) {
|
|
proj->addCable(cable);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (const Entry &entry : std::as_const(m_entries))
|
|
{
|
|
if (entry.cable)
|
|
{
|
|
if (!entry.cable->hasPart(entry.data.uuid)) {
|
|
entry.cable->addPart(entry.data);
|
|
}
|
|
for (const CableCore &core : m_cores.value(entry.cable.data())) {
|
|
entry.cable->setCore(core);
|
|
}
|
|
}
|
|
if (entry.part && !entry.part->scene() && m_diagram) {
|
|
m_diagram->addItem(entry.part);
|
|
}
|
|
}
|
|
|
|
CableManager::refreshLabels(proj);
|
|
}
|