mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-11 06:34:14 +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.
390 lines
12 KiB
C++
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");
|
|
}
|