Files
qelectrotech-source-mirror/sources/cable/cablepropertieseditorwidget.cpp
T
Kellermorph 9771ca96c9 Make the cable feature speak English like the rest of the interface
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.
2026-10-10 13:48:45 +02:00

390 lines
12 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 "cablepropertieseditorwidget.h"
#include "cablecreatedialog.h"
#include "cablepart.h"
#include "cablepropertieswidget.h"
#include "editcablecommand.h"
#include "../qeticons.h"
#include "../qetproject.h"
#include <QAbstractItemView>
#include <QLabel>
#include <QListWidget>
#include <QMenu>
#include <QSizePolicy>
#include <QUndoStack>
#include <QVBoxLayout>
/**
@brief CablePropertiesEditorWidget::CablePropertiesEditorWidget
@param cable the cable being edited
@param part the line of that cable this panel was opened by, which
is the line a core picked here is put down on
@param parent parent widget
*/
CablePropertiesEditorWidget::CablePropertiesEditorWidget(Cable *cable,
CablePart *part,
QWidget *parent) :
PropertiesEditorWidget(parent),
m_fields(new CablePropertiesWidget(this)),
m_info(new QLabel(this)),
m_free_label(new QLabel(this)),
m_free_cores(new QListWidget(this))
{
m_info->setWordWrap(true);
//The cores which stand nowhere yet, listed under the count of
//wired ones: double-clicking one puts it down on this very line
//without any dialog having to be opened for it. The list takes
//no keyboard focus, so clicking it never takes the drawing away
//from the user -- startPlacingCore() hands the focus to the
//sheet itself, which is where Escape has to be heard.
m_free_label->setText(tr("Unplaced cores:"));
m_free_label->setVisible(false);
m_free_cores->setSelectionMode(QAbstractItemView::SingleSelection);
m_free_cores->setAlternatingRowColors(true);
m_free_cores->setFocusPolicy(Qt::NoFocus);
m_free_cores->setToolTip(tr("Double-click a core to place it on the "
"line: the line follows the mouse and a "
"click leaves it where you aimed."));
m_free_cores->setVisible(false);
m_free_cores->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_free_cores, &QWidget::customContextMenuRequested,
this, &CablePropertiesEditorWidget::freeCoreMenu);
connect(m_free_cores, &QListWidget::itemDoubleClicked,
this, &CablePropertiesEditorWidget::placeRequested);
connect(m_free_cores, &QListWidget::itemActivated,
this, &CablePropertiesEditorWidget::placeRequested);
auto *layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(4);
layout->addWidget(m_fields);
//The button beside the type field: it opens the window which
//opens when a cable line has just been drawn, in the mode where
//it changes the type of this very cable.
connect(m_fields, &CablePropertiesWidget::typeChangeRequested,
this, &CablePropertiesEditorWidget::changeTypeRequested);
layout->addWidget(m_info);
layout->addWidget(m_free_label);
layout->addWidget(m_free_cores);
layout->addStretch(1);
//Wide enough for the labels, the ticks, the fields, the buttons
//setting how a text is written and the two distances nudging
//the label and the row of colours -- they sit side by side.
setMinimumWidth(250);
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
setDisabled(true);
setCable(cable, part);
}
/**
@brief CablePropertiesEditorWidget::setCable
Set (or change) the cable whose properties are edited.
@param cable
@param part the line of that cable this panel was opened by: the one
a core picked in the list is put down on, and null while no single
line is selected -- then nothing can be put down and the list stays
out of the way
*/
void CablePropertiesEditorWidget::setCable(Cable *cable, CablePart *part)
{
if (!cable) return;
m_part = part;
if (m_cable && m_cable != cable)
disconnect(m_cable, nullptr, this, nullptr);
m_cable = cable;
//Keep the panel in sync when the cable is edited elsewhere (the
//dialog reached by double-clicking its line, for one): otherwise
//the panel would write back what it held before that edit.
connect(m_cable, &Cable::changed,
this, &CablePropertiesEditorWidget::updateUi, Qt::UniqueConnection);
setEnabled(true);
updateUi();
}
/**
@brief CablePropertiesEditorWidget::setLiveEdit
In live-edit mode (how the panel uses every editor) an edit is
applied as soon as a field is finished, rather than waiting for a
call to apply(). On top of that the text itself is written onto the
cable with every character, so what he types shows up on the drawing
while he is still typing it -- the whole editing of one field stays
one single undo step, which is worked out when the field is left.
@param live_edit true to enable live edit
@return always true
*/
bool CablePropertiesEditorWidget::setLiveEdit(bool live_edit)
{
if (m_live_edit == live_edit) return true;
m_live_edit = live_edit;
if (m_live_edit) {
m_live_connection = connect(m_fields, &CablePropertiesWidget::propertiesEdited,
this, &CablePropertiesEditorWidget::apply);
connect(m_fields, &CablePropertiesWidget::propertiesTyped,
this, &CablePropertiesEditorWidget::applyLive);
} else {
disconnect(m_live_connection);
disconnect(m_fields, &CablePropertiesWidget::propertiesTyped,
this, &CablePropertiesEditorWidget::applyLive);
}
return true;
}
/**
@brief CablePropertiesEditorWidget::applyLive
Put what is being typed straight onto the cable, without pushing an
undo step: the whole editing of one field is one step, which
apply() works out from what the panel showed when the field is left.
*/
void CablePropertiesEditorWidget::applyLive()
{
//Ignore the signals sent while the form is being filled in: at
//that moment it holds a half loaded state.
if (m_updating) return;
if (!m_cable) return;
m_live_cable = m_cable;
m_cable->setProperties(m_fields->properties());
}
/**
@brief CablePropertiesEditorWidget::changeTypeRequested
Open the window which opens when a cable line has just been drawn,
in the mode where it changes the type of this very cable: he picks
the new type there out of the cable type file, and the cable takes
it -- cores reworked, marks redrawn -- as one single undo step.
The form is filled in again afterwards rather than through the usual
path: the type has just changed under it, and it may not go on
offering the one which was left.
*/
void CablePropertiesEditorWidget::changeTypeRequested()
{
if (!m_cable) return;
CableCreateDialog dialog(m_part ? m_part->diagram() : nullptr,
m_cable,
this);
if (dialog.exec() != QDialog::Accepted) return;
m_updating = true;
m_fields->setProperties(m_cable->properties());
m_shown = m_cable->properties();
m_updating = false;
updateInfo();
}
/**
@brief CablePropertiesEditorWidget::apply
Put what was typed onto the cable as one undo step.
*/
void CablePropertiesEditorWidget::apply()
{
//Ignore the signals sent while the form is being filled in: at
//that moment it holds a half loaded state.
if (m_updating) return;
if (!m_cable) return;
if (QUndoCommand *undo = associatedUndo())
{
auto *project = qobject_cast<QETProject *>(m_cable->parent());
if (project && project->undoStack()) {
project->undoStack()->push(undo);
} else {
m_cable->setProperties(m_fields->properties());
delete undo;
}
}
m_shown = m_fields->properties();
}
/**
@brief CablePropertiesEditorWidget::associatedUndo
@return the edit as one undo step, or nullptr when nothing changed
*/
QUndoCommand *CablePropertiesEditorWidget::associatedUndo() const
{
if (!m_cable) return nullptr;
const CableProperties after = m_fields->properties();
if (after == m_shown) return nullptr;
return new ChangeCablePropertiesCommand(m_cable, m_shown, after);
}
/**
@brief CablePropertiesEditorWidget::reset
Discard the edit in progress, putting back what the panel showed.
*/
void CablePropertiesEditorWidget::reset()
{
if (!m_cable) return;
m_updating = true;
m_fields->setProperties(m_shown);
m_updating = false;
}
/**
@brief CablePropertiesEditorWidget::updateInfo
Say how many cores of this cable are wired to a conductor.
*/
void CablePropertiesEditorWidget::updateInfo()
{
if (!m_cable) return;
m_info->setText(tr("%1 of %2 cores wired to a conductor",
"how many cores of this cable are wired",
m_cable->usedCoreCount())
.arg(m_cable->usedCoreCount())
.arg(m_cable->coreCount()));
updateFreeCores();
}
/**
@brief CablePropertiesEditorWidget::updateFreeCores
List the cores the type of this cable offers which stand nowhere on
the sheet yet: those are the ones he can still put down, and they
come down straight from here, on the line this panel was opened by.
The list is of no use when there is none -- and it cannot work at
all when no single line of the cable is the selected one, since a
core then has no line to be put down on -- so it takes no room then.
*/
void CablePropertiesEditorWidget::updateFreeCores()
{
if (!m_free_cores) return;
const bool usable = m_cable && m_part && m_part->cable() == m_cable;
m_free_cores->clear();
if (usable)
{
for (int i = 0; i < m_cable->coreCount(); ++i)
{
if (m_cable->hasCore(i)) continue;
const QString color = m_cable->colorOfCore(i);
auto *item = new QListWidgetItem(
color.isEmpty() ? tr("Core %1").arg(i + 1)
: tr("Core %1 — %2").arg(i + 1).arg(color),
m_free_cores);
item->setData(Qt::UserRole, i);
}
}
const bool any = usable && m_free_cores->count() > 0;
m_free_label->setVisible(any);
m_free_cores->setVisible(any);
if (!any) return;
//As high as the entries it really shows, four of them at most:
//the dock is small, and a handful of free cores must not turn
//it into a wall.
const int row = qMax(m_free_cores->sizeHintForRow(0), 18);
m_free_cores->setFixedHeight(row * qMin(4, m_free_cores->count())
+ 2 * m_free_cores->frameWidth() + 4);
}
/**
@brief CablePropertiesEditorWidget::placeRequested
He double-clicked one of the cores which stand nowhere: this very
line takes the mouse and waits for the click which puts the core
down. There is no dialog to close here -- the panel stays where it
is, and it reads the cable back as soon as the core lands.
@param item the entry he double-clicked
*/
void CablePropertiesEditorWidget::placeRequested(QListWidgetItem *item)
{
if (!item || !m_part) return;
m_part->startPlacingCore(item->data(Qt::UserRole).toInt());
}
/**
@brief CablePropertiesEditorWidget::freeCoreMenu
The small menu a right click on a core which stands nowhere opens:
one entry, doing what a double click does -- the line takes the
mouse, the mark shows on it at once and the mouse is put down there,
so the very next click leaves the core standing on that line.
@param pos where he right-clicked, in list coordinates
*/
void CablePropertiesEditorWidget::freeCoreMenu(const QPoint &pos)
{
QListWidgetItem *item = m_free_cores ? m_free_cores->itemAt(pos) : nullptr;
if (!item || !m_part) return;
m_free_cores->setCurrentItem(item);
QMenu menu(m_free_cores);
QAction *place = menu.addAction(tr("Place this core on the line"));
place->setIcon(QET::Icons::Cable);
if (menu.exec(m_free_cores->viewport()->mapToGlobal(pos)) == place) {
//Gone before he is sent off: the mouse goes to the line
//and nothing may be left standing over the place it is
//sent to.
menu.hide();
placeRequested(item);
}
}
/**
@brief CablePropertiesEditorWidget::updateUi
Read the cable back into the panel.
Not while a field is being typed in: writing what was just typed
back into that field would take the cursor away from the user with
every key, and it would take back the state the whole editing of
that field is measured against when the undo step is worked out.
*/
void CablePropertiesEditorWidget::updateUi()
{
if (!m_cable) return;
if (m_fields->editing() && m_live_cable == m_cable) {
updateInfo();
return;
}
m_updating = true;
m_fields->setProperties(m_cable->properties());
m_shown = m_cable->properties();
updateInfo();
m_updating = false;
}
/**
@brief CablePropertiesEditorWidget::title
@return the panel title
*/
QString CablePropertiesEditorWidget::title() const
{
return tr("Cable");
}