diff --git a/CMakeLists.txt b/CMakeLists.txt
index bd685a45d..99973095d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -345,7 +345,6 @@ target_include_directories(
${QET_DIR}/sources/print
${QET_DIR}/sources/svg
${QET_DIR}/sources/materiallist
- ${QET_DIR}/sources/cablelist
)
if(NOT BUILD_WITH_KF)
diff --git a/cmake/qet_compilation_vars.cmake b/cmake/qet_compilation_vars.cmake
index 61f2f9286..94d81db28 100644
--- a/cmake/qet_compilation_vars.cmake
+++ b/cmake/qet_compilation_vars.cmake
@@ -387,8 +387,6 @@ set(QET_SRC_FILES
${QET_DIR}/sources/diagramevent/diagrameventfillet.h
${QET_DIR}/sources/diagramevent/diagrameventaddpath.cpp
${QET_DIR}/sources/diagramevent/diagrameventaddpath.h
- ${QET_DIR}/sources/diagramevent/diagrameventaddcable.cpp
- ${QET_DIR}/sources/diagramevent/diagrameventaddcable.h
${QET_DIR}/sources/diagramevent/diagrameventaddtext.cpp
${QET_DIR}/sources/diagramevent/diagrameventaddtext.h
${QET_DIR}/sources/diagramevent/diagrameventinterface.cpp
@@ -813,8 +811,6 @@ set(QET_SRC_FILES
${QET_DIR}/sources/ui/elementinfowidget.h
${QET_DIR}/sources/ui/terminalnumberingdialog.cpp
${QET_DIR}/sources/ui/terminalnumberingdialog.h
- ${QET_DIR}/sources/ui/cablenumberingdialog.cpp
- ${QET_DIR}/sources/ui/cablenumberingdialog.h
${QET_DIR}/sources/ui/elementpropertieswidget.cpp
${QET_DIR}/sources/ui/elementpropertieswidget.h
${QET_DIR}/sources/ui/formulaassistantdialog.cpp
@@ -940,44 +936,6 @@ set(QET_SRC_FILES
${QET_DIR}/sources/materiallist/materialselectiondialog.cpp
${QET_DIR}/sources/materiallist/materialselectiondialog.h
- ${QET_DIR}/sources/cablelist/cabletypelist.cpp
- ${QET_DIR}/sources/cablelist/cabletypelist.h
- ${QET_DIR}/sources/cablelist/cableexportdialog.cpp
- ${QET_DIR}/sources/cablelist/cableexportdialog.h
- ${QET_DIR}/sources/cablelist/cablelistmodel.cpp
- ${QET_DIR}/sources/cablelist/cablelistmodel.h
- ${QET_DIR}/sources/cablelist/cablelistmodelpropertieswidget.cpp
- ${QET_DIR}/sources/cablelist/cablelistmodelpropertieswidget.h
- ${QET_DIR}/sources/cablelist/cablelistrows.cpp
- ${QET_DIR}/sources/cablelist/cablelistrows.h
- ${QET_DIR}/sources/cablelist/cablequerywidget.cpp
- ${QET_DIR}/sources/cablelist/cablequerywidget.h
-
- ${QET_DIR}/sources/cable/addcablecommand.cpp
- ${QET_DIR}/sources/cable/addcablecommand.h
- ${QET_DIR}/sources/cable/cable.cpp
- ${QET_DIR}/sources/cable/cable.h
- ${QET_DIR}/sources/cable/cablecopy.cpp
- ${QET_DIR}/sources/cable/cablecopy.h
- ${QET_DIR}/sources/cable/cablecreatedialog.cpp
- ${QET_DIR}/sources/cable/cablecreatedialog.h
- ${QET_DIR}/sources/cable/cablemanager.cpp
- ${QET_DIR}/sources/cable/cablemanager.h
- ${QET_DIR}/sources/cable/cablepart.cpp
- ${QET_DIR}/sources/cable/cablepart.h
- ${QET_DIR}/sources/cable/cablepropertiesdialog.cpp
- ${QET_DIR}/sources/cable/cablepropertiesdialog.h
- ${QET_DIR}/sources/cable/cablepropertieseditorwidget.cpp
- ${QET_DIR}/sources/cable/cablepropertieseditorwidget.h
- ${QET_DIR}/sources/cable/cablepropertieswidget.cpp
- ${QET_DIR}/sources/cable/cablepropertieswidget.h
- ${QET_DIR}/sources/cable/cablerenumber.cpp
- ${QET_DIR}/sources/cable/cablerenumber.h
- ${QET_DIR}/sources/cable/cabletextformatdialog.cpp
- ${QET_DIR}/sources/cable/cabletextformatdialog.h
- ${QET_DIR}/sources/cable/editcablecommand.cpp
- ${QET_DIR}/sources/cable/editcablecommand.h
-
${QET_DIR}/sources/xml/terminalstripitemxml.cpp
${QET_DIR}/sources/xml/terminalstripitemxml.h
${QET_DIR}/sources/xml/terminalstriplayoutpatternxml.cpp
diff --git a/ico/icon-themes.qrc b/ico/icon-themes.qrc
index 47e98b7c6..30d2b9de1 100644
--- a/ico/icon-themes.qrc
+++ b/ico/icon-themes.qrc
@@ -257,21 +257,6 @@
themes/generated/traced-24/bring_forward.svgthemes/generated/traced-large-24/bring_forward.svgqet/traced/large22/bring_forward.svg
- qet/traced/16/cable.svg
- qet/traced/large16/cable.svg
- qet/traced/22/cable.svg
- qet/traced/large22/cable.svg
- qet/traced/large16/cable.svg
- qet/traced/large16/cable.svg
- themes/generated/traced-large-24/cable.svg
- themes/generated/traced-large-24/cable.svg
- qet/traced/large16/cable.svg
- qet/traced/large16/cable.svg
- qet/traced/large16/cable.svg
- qet/traced/large16/cable.svg
- themes/generated/traced-24/cable.svg
- themes/generated/traced-large-24/cable.svg
- qet/traced/large22/cable.svgqet/traced/16/circle.svgqet/traced/large16/circle.svgqet/traced/22/circle.svg
@@ -2714,12 +2699,6 @@
themes/qet-dark/traced/large22/bring_forward.svgthemes/qet-dark/traced-large-24/bring_forward.svgthemes/qet-dark/traced-24/bring_forward.svg
- themes/qet-dark/traced/16/cable.svg
- themes/qet-dark/traced/large16/cable.svg
- themes/qet-dark/traced/22/cable.svg
- themes/qet-dark/traced/large22/cable.svg
- themes/qet-dark/traced-large-24/cable.svg
- themes/qet-dark/traced-24/cable.svgthemes/qet-dark/traced/16/circle.svgthemes/qet-dark/traced/large16/circle.svgthemes/qet-dark/traced/22/circle.svg
@@ -3537,21 +3516,6 @@
themes/qet-dark/traced-24/bring_forward.svgthemes/qet-dark/traced-large-24/bring_forward.svgthemes/qet-dark/traced/large22/bring_forward.svg
- themes/qet-dark/traced/16/cable.svg
- themes/qet-dark/traced/large16/cable.svg
- themes/qet-dark/traced/22/cable.svg
- themes/qet-dark/traced/large22/cable.svg
- themes/qet-dark/traced/large16/cable.svg
- themes/qet-dark/traced/large16/cable.svg
- themes/qet-dark/traced-large-24/cable.svg
- themes/qet-dark/traced-large-24/cable.svg
- themes/qet-dark/traced/large16/cable.svg
- themes/qet-dark/traced/large16/cable.svg
- themes/qet-dark/traced/large16/cable.svg
- themes/qet-dark/traced/large16/cable.svg
- themes/qet-dark/traced-24/cable.svg
- themes/qet-dark/traced-large-24/cable.svg
- themes/qet-dark/traced/large22/cable.svgthemes/qet-dark/traced/16/circle.svgthemes/qet-dark/traced/large16/circle.svgthemes/qet-dark/traced/22/circle.svg
diff --git a/ico/qet/traced/16/cable.svg b/ico/qet/traced/16/cable.svg
deleted file mode 100644
index 7c619123c..000000000
--- a/ico/qet/traced/16/cable.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
diff --git a/ico/qet/traced/22/cable.svg b/ico/qet/traced/22/cable.svg
deleted file mode 100644
index e0a389dea..000000000
--- a/ico/qet/traced/22/cable.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
diff --git a/ico/qet/traced/large16/cable.svg b/ico/qet/traced/large16/cable.svg
deleted file mode 100644
index 623488647..000000000
--- a/ico/qet/traced/large16/cable.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
diff --git a/ico/qet/traced/large22/cable.svg b/ico/qet/traced/large22/cable.svg
deleted file mode 100644
index 97f52785c..000000000
--- a/ico/qet/traced/large22/cable.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
diff --git a/ico/themes/generated/traced-24/cable.svg b/ico/themes/generated/traced-24/cable.svg
deleted file mode 100644
index ff2411462..000000000
--- a/ico/themes/generated/traced-24/cable.svg
+++ /dev/null
@@ -1,8 +0,0 @@
-
diff --git a/ico/themes/generated/traced-large-24/cable.svg b/ico/themes/generated/traced-large-24/cable.svg
deleted file mode 100644
index 778188781..000000000
--- a/ico/themes/generated/traced-large-24/cable.svg
+++ /dev/null
@@ -1,8 +0,0 @@
-
diff --git a/ico/themes/qet-dark/traced-24/cable.svg b/ico/themes/qet-dark/traced-24/cable.svg
deleted file mode 100644
index 6cf88f6c5..000000000
--- a/ico/themes/qet-dark/traced-24/cable.svg
+++ /dev/null
@@ -1,8 +0,0 @@
-
diff --git a/ico/themes/qet-dark/traced-large-24/cable.svg b/ico/themes/qet-dark/traced-large-24/cable.svg
deleted file mode 100644
index 3aae9ea45..000000000
--- a/ico/themes/qet-dark/traced-large-24/cable.svg
+++ /dev/null
@@ -1,8 +0,0 @@
-
diff --git a/ico/themes/qet-dark/traced/16/cable.svg b/ico/themes/qet-dark/traced/16/cable.svg
deleted file mode 100644
index b21070f7f..000000000
--- a/ico/themes/qet-dark/traced/16/cable.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
diff --git a/ico/themes/qet-dark/traced/22/cable.svg b/ico/themes/qet-dark/traced/22/cable.svg
deleted file mode 100644
index 608b450e4..000000000
--- a/ico/themes/qet-dark/traced/22/cable.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
diff --git a/ico/themes/qet-dark/traced/large16/cable.svg b/ico/themes/qet-dark/traced/large16/cable.svg
deleted file mode 100644
index 8c9386972..000000000
--- a/ico/themes/qet-dark/traced/large16/cable.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
diff --git a/ico/themes/qet-dark/traced/large22/cable.svg b/ico/themes/qet-dark/traced/large22/cable.svg
deleted file mode 100644
index 104596625..000000000
--- a/ico/themes/qet-dark/traced/large22/cable.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
diff --git a/sources/autoNum/assignvariables.cpp b/sources/autoNum/assignvariables.cpp
index 40563956c..633fe7da3 100644
--- a/sources/autoNum/assignvariables.cpp
+++ b/sources/autoNum/assignvariables.cpp
@@ -649,74 +649,6 @@ namespace autonum
}
}
- /**
- @brief resetFolioCounters
- @param nc the rule to read
- @return the same rule, with the parts whose counter belongs to a
- single folio put back to the beginning that folio is numbered
- from -- the initial value the rule carries for it -- while the
- parts counting over the whole project are left alone
- */
- NumerotationContext resetFolioCounters(const NumerotationContext &nc)
- {
- NumerotationContext out = nc;
- for (int i = 0; i < out.size(); ++i)
- {
- const QStringList parts = out.itemAt(i);
- if (parts.isEmpty()) continue;
-
- const QString &type = parts.at(0);
- if (type == QLatin1String("unitfolio")
- || type == QLatin1String("tenfolio")
- || type == QLatin1String("hundredfolio"))
- {
- //The beginning a folio is numbered from is the one
- //written into the rule, never a hard 1: a counter
- //starting at 5 starts every folio at 5.
- const bool has_start = parts.size() > 3
- && !parts.at(3).isEmpty();
- out.replaceValue(i, has_start ? parts.at(3)
- : QStringLiteral("1"));
- }
- }
- return out;
- }
-
- /**
- @brief resetContextCounters
- @param nc the rule to read
- @return the same rule, with every part which counts set back to
- the value it started from
- */
- NumerotationContext resetContextCounters(const NumerotationContext &nc)
- {
- NumerotationContext out = resetFolioCounters(nc);
- for (int i = 0; i < out.size(); ++i)
- {
- const QStringList parts = out.itemAt(i);
- if (parts.isEmpty()) continue;
-
- const QString &type = parts.at(0);
- if (type == QLatin1String("unit")
- || type == QLatin1String("ten")
- || type == QLatin1String("hundred"))
- {
- out.replaceValue(i, QStringLiteral("1"));
- }
- else if (type == QLatin1String("wrap"))
- {
- //A cyclic part walks over [0, modulus), so it goes
- //back to 0 rather than to 1
- out.replaceValue(i, QStringLiteral("0"));
- }
- else if (type == QLatin1String("alpha"))
- {
- out.replaceValue(i, QStringLiteral("a"));
- }
- }
- return out;
- }
-
/**
@brief numerotationContextToFormula
@param nc
diff --git a/sources/autoNum/assignvariables.h b/sources/autoNum/assignvariables.h
index 04e353274..255ad6003 100644
--- a/sources/autoNum/assignvariables.h
+++ b/sources/autoNum/assignvariables.h
@@ -117,31 +117,6 @@ namespace autonum
void setFolioSequentialToHash(QStringList &list, QHash &hash, const QString& autoNumName);
void setSequential(const QString& label, autonum::sequentialNumbers &seqStruct, NumerotationContext &context, Diagram *diagram, const QString& hashKey);
QString numerotationContextToFormula(const NumerotationContext &nc);
- /**
- Put back every part of a numbering rule whose counter belongs to
- one single folio -- "Chiffre 1 - Folio" and its two longer forms
- -- to the beginning that folio is numbered from, which is the
- initial value written into the rule itself. The parts which count
- over the whole project are left exactly as they are: a folio
- starts again, the project does not.
- @param nc the rule to read
- @return the same rule, with its folio-bound counters restarted
- */
- NumerotationContext resetFolioCounters(const NumerotationContext &nc);
- /**
- Set every part of a numbering rule which counts back to its very
- first value, so that numbering starts at the beginning again --
- which is what numbering a whole project over has to do, whatever
- the last number handed out happened to be. A part which counts
- from an initial value of its own goes back to that value, a plain
- number goes back to 1, a cyclic one to 0 and an alphabetic one to
- "a". The parts which do not count -- a fixed text, the number of
- the folio, the plant -- are left exactly as they are: they say
- what a number is made of, they are not the part which goes on.
- @param nc the rule to read
- @return the same rule, with its counters at their beginning
- */
- NumerotationContext resetContextCounters(const NumerotationContext &nc);
QString elementPrefixForLocation(const ElementsLocation &location);
}
diff --git a/sources/autoNum/ui/numparteditorw.cpp b/sources/autoNum/ui/numparteditorw.cpp
index 7b1d25908..9f45289ad 100644
--- a/sources/autoNum/ui/numparteditorw.cpp
+++ b/sources/autoNum/ui/numparteditorw.cpp
@@ -105,16 +105,6 @@ NumPartEditorW::NumPartEditorW (NumerotationContext &context,
if (strl.at(0)=="wrap" && strl.size() > 4)
ui -> modulus_spinBox -> setValue(strl.at(4).toInt());
ui -> format_le -> setText(NumerotationContext::formatOf(strl));
- //A counter bound to the folio keeps the start it was
- //configured with: value_field shows the live counter, and
- //writing that back as the start would make every folio
- //begin at the last number the previous numbering gave out
- if ((strl.at(0)==QLatin1String("unitfolio")
- || strl.at(0)==QLatin1String("tenfolio")
- || strl.at(0)==QLatin1String("hundredfolio"))
- && strl.size() > 3 && !strl.at(3).isEmpty()) {
- m_initial_value = strl.at(3).toInt();
- }
}
}
@@ -141,12 +131,8 @@ void NumPartEditorW::setVisibleItems()
<< tr("Alphabetical")
<< tr("Text");
}
- else if (m_edited_type == 1 || m_edited_type == 3)
+ else if (m_edited_type == 1)
{
- //Type 3 is a cable: it is drawn along a folio just like a
- //conductor is, so it takes the same kinds of variable and
- //none of the ones an element has and a cable has not (its
- //line, its column, its prefix).
items << tr("number format 1")
<< tr("number format 1 - Sheet")
<< tr("number format 01")
@@ -246,8 +232,7 @@ NumerotationContext NumPartEditorW::toNumContext()
nc.addValue(type_str,
ui -> value_field -> displayText(),
ui -> increase_spinBox -> value(),
- m_initial_value >= 0 ? m_initial_value
- : ui->value_field->displayText().toInt(),
+ ui->value_field->displayText().toInt(),
0,
number_format);
else if (type_str == "wrap")
@@ -330,10 +315,6 @@ void NumPartEditorW::on_type_cb_activated(int) {
*/
void NumPartEditorW::on_value_field_textEdited()
{
- //What the user types in is a deliberate start for a counter
- //bound to the folio; merely showing the live counter (as
- //setContext() does) never changes the start.
- m_initial_value = ui -> value_field -> text().toInt();
emit changed();
}
diff --git a/sources/autoNum/ui/numparteditorw.h b/sources/autoNum/ui/numparteditorw.h
index b9ee8aafb..ce7d3ffae 100644
--- a/sources/autoNum/ui/numparteditorw.h
+++ b/sources/autoNum/ui/numparteditorw.h
@@ -77,14 +77,6 @@ class NumPartEditorW : public QWidget
QValidator *intValidator;
QValidator *alphaValidator;
int m_edited_type = -1; ///<0 == element : 1 == conductor : 2 == folio
- /**
- The value a counter bound to the folio counts each folio
- from (-1 while the row carries none): value_field shows
- the live counter, which numbering a project over leaves
- at the last number given out, so the start has to be
- kept apart from what is on show.
- */
- int m_initial_value = -1;
diff --git a/sources/autoNum/ui/pastenumberingimport.cpp b/sources/autoNum/ui/pastenumberingimport.cpp
index 3a1bc9f2f..0e4ca0476 100644
--- a/sources/autoNum/ui/pastenumberingimport.cpp
+++ b/sources/autoNum/ui/pastenumberingimport.cpp
@@ -37,41 +37,22 @@ QList PasteNumberingImport::copiedBy(const
elements follow and the project does not have.
@param parent : parent of the question
@param copied : the numberings the copy carries
- @param extra : commands the copy brings beside the paste itself
- (the cable lines of a fragment, for example); they are pushed inside
- the same macro, so one Ctrl+Z undoes all of it at once.
*/
void PasteNumberingImport::push(QWidget *parent,
Diagram *diagram,
const DiagramContent &content,
- const QList &copied,
- const QList &extra)
+ const QList &copied)
{
QETProject *project = diagram->project();
auto *paste = new PasteDiagramCommand(diagram, content);
- //The paste and the commands beside it are one step: only the
- //macros really need more than the single push.
- const auto push_all = [&]() {
- if (extra.isEmpty()) {
- diagram->undoStack().push(paste);
- return;
- }
- diagram->undoStack().beginMacro(paste->text());
- diagram->undoStack().push(paste);
- for (QUndoCommand *cmd : extra) {
- diagram->undoStack().push(cmd);
- }
- diagram->undoStack().endMacro();
- };
-
QList missing;
if (project && !copied.isEmpty()
&& QSettings().value("diagramcommands/autonumber-pasted-elements", true).toBool()) {
missing = ElementAutoNumSchemeCommand::missingForPaste(project, copied, content.m_elements);
}
if (missing.isEmpty()) {
- push_all();
+ diagram->undoStack().push(paste);
return;
}
@@ -90,7 +71,7 @@ void PasteNumberingImport::push(QWidget *parent,
QMessageBox::Yes | QMessageBox::No,
QMessageBox::Yes);
if (answer != QMessageBox::Yes) {
- push_all();
+ diagram->undoStack().push(paste);
return;
}
@@ -110,8 +91,5 @@ void PasteNumberingImport::push(QWidget *parent,
}
}
diagram->undoStack().push(paste);
- for (QUndoCommand *cmd : extra) {
- diagram->undoStack().push(cmd);
- }
diagram->undoStack().endMacro();
}
diff --git a/sources/autoNum/ui/pastenumberingimport.h b/sources/autoNum/ui/pastenumberingimport.h
index 8e03c3b0d..0889ad813 100644
--- a/sources/autoNum/ui/pastenumberingimport.h
+++ b/sources/autoNum/ui/pastenumberingimport.h
@@ -23,7 +23,6 @@
#include
#include
-#include
class Diagram;
class QWidget;
@@ -41,11 +40,6 @@ class QWidget;
without a numbering.
The import and the paste are one undo step.
-
- A paste which brings more than itself (the cable lines of the copy,
- for instance, which are no part of a DiagramContent) hands those
- commands over through @p extra: they are pushed inside the very
- same macro, so one Ctrl+Z still takes the whole of it back.
*/
class PasteNumberingImport
{
@@ -55,8 +49,7 @@ class PasteNumberingImport
static void push(QWidget *parent,
Diagram *diagram,
const DiagramContent &content,
- const QList &copied,
- const QList &extra = QList());
+ const QList &copied);
static QList copiedBy(const QDomDocument &clipboard);
};
diff --git a/sources/autoNum/ui/selectautonumw.cpp b/sources/autoNum/ui/selectautonumw.cpp
index dc215651e..ff32c7b31 100644
--- a/sources/autoNum/ui/selectautonumw.cpp
+++ b/sources/autoNum/ui/selectautonumw.cpp
@@ -138,9 +138,6 @@ void SelectAutonumW::setContext(const NumerotationContext &context)
updatePartButtons();
applyEnable(false);
- //Everything the rule held before is read now, so nothing of it
- //is left to write: what is on show is what there is.
- m_dirty = false;
}
/**
@@ -150,11 +147,7 @@ void SelectAutonumW::setContext(const NumerotationContext &context)
*/
void SelectAutonumW::insertPartRow(NumPartEditorW *part)
{
- connect(part, &NumPartEditorW::changed, this, [this]() {
- m_dirty = true;
- applyEnable();
- emit contextEdited();
- });
+ connect(part, &NumPartEditorW::changed, this, [this]() { applyEnable(); });
PartRow r;
r.part = part;
@@ -270,10 +263,7 @@ NumerotationContext SelectAutonumW::toNumContext() const
void SelectAutonumW::on_add_button_clicked()
{
insertPartRow(new NumPartEditorW(m_edited_type, this));
- m_dirty = true;
- ui -> remove_button -> setEnabled(true);
applyEnable();
- emit contextEdited();
}
/**
@@ -282,16 +272,9 @@ void SelectAutonumW::on_add_button_clicked()
*/
void SelectAutonumW::on_remove_button_clicked()
{
- //remove if @num_part_list contains more than one item
- if (num_part_list_.size() > 1) {
- m_dirty = true;
+ if (!m_rows.isEmpty()) {
removePartRow(m_rows.last().row);
- if (num_part_list_.size() == 1) {
- ui -> remove_button -> setDisabled(true);
- }
}
- applyEnable();
- emit contextEdited();
}
/**
@@ -313,78 +296,6 @@ QComboBox *SelectAutonumW::contextComboBox() const
return ui->m_comboBox;
}
-/**
- @brief SelectAutonumW::setSingleRuleMode
- @param single true to show this editor as the one and only rule of
- its kind.
-
- The row which lists the available numberings -- its label, its combo
- box and the button which deletes one -- is hidden, so there is
- nothing to name a rule with and nothing to choose between: the rule
- being edited is all there is. The combo box itself stays where it is
- and keeps holding the rule's name, so everything which reads or
- writes the rule through contextComboBox() carries on untouched; he
- just cannot see it.
-*/
-void SelectAutonumW::setSingleRuleMode(bool single)
-{
- m_single_rule = single;
- ui->label->setVisible(!single);
- ui->m_comboBox->setVisible(!single);
- ui->m_remove_pb->setVisible(!single);
-
- //A single rule has no list to be deleted from, so it needs a
- //button of its own to get out of the numbering again. It is
- //only shown when there is really a rule to take away -- see
- //setRuleRemovable.
- if (single && !m_rule_remove_pb) {
- m_rule_remove_pb = new QPushButton(tr("Delete the rule"), this);
- m_rule_remove_pb->setToolTip(tr(
- "Delete the cable numbering rule: cables are "
- "numbered again as before, and the question of "
- "defining one comes back on the next drawing."));
- m_rule_remove_pb->setObjectName(QStringLiteral("m_rule_remove_pb"));
- if (ui->m_definition_groupe && ui->m_definition_groupe->layout()) {
- ui->m_definition_groupe->layout()->addWidget(m_rule_remove_pb);
- }
- connect(m_rule_remove_pb, &QPushButton::clicked,
- this, &SelectAutonumW::removeClicked);
- }
- if (m_rule_remove_pb) {
- m_rule_remove_pb->setVisible(m_single_rule && m_rule_removable);
- }
-}
-
-/**
- @brief SelectAutonumW::setRuleRemovable
- Whether the button which takes the whole rule away is worth showing:
- it has nothing to do while this editor holds no rule at all, and it
- would only be a button which does nothing.
- @param can_remove true when a rule is there to be taken away
-*/
-void SelectAutonumW::setRuleRemovable(bool can_remove)
-{
- m_rule_removable = can_remove;
- if (m_rule_remove_pb) {
- m_rule_remove_pb->setVisible(m_single_rule && can_remove);
- }
-}
-
-/**
- @brief SelectAutonumW::isValid
- @return true when there is at least one variable in the editor and
- every one of them holds what it needs. An editor which has only just
- been opened holds an empty rule, and an empty rule is not one.
-*/
-bool SelectAutonumW::isValid()
-{
- if (num_part_list_.isEmpty()) return false;
- for (NumPartEditorW *npe : num_part_list_) {
- if (!npe || !npe->isValid()) return false;
- }
- return true;
-}
-
/**
@brief SelectAutonumW::on_buttonBox_clicked
Action on button clicked
@@ -453,32 +364,6 @@ void SelectAutonumW::on_buttonBox_clicked(QAbstractButton *button)
));
break;
}
- else if (m_edited_type == 3)
- {
- QMessageBox::information (
- this,
- tr("Cable Auto Numbering",
- "title window"),
- tr("This is where you can define how new cables will be numbered.\n"
- "-A numbering is composed of a minimum variable.\n"
- "-You can add or delete a dialing variable through the - and + buttons.\n"
- "-A numbering variable includes: a type, a value and an increment.\n"
-
- "\n-the \"Digit 1\", \"Digit 01\" and \"Digit 001\" types represent a numeric type defined in the \"Value\" field, "
- "which is incremented to each new cable by the value of the \"Increment\" field.\n"
- "-\"Digit 01\" and \"Digit 001\", are respectively represented on the diagram by two and three digits minimum.\n"
- "If the digit defined in the Value field has fewer digits than the chosen type, it will be preceded by one or two 0s in "
- "order to respect its type.\n"
-
- "\n-Type \"Text\", represents a fixed text.\nThe \"Increment\" field is not used.\n"
-
- "\n-The \"Sheet no.\" type represents the number of the current sheet.\nThe other fields are not used.\n"
-
- "\n-The \"Sheet\" type represents the name of the current sheet.\nThe other fields are not used.",
- "help dialog about the cable autonumerotation"
- ));
- break;
- }
else
{
QMessageBox::information (
@@ -512,9 +397,6 @@ void SelectAutonumW::on_buttonBox_clicked(QAbstractButton *button)
case QDialogButtonBox::ApplyRole:
applyEnable(false);
emit applyPressed();
- //Whatever was edited is written now, so there is
- //nothing of it left for a later save to write again.
- m_dirty = false;
break;
};
}
diff --git a/sources/autoNum/ui/selectautonumw.h b/sources/autoNum/ui/selectautonumw.h
index b43c47fa2..77055b764 100644
--- a/sources/autoNum/ui/selectautonumw.h
+++ b/sources/autoNum/ui/selectautonumw.h
@@ -27,7 +27,6 @@ class NumPartEditorW;
class QAbstractButton;
class FormulaAutonumberingW;
class QComboBox;
-class QPushButton;
class QToolButton;
class QHBoxLayout;
@@ -55,55 +54,11 @@ class SelectAutonumW : public QWidget
void contextToFormula ();
QString formula();
QComboBox *contextComboBox() const;
- /**
- Show this editor as the only numbering rule there is: the
- row which lets several named numberings be kept and chosen
- between is hidden, so the rule being edited is the one and
- only. Everything else -- the definition itself, the buttons
- around it -- stays exactly as it is for the other kinds.
- */
- void setSingleRuleMode(bool single);
- bool isSingleRuleMode() const { return m_single_rule; }
- /**
- True while everything the editor holds is filled in well
- enough to be a rule -- no variable left with nothing in it.
- The Apply button says the same thing; this is so that a page
- which saves on its own way knows it too, rather than writing
- a rule nobody has finished filling in.
- */
- bool isValid();
- /**
- True while the rule on show has been touched since it was
- read or since it was last applied: a page which saves on
- its own way (the cable rule does) writes it only when there
- is something to write, so that confirming a window with
- nothing changed in it leaves the project alone.
- */
- bool isModified() const { return m_dirty; }
- /**
- Show the button which takes the rule itself away, which is
- the only way to stop numbering cables automatically again:
- the row the other numberings delete from is hidden here, so
- a single rule would have no way out at all. The page behind
- the editor says whether there is a rule to take away at the
- moment -- without one the button has nothing to do and is
- kept out of sight.
- @param can_remove true when a rule is really there
- */
- void setRuleRemovable(bool can_remove);
void setExplicitNaming();
signals:
void applyPressed();
void removeClicked();
- /**
- Sent whenever what the rule holds is edited -- a
- field of a part changed, a part added, a part taken
- away -- so that a window holding this editor can let
- its own buttons say whether there is something to
- write now.
- */
- void contextEdited();
/// Only with setExplicitNaming()
void newClicked();
void renameClicked();
@@ -141,19 +96,7 @@ class SelectAutonumW : public QWidget
NumerotationContext m_context;
FormulaAutonumberingW *m_feaw;
FormulaAutonumberingW *m_fcaw;
- int m_edited_type = -1; ///<0 == element : 1 == conductor : 2 == folio : 3 == cable
- ///< when true, the row of available numberings is hidden and
- ///< this editor holds the one rule there is (see setSingleRuleMode)
- bool m_single_rule = false;
- ///< true from the moment the rule on show is touched until
- ///< it is read again or applied (see isModified)
- bool m_dirty = false;
- ///< the button which takes the whole rule away, built for
- ///< the one kind of numbering which has no list to delete
- ///< it from (see setRuleRemovable)
- QPushButton *m_rule_remove_pb = nullptr;
- ///< whether such a rule is there to be taken away
- bool m_rule_removable = false;
+ int m_edited_type = -1; ///<0 == element : 1 == conductor : 2 == folio
};
#endif // SELECTAUTONUMW_H
diff --git a/sources/bomexport.cpp b/sources/bomexport.cpp
index 768dd78c0..97ecdff61 100644
--- a/sources/bomexport.cpp
+++ b/sources/bomexport.cpp
@@ -29,9 +29,7 @@ QString csvField(QString value)
return QLatin1Char('"') + value + QLatin1Char('"');
}
-}
-
-QByteArray BomExport::csvRecord(const QStringList &values)
+QByteArray csvRecord(const QStringList &values)
{
QStringList escaped;
for (const auto &value : values) {
@@ -39,6 +37,7 @@ QByteArray BomExport::csvRecord(const QStringList &values)
}
return (escaped.join(QLatin1Char(';')) + QLatin1Char('\n')).toUtf8();
}
+}
QStringList BomExport::defaultColumns()
{
diff --git a/sources/bomexport.h b/sources/bomexport.h
index a12ba5789..5305d378e 100644
--- a/sources/bomexport.h
+++ b/sources/bomexport.h
@@ -30,11 +30,6 @@ namespace BomExport
QString defaultQuery(bool include_slaves = true,
bool include_junctions = true);
QString junctionFilter();
- /// One CSV record: every field quoted, ';' between them, '\n'
- /// after -- the format every CSV export of this program writes.
- /// Public so a list which is not read from the project database
- /// (the cable list) writes the same bytes.
- QByteArray csvRecord(const QStringList &values);
QByteArray toCsv(QSqlQuery &query, const QStringList &headers,
bool include_headers = true, int *row_count = nullptr);
bool writeCsv(const QString &file_path, const QByteArray &csv,
diff --git a/sources/cable/addcablecommand.cpp b/sources/cable/addcablecommand.cpp
deleted file mode 100644
index d3d81b41b..000000000
--- a/sources/cable/addcablecommand.cpp
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- 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 .
-*/
-#include "addcablecommand.h"
-
-#include "cablemanager.h"
-#include "cablepart.h"
-#include "../autoNum/numerotationcontextcommands.h"
-#include "../diagram.h"
-#include "../qetproject.h"
-
-#include
-
-/**
- @brief AddCableCommand::AddCableCommand
- @param cable the cable the line was just given to
- @param part the line which was just drawn
- @param diagram the folio it was drawn on
- @param own_cable true when the cable was created by this very action
- @param taken the cores this line took away from other cables, so
- undo can give them back
-*/
-AddCableCommand::AddCableCommand(Cable *cable,
- CablePart *part,
- Diagram *diagram,
- bool own_cable,
- const QList &taken) :
- m_cable(cable),
- m_part(part),
- m_diagram(diagram),
- m_taken(taken),
- m_own_cable(own_cable)
-{
- if (!m_cable || !m_part) return;
-
- m_part_data.uuid = m_part->partUuid();
- m_part_data.diagram = m_diagram ? m_diagram->uuid() : QUuid();
- m_part_data.p1 = m_part->firstPoint();
- m_part_data.p2 = m_part->secondPoint();
-
- for (const CableCore &core : m_cable->usedCores()) {
- if (core.part == m_part_data.uuid) {
- m_cores.append(core);
- }
- }
-
- //A cable this action brought into being takes the next number
- //the project's numbering rule hands out. The number itself was
- //worked out before this command existed; what has to happen here
- //is moving the rule on, and moving it is undoable just like the
- //cable is -- so undoing gives the number back and the cable
- //drawn after that takes it again instead of skipping over it.
- //
- //Only the rule as it stands now is taken down. Where the rule
- //stands after this cable is worked out when the cable really
- //comes into being, because several cables pasted together are
- //all made before any of them is added, and each of them has to
- //move the rule on from wherever the one before it left it.
- if (m_own_cable && m_diagram && project() && project()->hasCableAutoNum())
- {
- const QString key = project()->cableCurrentAutoNum();
- const NumerotationContext current = project()->cableAutoNum(key);
- if (!current.isEmpty())
- {
- m_num_key = key;
- m_num_before = current;
- }
- }
-}
-
-/**
- @brief AddCableCommand::~AddCableCommand
- Whatever the stack leaves behind has to be cleaned up here: a cable
- which is not held by the project any more belongs to this command,
- and so does a line which never made it onto a folio.
-
- The cable is a QPointer, so if another command took it away first it
- is already null here and there is nothing left to do -- which is
- exactly the point, since following a raw pointer here meant freeing
- the same cable a second time.
-*/
-AddCableCommand::~AddCableCommand()
-{
- if (m_cable && m_own_cable) {
- QETProject *proj = project();
- if (!proj || !proj->cables().contains(m_cable.data())) {
- delete m_cable.data();
- }
- }
- if (m_part && !m_part->scene()) {
- delete m_part;
- }
-}
-
-/**
- @brief AddCableCommand::project
- @return the project this command works in, or nullptr
-*/
-QETProject *AddCableCommand::project() const
-{
- return m_diagram ? m_diagram->project() : nullptr;
-}
-
-/**
- @brief AddCableCommand::redo
- Put the line on the folio, together with everything the cable holds
- for it.
-*/
-void AddCableCommand::redo()
-{
- QETProject *proj = project();
- if (!proj || !m_cable || !m_part || !m_diagram) return;
-
- if (m_own_cable) {
- proj->addCable(m_cable);
- } else {
- if (!m_cable->hasPart(m_part_data.uuid)) {
- m_cable->addPart(m_part_data);
- }
- for (const CableCore &core : m_cores) {
- m_cable->setCore(core);
- }
- }
-
- if (m_part->scene() != m_diagram) {
- m_diagram->addItem(m_part);
- }
-
- //Take the cores away from the cables which held them before this
- //line claimed those conductors. On the very first run they are
- //already gone, so nothing happens then; redoing after an undo
- //has to take them again.
- for (const CableCoreTaken &taken : std::as_const(m_taken))
- {
- if (taken.cable && taken.cable != m_cable) {
- taken.cable->removeCore(taken.core.core);
- }
- }
- //The cable field of every conductor is worked out again either
- //way: redoing after an undo puts the cable back into the project
- //without it having said anything, and the wires it feeds would go
- //on showing a blank cable field although the cable which
- //generates that field is right there again.
- //
- //And the number this cable was given is spent now: the rule
- //moves on to the one the next cable will be handed. Worked out
- //here rather than when the command was made, because a run of
- //cables pasted together is made from end to end before any of
- //it is added -- each of them has to move the rule on from where
- //the one before it left it.
- if (!m_num_key.isEmpty()) {
- NumerotationContextCommands ncc(proj->cableAutoNum(m_num_key),
- m_diagram.data());
- proj->addCableAutoNum(m_num_key, ncc.next());
- proj->setCurrentCableAutoNum(m_num_key);
- }
- CableManager::refreshLabels(proj);
-}
-
-/**
- @brief AddCableCommand::undo
- Take the line back off the folio. An existing cable loses the
- section and the cores it was carrying on it -- and with them the
- cable field of the conductors, which CableManager works out again as
- soon as the cable says it changed.
-
- The cores this line had taken away from other cables go back to
- those cables afterwards: without them the conductors they belong to
- would be left with a blank cable field although the cable which
- generates that field is still there.
-*/
-void AddCableCommand::undo()
-{
- QETProject *proj = project();
- if (!proj || !m_cable || !m_part || !m_diagram) return;
-
- if (m_part->scene()) {
- m_diagram->removeItem(m_part);
- }
-
- if (m_own_cable) {
- //The cable keeps everything it knows: it is only taken out of
- //the project, so redo finds it exactly as it was.
- proj->removeCable(m_cable);
- } else {
- m_cable->removePart(m_part_data.uuid);
- }
-
- for (const CableCoreTaken &taken : std::as_const(m_taken))
- {
- if (taken.cable && taken.cable != m_cable) {
- taken.cable->setCore(taken.core);
- }
- }
- //The number this cable was given goes back into the rule, so
- //the cable drawn after an undo takes that number rather than
- //the one after it: nothing of it is left spent.
- if (!m_num_key.isEmpty()) {
- proj->addCableAutoNum(m_num_key, m_num_before);
- proj->setCurrentCableAutoNum(m_num_key);
- }
-
- //Same as in redo(): worked out again whatever happened above,
- //so a cable section or a whole cable taken away never leaves
- //the wires it used to feed with a cable field it still writes.
- CableManager::refreshLabels(proj);
-}
diff --git a/sources/cable/addcablecommand.h b/sources/cable/addcablecommand.h
deleted file mode 100644
index 2980ece12..000000000
--- a/sources/cable/addcablecommand.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- 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 .
-*/
-#ifndef ADDCABLECOMMAND_H
-#define ADDCABLECOMMAND_H
-
-#include "cable.h"
-
-#include "../autoNum/numerotationcontext.h"
-
-#include
-#include
-#include
-
-class CablePart;
-class Diagram;
-class QETProject;
-
-/**
- @brief Puts a drawn cable line on the folio, or takes it back off.
-
- Both halves are one single step, because a cable line without its
- cable is of no use to anybody: undoing has to take the line away and
- leave nothing behind, and redoing has to put both back exactly as
- they were.
-*/
-class AddCableCommand : public QUndoCommand
-{
- public:
- AddCableCommand(Cable *cable,
- CablePart *part,
- Diagram *diagram,
- bool own_cable,
- const QList &taken = QList());
- ~AddCableCommand() override;
-
- void undo() override;
- void redo() override;
-
- private:
- QETProject *project() const;
-
- private:
- /**
- The cable the line belongs to.
-
- A pointer, not a raw one: RemoveCableCommand deletes the
- cable when it goes, and both of them are torn down together
- when the stack takes a new command. Whoever of the two runs
- first frees the cable, so the other one must not follow it
- into the freed memory -- QPointer goes null instead, and
- this command then simply has nothing left to clean up.
- */
- QPointer m_cable;
- ///The line itself, owned by the folio once it is drawn
- QPointer m_part;
- QPointer m_diagram;
- ///Which section of the cable this line is
- CablePartData m_part_data;
- ///The cores the line was giving to the cable, put back by redo
- QList m_cores;
- ///The cores this line took away from other cables: redo takes
- ///them again, undo gives them back
- QList m_taken;
- ///True when this command brought the cable into existence, so it
- ///is the one which has to take it away again
- bool m_own_cable = false;
- /**
- When this cable took a number out of the project's numbering
- rule, the rule as it was before it did and as it was after:
- an empty key means the project numbers its cables by no rule
- and nothing here applies. Handing the number out is part of
- bringing the cable into being, so undo has to hand it back --
- otherwise undoing a cable and drawing it once more would
- leave a gap in the numbering for good.
- */
- QString m_num_key;
- NumerotationContext m_num_before;
-};
-
-#endif // ADDCABLECOMMAND_H
diff --git a/sources/cable/cable.cpp b/sources/cable/cable.cpp
deleted file mode 100644
index 79810c5e7..000000000
--- a/sources/cable/cable.cpp
+++ /dev/null
@@ -1,921 +0,0 @@
-/*
- 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 .
-*/
-#include "cable.h"
-
-#include
-#include
-
-#include
-
-namespace {
- /**
- @brief The name an alignment is written under in the file, an
- empty string when the alignment is the plain default (the text
- ends where it always did, before the line).
- @param alignment Qt::AlignLeft, Qt::AlignHCenter or Qt::AlignRight
- @return left, center or right
- */
- QString alignmentName(int alignment)
- {
- switch (alignment)
- {
- case int(Qt::AlignLeft): return QStringLiteral("left");
- case int(Qt::AlignHCenter): return QStringLiteral("center");
- case int(Qt::AlignRight): return QStringLiteral("right");
- default: return QString();
- }
- }
-
- /**
- @brief The alignment a name written in the file stands for.
- @param name left, center or right
- @return the Qt flags, 0 when the name says nothing
- */
- int alignmentFromName(const QString &name)
- {
- if (name == QLatin1String("left")) return int(Qt::AlignLeft);
- if (name == QLatin1String("center")) return int(Qt::AlignHCenter);
- if (name == QLatin1String("right")) return int(Qt::AlignRight);
- return 0;
- }
-}
-
-/**
- @brief Cable::Cable
- @param parent
-*/
-Cable::Cable(QObject *parent) :
- QObject(parent),
- m_uuid(QUuid::createUuid())
-{
-}
-
-/**
- @brief Cable::uuid
- @return the identity of the cable, never empty once constructed
-*/
-QUuid Cable::uuid() const
-{
- return m_uuid;
-}
-
-/**
- @brief Cable::setUuid
- Only used while reading a project: a cable keeps the identity it was
- saved with, so the conductors it feeds stay bound to it.
- @param uuid
-*/
-void Cable::setUuid(const QUuid &uuid)
-{
- if (uuid.isNull() || uuid == m_uuid) {
- return;
- }
- m_uuid = uuid;
- emit changed();
-}
-
-/**
- @brief Cable::designation
- @return the cable number, e.g. 15W1, which heads the label drawn next
- to the trunk line and opens every core label
-*/
-QString Cable::designation() const
-{
- return m_designation;
-}
-
-/**
- @brief Cable::setDesignation
- @param designation
-*/
-void Cable::setDesignation(const QString &designation)
-{
- if (m_designation == designation) return;
- m_designation = designation;
- emit changed();
-}
-
-/**
- @brief Cable::designationByHand
- @return true when the name of this cable was typed in rather than
- worked out of the numbering rule
-*/
-bool Cable::designationByHand() const
-{
- return m_designation_by_hand;
-}
-
-/**
- @brief Cable::setDesignationByHand
- Says where the name of this cable comes from. Nothing of it is
- drawn, so it changes no label and asks for no redraw.
- @param by_hand
-*/
-void Cable::setDesignationByHand(bool by_hand)
-{
- m_designation_by_hand = by_hand;
-}
-
-/**
- @brief Cable::bmk
- @return the BMK of the cable
-*/
-QString Cable::bmk() const
-{
- return m_bmk;
-}
-
-/**
- @brief Cable::setBmk
- @param bmk
-*/
-void Cable::setBmk(const QString &bmk)
-{
- if (m_bmk == bmk) return;
- m_bmk = bmk;
- emit changed();
-}
-
-/**
- @brief Cable::installation
- @return the installation (Anlage) the cable belongs to
-*/
-QString Cable::installation() const
-{
- return m_installation;
-}
-
-/**
- @brief Cable::setInstallation
- @param installation
-*/
-void Cable::setInstallation(const QString &installation)
-{
- if (m_installation == installation) return;
- m_installation = installation;
- emit changed();
-}
-
-/**
- @brief Cable::location
- @return the location (Ort) the cable runs through
-*/
-QString Cable::location() const
-{
- return m_location;
-}
-
-/**
- @brief Cable::setLocation
- @param location
-*/
-void Cable::setLocation(const QString &location)
-{
- if (m_location == location) return;
- m_location = location;
- emit changed();
-}
-
-/**
- @brief Cable::length
- @return the length of the cable as it is written down: a free text,
- because it may come from the drawing or be typed in by hand
-*/
-QString Cable::length() const
-{
- return m_length;
-}
-
-/**
- @brief Cable::setLength
- @param length
-*/
-void Cable::setLength(const QString &length)
-{
- if (m_length == length) return;
- m_length = length;
- emit changed();
-}
-
-/**
- @brief Cable::properties
- @return every field the user fills in, together with what of it he
- want to see drawn next to the trunk line
-*/
-CableProperties Cable::properties() const
-{
- CableProperties properties;
- properties.designation = m_designation;
- properties.designation_by_hand = m_designation_by_hand;
- properties.type = m_type;
- properties.installation = m_installation;
- properties.location = m_location;
- properties.length = m_length;
- properties.show_designation = m_show_designation;
- properties.show_type = m_show_type;
- properties.show_installation = m_show_installation;
- properties.show_location = m_show_location;
- properties.show_length = m_show_length;
- properties.texts = m_texts;
- properties.text_offset = m_text_offset;
- properties.core_offset = m_core_offset;
- return properties;
-}
-
-/**
- @brief Cable::setProperties
- Write a whole set of fields at once -- one undo step covers it, and
- the label next to the trunk line and the conductors it feeds are
- worked out again once, not ten times.
- @param properties
-*/
-void Cable::setProperties(const CableProperties &properties)
-{
- if (properties == this->properties()) return;
-
- m_designation = properties.designation;
- m_designation_by_hand = properties.designation_by_hand;
- m_type = properties.type;
- m_installation = properties.installation;
- m_location = properties.location;
- m_length = properties.length;
- m_show_designation = properties.show_designation;
- m_show_type = properties.show_type;
- m_show_installation = properties.show_installation;
- m_show_location = properties.show_location;
- m_show_length = properties.show_length;
- m_texts = properties.texts;
- m_text_offset = properties.text_offset;
- m_core_offset = properties.core_offset;
- emit changed();
-}
-
-/**
- @brief Cable::type
- @return the type of the cable, e.g. H07RN-F 4G1,5. The type is copied
- from the cable type file when the cable is created, so a project
- opens even when that file is gone or has changed since.
-*/
-QString Cable::type() const
-{
- return m_type;
-}
-
-/**
- @brief Cable::setType
- @param type
-*/
-void Cable::setType(const QString &type)
-{
- if (m_type == type) return;
- m_type = type;
- emit changed();
-}
-
-/**
- @brief Cable::coreCount
- @return how many cores the type of this cable has. This is only the
- size of the type, not the number of cores actually wired: see
- usedCoreCount().
-*/
-int Cable::coreCount() const
-{
- return m_core_count;
-}
-
-/**
- @brief Cable::setCoreCount
- @param count
-*/
-void Cable::setCoreCount(int count)
-{
- count = qMax(0, count);
- if (m_core_count == count) return;
- m_core_count = count;
-
- //A type with fewer cores takes the cores it no longer has away
- //from the conductors which showed them: the label of a core the
- //cable doesn't own would be a lie.
- for (int i = m_cores.size() - 1; i >= 0; --i) {
- if (m_cores.at(i).core >= m_core_count) {
- m_cores.removeAt(i);
- }
- }
- emit changed();
-}
-
-/**
- @brief Cable::coreColors
- @return the colour of each core, in the order of the cable type file
-*/
-QStringList Cable::coreColors() const
-{
- return m_core_colors;
-}
-
-/**
- @brief Cable::setCoreColors
- @param colors
-*/
-void Cable::setCoreColors(const QStringList &colors)
-{
- if (m_core_colors == colors) return;
- m_core_colors = colors;
- emit changed();
-}
-
-/**
- @brief Cable::typeState
- @return everything the type of this cable decides, taken in one
- block: changing the type works on that block, and undo puts the very
- same block back -- name, size, colours and the cores which go with
- them, so no mark can be left standing on a core the cable lost.
-*/
-CableTypeState Cable::typeState() const
-{
- CableTypeState state;
- state.type = m_type;
- state.core_count = m_core_count;
- state.core_colors = m_core_colors;
- state.cores = m_cores;
- return state;
-}
-
-/**
- @brief Cable::applyTypeState
- Put back a state taken with typeState().
-
- The count comes before the cores: a cable which has fewer cores than
- it had cannot hold the others, and setCores() drops what stands
- beyond the count -- which is exactly what a type with fewer cores
- means, and exactly what undo has to take back.
- @param state
-*/
-void Cable::applyTypeState(const CableTypeState &state)
-{
- setType(state.type);
- setCoreColors(state.core_colors);
- setCoreCount(state.core_count);
- setCores(state.cores);
-}
-
-/**
- @brief Cable::usedCores
- @return every core wired to a conductor, ordered by core number
-*/
-QList Cable::usedCores() const
-{
- QList cores = m_cores;
- std::sort(cores.begin(), cores.end(),
- [](const CableCore &a, const CableCore &b) {return a.core < b.core;});
- return cores;
-}
-
-/**
- @brief Cable::hasCore
- @param core
- @return true when that core is wired to a conductor
-*/
-bool Cable::hasCore(int core) const
-{
- for (const CableCore &c : m_cores) {
- if (c.core == core) return true;
- }
- return false;
-}
-
-/**
- @brief Cable::core
- @param core
- @return the assignment of that core, an empty one when it is free
-*/
-CableCore Cable::core(int core) const
-{
- for (const CableCore &c : m_cores) {
- if (c.core == core) return c;
- }
- return CableCore();
-}
-
-/**
- @brief Cable::setCore
- Wire a core to a conductor, or move the slash of an already wired
- core. A core number may only appear once, so the previous holder of
- that number, if any, is replaced.
- @param core
-*/
-void Cable::setCore(const CableCore &core)
-{
- if (core.core < 0 || core.core >= m_core_count) return;
-
- for (int i = 0; i < m_cores.size(); ++i) {
- if (m_cores.at(i).core == core.core) {
- if (m_cores.at(i).conductor == core.conductor
- && m_cores.at(i).part == core.part
- && m_cores.at(i).position == core.position) {
- return;
- }
- m_cores[i] = core;
- emit changed();
- return;
- }
- }
- m_cores.append(core);
- emit changed();
-}
-
-/**
- @brief Cable::setCores
- Write a whole set of assignments in place of the current one, the way
- undoing a dragged slash has to: every core which was moved, swapped
- or dropped comes back at once.
- @param cores
-*/
-void Cable::setCores(const QList &cores)
-{
- m_cores = cores;
- for (int i = m_cores.size() - 1; i >= 0; --i) {
- if (m_cores.at(i).core < 0 || m_cores.at(i).core >= m_core_count) {
- m_cores.removeAt(i);
- }
- }
- emit changed();
-}
-
-/**
- @brief Cable::removeCore
- Wire a core back to nothing: its conductor stops showing the label
- this cable gave it.
- @param core
-*/
-void Cable::removeCore(int core)
-{
- for (int i = 0; i < m_cores.size(); ++i) {
- if (m_cores.at(i).core == core) {
- m_cores.removeAt(i);
- emit changed();
- return;
- }
- }
-}
-
-/**
- @brief Cable::usedCoreCount
- @return how many cores of this cable are wired to a conductor
-*/
-int Cable::usedCoreCount() const
-{
- return m_cores.size();
-}
-
-/**
- @brief Cable::freeCoreCount
- @return how many cores the type offers and no conductor shows yet
-*/
-int Cable::freeCoreCount() const
-{
- return qMax(0, m_core_count - m_cores.size());
-}
-
-/**
- @brief Cable::firstFreeCore
- @return the lowest core number which is free, -1 when every core of
- the type is already wired
-*/
-int Cable::firstFreeCore() const
-{
- for (int i = 0; i < m_core_count; ++i) {
- if (!hasCore(i)) return i;
- }
- return -1;
-}
-
-/**
- @brief Cable::colorOfCore
- @param core
- @return the colour this core has in the cable type file, an empty
- string when the type doesn't describe that many cores
-*/
-QString Cable::colorOfCore(int core) const
-{
- return m_core_colors.value(core);
-}
-
-/**
- @brief Cable::labelOfCore
- The text written into the cable field of the conductor this core is
- wired to. It is never typed by hand: it is worked out here, from the
- cable and the core, so renaming the cable or changing its type
- updates every conductor at once.
- @param core
- @return e.g. "15W1:br"
-*/
-QString Cable::labelOfCore(int core) const
-{
- if (m_designation.isEmpty()) {
- return QString();
- }
- const QString color = colorOfCore(core);
- if (color.isEmpty()) {
- return m_designation;
- }
- return m_designation + QLatin1Char(':') + color;
-}
-
-/**
- @brief Cable::parts
- @return every drawn section of this cable, whatever folio it sits on
-*/
-QList Cable::parts() const
-{
- return m_parts;
-}
-
-/**
- @brief Cable::hasPart
- @param part
- @return true when the cable has a section with that identity
-*/
-bool Cable::hasPart(const QUuid &part) const
-{
- if (part.isNull()) return false;
- for (const CablePartData &p : m_parts) {
- if (p.uuid == part) return true;
- }
- return false;
-}
-
-/**
- @brief Cable::part
- @param part
- @return the section with that identity, an invalid one when there is none
-*/
-CablePartData Cable::part(const QUuid &part) const
-{
- for (const CablePartData &p : m_parts) {
- if (p.uuid == part) return p;
- }
- return CablePartData();
-}
-
-/**
- @brief Cable::addPart
- @param part
-*/
-void Cable::addPart(const CablePartData &part)
-{
- if (part.uuid.isNull() || hasPart(part.uuid)) return;
- m_parts.append(part);
- emit changed();
-}
-
-/**
- @brief Cable::removePart
- Also drops the cores drawn on that section, since their slash and
- their colour label live with it.
- @param part
-*/
-void Cable::removePart(const QUuid &part)
-{
- bool touched = false;
- for (int i = m_parts.size() - 1; i >= 0; --i) {
- if (m_parts.at(i).uuid == part) {
- m_parts.removeAt(i);
- touched = true;
- }
- }
- for (int i = m_cores.size() - 1; i >= 0; --i) {
- if (m_cores.at(i).part == part) {
- m_cores.removeAt(i);
- touched = true;
- }
- }
- if (touched) {
- emit changed();
- }
-}
-
-/**
- @brief Cable::setPartGeometry
- Moving or lengthening the trunk line is a purely optical change: no
- core is added, none is removed, nothing is reassigned. Only the line
- itself moves -- which is why this never emits changed(): the labels
- the cable writes into the conductors do not depend on it.
- @param part
- @param p1
- @param p2
-*/
-void Cable::setPartGeometry(const QUuid &part, const QPointF &p1, const QPointF &p2)
-{
- for (int i = 0; i < m_parts.size(); ++i) {
- if (m_parts.at(i).uuid != part) continue;
- if (m_parts.at(i).p1 == p1 && m_parts.at(i).p2 == p2) return;
- m_parts[i].p1 = p1;
- m_parts[i].p2 = p2;
- return;
- }
-}
-
-/**
- @brief Cable::setPartDiagram
- @param part
- @param diagram
-*/
-void Cable::setPartDiagram(const QUuid &part, const QUuid &diagram)
-{
- for (int i = 0; i < m_parts.size(); ++i) {
- if (m_parts.at(i).uuid != part) continue;
- if (m_parts.at(i).diagram == diagram) return;
- m_parts[i].diagram = diagram;
- emit changed();
- return;
- }
-}
-
-/**
- @brief Cable::movePartCores
- Carry the colour labels of one section along when that section is
- moved as a whole: when a copy is put down, when it is stamped a grid
- way beside the original, and when the line travels with the rest of
- a selection rather than on its own.
-
- Quiet on purpose. Where a slash stands says nothing about the text
- a conductor shows, so there is nothing to work out again -- and this
- runs while items travel under the mouse, where one full pass over
- every wire of the project would cost more than the movement itself.
- @param part the section which moved
- @param delta how far it came, in scene coordinates
-*/
-void Cable::movePartCores(const QUuid &part, const QPointF &delta)
-{
- if (part.isNull() || delta.isNull()) return;
-
- for (CableCore &core : m_cores)
- {
- if (core.part == part) {
- core.position += delta;
- }
- }
-}
-
-/**
- @brief Cable::toXml
- @param document
- @return the <cable> element holding every part and every wired core
-*/
-QDomElement Cable::toXml(QDomDocument &document) const
-{
- QDomElement root = document.createElement(QLatin1String("cable"));
- root.setAttribute(QLatin1String("uuid"), m_uuid.toString());
- if (!m_designation.isEmpty()) {
- root.setAttribute(QLatin1String("designation"), m_designation);
- }
- //Only a name which was typed in says so: the ones the numbering
- //rule handed out are the ordinary case and need no marking
- if (m_designation_by_hand) {
- root.setAttribute(QLatin1String("designation_by_hand"), QLatin1String("1"));
- }
- if (!m_bmk.isEmpty()) {
- root.setAttribute(QLatin1String("bmk"), m_bmk);
- }
- if (!m_installation.isEmpty()) {
- root.setAttribute(QLatin1String("installation"), m_installation);
- }
- if (!m_location.isEmpty()) {
- root.setAttribute(QLatin1String("location"), m_location);
- }
- if (!m_length.isEmpty()) {
- root.setAttribute(QLatin1String("length"), m_length);
- }
- //Only what was switched off is written: a cable written before
- //these existed shows everything, like the ones written from now
- if (!m_show_designation) {
- root.setAttribute(QLatin1String("show_designation"), QLatin1String("0"));
- }
- if (!m_show_type) {
- root.setAttribute(QLatin1String("show_type"), QLatin1String("0"));
- }
- if (!m_show_installation) {
- root.setAttribute(QLatin1String("show_installation"), QLatin1String("0"));
- }
- if (!m_show_location) {
- root.setAttribute(QLatin1String("show_location"), QLatin1String("0"));
- }
- if (!m_show_length) {
- root.setAttribute(QLatin1String("show_length"), QLatin1String("0"));
- }
- if (!m_type.isEmpty()) {
- root.setAttribute(QLatin1String("type"), m_type);
- }
- if (m_core_count > 0) {
- root.setAttribute(QLatin1String("cores"), QString::number(m_core_count));
- }
-
- if (!m_core_colors.isEmpty())
- {
- QDomElement colors = document.createElement(QLatin1String("colors"));
- for (const QString &color : m_core_colors) {
- QDomElement e = document.createElement(QLatin1String("color"));
- e.appendChild(document.createTextNode(color));
- colors.appendChild(e);
- }
- root.appendChild(colors);
- }
-
- //Only the texts this cable does not write the way the preferences
- //say are stored, so a cable stays silent as long as it merely
- //follows the default -- and a later change of that default still
- //reaches it. The nudges are kept in the same element: they say
- //where those texts stand rather than what the cable is.
- if (!m_texts.isEmpty() || !m_text_offset.isNull() || !m_core_offset.isNull())
- {
- QDomElement texts = document.createElement(QLatin1String("texts"));
- bool stored = false;
- for (auto it = m_texts.cbegin(); it != m_texts.cend(); ++it)
- {
- const CableTextFormat &format = it.value();
- if (format.font.isEmpty() && format.alignment == 0) {
- continue;
- }
- QDomElement e = document.createElement(QLatin1String("text"));
- e.setAttribute(QLatin1String("key"), it.key());
- if (!format.font.isEmpty()) {
- e.setAttribute(QLatin1String("font"), format.font);
- }
- const QString align = alignmentName(format.alignment);
- if (!align.isEmpty()) {
- e.setAttribute(QLatin1String("align"), align);
- }
- texts.appendChild(e);
- stored = true;
- }
- if (!m_text_offset.isNull())
- {
- texts.setAttribute(QLatin1String("text_dx"),
- QString::number(m_text_offset.x(), 'f', 2));
- texts.setAttribute(QLatin1String("text_dy"),
- QString::number(m_text_offset.y(), 'f', 2));
- stored = true;
- }
- if (!m_core_offset.isNull())
- {
- texts.setAttribute(QLatin1String("core_dx"),
- QString::number(m_core_offset.x(), 'f', 2));
- texts.setAttribute(QLatin1String("core_dy"),
- QString::number(m_core_offset.y(), 'f', 2));
- stored = true;
- }
- if (stored) {
- root.appendChild(texts);
- }
- }
-
- for (const CablePartData &part : m_parts)
- {
- QDomElement e = document.createElement(QLatin1String("part"));
- e.setAttribute(QLatin1String("uuid"), part.uuid.toString());
- e.setAttribute(QLatin1String("diagram"), part.diagram.toString());
- e.setAttribute(QLatin1String("x1"), QString::number(part.p1.x(), 'f', 2));
- e.setAttribute(QLatin1String("y1"), QString::number(part.p1.y(), 'f', 2));
- e.setAttribute(QLatin1String("x2"), QString::number(part.p2.x(), 'f', 2));
- e.setAttribute(QLatin1String("y2"), QString::number(part.p2.y(), 'f', 2));
- root.appendChild(e);
- }
-
- for (const CableCore &core : usedCores())
- {
- QDomElement e = document.createElement(QLatin1String("core"));
- e.setAttribute(QLatin1String("index"), QString::number(core.core));
- if (!core.part.isNull()) {
- e.setAttribute(QLatin1String("part"), core.part.toString());
- }
- if (!core.conductor.isNull()) {
- e.setAttribute(QLatin1String("conductor"), core.conductor.toString());
- }
- e.setAttribute(QLatin1String("x"), QString::number(core.position.x(), 'f', 2));
- e.setAttribute(QLatin1String("y"), QString::number(core.position.y(), 'f', 2));
- root.appendChild(e);
- }
-
- return root;
-}
-
-/**
- @brief Cable::fromXml
- @param element a <cable> element
-*/
-void Cable::fromXml(const QDomElement &element)
-{
- m_uuid = QUuid(element.attribute(QLatin1String("uuid")));
- if (m_uuid.isNull()) {
- m_uuid = QUuid::createUuid();
- }
- m_designation = element.attribute(QLatin1String("designation"));
- //A file written before this existed holds names the program
- //gave, which is what not marking them means
- m_designation_by_hand = element.attribute(
- QLatin1String("designation_by_hand")) == QLatin1String("1");
- m_bmk = element.attribute(QLatin1String("bmk"));
- m_installation = element.attribute(QLatin1String("installation"));
- m_location = element.attribute(QLatin1String("location"));
- m_length = element.attribute(QLatin1String("length"));
- m_show_designation = element.attribute(
- QLatin1String("show_designation"), QLatin1String("1")).toInt() != 0;
- m_show_type = element.attribute(
- QLatin1String("show_type"), QLatin1String("1")).toInt() != 0;
- m_show_installation = element.attribute(
- QLatin1String("show_installation"), QLatin1String("1")).toInt() != 0;
- m_show_location = element.attribute(
- QLatin1String("show_location"), QLatin1String("1")).toInt() != 0;
- m_show_length = element.attribute(
- QLatin1String("show_length"), QLatin1String("1")).toInt() != 0;
- m_type = element.attribute(QLatin1String("type"));
- m_core_count = qMax(0, element.attribute(QLatin1String("cores"), QLatin1String("0")).toInt());
-
- m_core_colors.clear();
- const QDomElement colors = element.firstChildElement(QLatin1String("colors"));
- for (QDomElement e = colors.firstChildElement(QLatin1String("color"));
- !e.isNull();
- e = e.nextSiblingElement(QLatin1String("color"))) {
- m_core_colors.append(e.text());
- }
-
- //The texts this cable writes its own way; everything not in here
- //keeps following what the preferences say.
- m_texts.clear();
- m_text_offset = QPointF();
- m_core_offset = QPointF();
- const QDomElement texts = element.firstChildElement(QLatin1String("texts"));
- for (QDomElement e = texts.firstChildElement(QLatin1String("text"));
- !e.isNull();
- e = e.nextSiblingElement(QLatin1String("text")))
- {
- const QString key = e.attribute(QLatin1String("key"));
- if (key.isEmpty()) continue;
- CableTextFormat format;
- format.font = e.attribute(QLatin1String("font"));
- format.alignment = alignmentFromName(e.attribute(QLatin1String("align")));
- if (format.font.isEmpty() && format.alignment == 0) continue;
- m_texts.insert(key, format);
- }
- if (!texts.isNull())
- {
- m_text_offset.setX(texts.attribute(QLatin1String("text_dx")).toDouble());
- m_text_offset.setY(texts.attribute(QLatin1String("text_dy")).toDouble());
- m_core_offset.setX(texts.attribute(QLatin1String("core_dx")).toDouble());
- m_core_offset.setY(texts.attribute(QLatin1String("core_dy")).toDouble());
- }
-
- m_parts.clear();
- for (QDomElement e = element.firstChildElement(QLatin1String("part"));
- !e.isNull();
- e = e.nextSiblingElement(QLatin1String("part")))
- {
- CablePartData part;
- part.uuid = QUuid(e.attribute(QLatin1String("uuid")));
- if (part.uuid.isNull()) {
- continue;
- }
- part.diagram = QUuid(e.attribute(QLatin1String("diagram")));
- part.p1 = QPointF(e.attribute(QLatin1String("x1")).toDouble(),
- e.attribute(QLatin1String("y1")).toDouble());
- part.p2 = QPointF(e.attribute(QLatin1String("x2")).toDouble(),
- e.attribute(QLatin1String("y2")).toDouble());
- m_parts.append(part);
- }
-
- m_cores.clear();
- for (QDomElement e = element.firstChildElement(QLatin1String("core"));
- !e.isNull();
- e = e.nextSiblingElement(QLatin1String("core")))
- {
- CableCore core;
- core.core = e.attribute(QLatin1String("index"), QLatin1String("-1")).toInt();
- if (core.core < 0) {
- continue;
- }
- core.part = QUuid(e.attribute(QLatin1String("part")));
- core.conductor = QUuid(e.attribute(QLatin1String("conductor")));
- core.position = QPointF(e.attribute(QLatin1String("x")).toDouble(),
- e.attribute(QLatin1String("y")).toDouble());
- if (hasCore(core.core)) {
- continue;
- }
- m_cores.append(core);
- }
-}
diff --git a/sources/cable/cable.h b/sources/cable/cable.h
deleted file mode 100644
index 29a0c9d55..000000000
--- a/sources/cable/cable.h
+++ /dev/null
@@ -1,376 +0,0 @@
-/*
- 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 .
-*/
-#ifndef CABLE_H
-#define CABLE_H
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-class QDomDocument;
-class QDomElement;
-
-/**
- @brief One core of a cable which is wired to a conductor of the drawing.
-
- A cable owns its assignment: this is the only place in QElectroTech
- which knows that core number 3 of cable 15W1 lands on such a
- conductor. The conductor itself only carries the text the assignment
- generates (15W1:br), written back by CableManager::refreshLabels(),
- so the text can never drift away from the cable it comes from.
-*/
-struct CableCore
-{
- /// Core number inside the cable, 0 based, from 0 to coreCount()-1
- int core = -1;
- /// The part carrying the slash drawn for this core
- QUuid part;
- /// The conductor this core lands on, null when the core is free
- QUuid conductor;
- /// Where the slash sits, in the coordinates of its own part
- QPointF position;
-
- ///Two assignments are the same when they say the same thing, so
- ///undo can tell a gesture which changed something from one which
- ///merely put the slash back where it already was
- friend bool operator==(const CableCore &a, const CableCore &b)
- {
- return a.core == b.core && a.part == b.part
- && a.conductor == b.conductor && a.position == b.position;
- }
- friend bool operator!=(const CableCore &a, const CableCore &b)
- {
- return !(a == b);
- }
-};
-
-/**
- @brief One drawn section of a cable: the trunk line of one folio.
-
- The geometry is kept as plain data rather than as a pointer to the
- graphics item, because the folio owns the item and deletes it when the
- folio is deleted, while the cable outlives it (it may have another
- part on another folio).
-*/
-struct CablePartData
-{
- QUuid uuid;
- QUuid diagram;
- QPointF p1;
- QPointF p2;
-
- bool isHorizontal() const {return qFuzzyCompare(p1.y(), p2.y());}
-};
-
-/**
- @brief How one of the texts of a cable is written.
-
- Both fields may say nothing at all, and that is the normal case:
- the cable then writes that text the way the preferences offer it
- (QETApp::cableTextsFont() and QETApp::cableCoreFont()), so picking
- another default in the preferences reaches every cable which has
- not been given a font of its own.
-
- The texts of a cable are kept in one hash rather than one pair of
- members each, so that a new text can be added without dragging a
- new field through the whole code. They are named the way the file
- writes them:
- "designation", "type", "installation", "location", "length" for the
- five texts written next to the trunk line, and "cores" for the
- colour labels of the cores -- which have no alignment, being turned
- by a quarter along their own line.
-*/
-struct CableTextFormat
-{
- ///The font, in the stable description QETUtils::fontToString()
- ///writes; empty when this cable follows the preferences
- QString font;
- ///Qt::AlignLeft, Qt::AlignHCenter or Qt::AlignRight; 0 when the
- ///text goes on ending where it always did, before the line
- int alignment = 0;
-
- bool operator==(const CableTextFormat &other) const
- {
- return font == other.font && alignment == other.alignment;
- }
- bool operator!=(const CableTextFormat &other) const
- {
- return !(*this == other);
- }
-};
-
-/**
- @brief Everything about a cable the user may type in or tick, and
- whether what he typed is written on the sheet at all.
-
- It is kept as a whole so that one undo step can put a whole edit
- back, and so the selection panel and the dialog always offer exactly
- the same fields in the same order.
-*/
-struct CableProperties
-{
- ///Name of the cable, e.g. 15W1 -- the head of the label
- QString designation;
- /**
- Where that name comes from: false while the program worked
- it out of the numbering rule, true from the moment it was
- typed in. Nothing shows it and no field edits it -- it
- travels with the fields so that undo puts it back with them,
- and so that numbering the whole project over again knows
- which names were never the rule's to begin with and may be
- left alone.
- */
- bool designation_by_hand = false;
- ///Kind of cable, e.g. H07RN-F 4G1,5
- QString type;
- ///Installation the cable belongs to
- QString installation;
- ///Place the cable runs through
- QString location;
- ///Length, typed in by hand
- QString length;
-
- ///What of that is written next to the trunk line of the drawing
- bool show_designation = true;
- bool show_type = true;
- bool show_installation = true;
- bool show_location = true;
- bool show_length = true;
-
- ///How each text is written -- font and alignment, keyed by the
- ///names listed on CableTextFormat. A text which is not in here
- ///follows what the preferences say, which is where a cable
- ///freshly drawn stands for all of them.
- QHash texts;
-
- ///How far the label at the left of the trunk line is nudged from
- ///where it has always stood. It is a nudge on the drawing only,
- ///and it moves the whole label as one thing: installation, place
- ///and designation over the line, the type and the length under
- ///it -- while the line, the slashes and the wiring keep standing
- ///exactly where they were.
- QPointF text_offset;
- ///How far the row of colours is nudged from the slashes it names.
- ///Again on the drawing only: the slashes, the entries they mark
- ///and every wire behind them stay where they are, and so does the
- ///row itself when the trunk line is moved.
- QPointF core_offset;
-
- bool operator==(const CableProperties &other) const
- {
- return designation == other.designation
- && designation_by_hand == other.designation_by_hand
- && type == other.type
- && installation == other.installation
- && location == other.location
- && length == other.length
- && show_designation == other.show_designation
- && show_type == other.show_type
- && show_installation == other.show_installation
- && show_location == other.show_location
- && show_length == other.show_length
- && texts == other.texts
- && text_offset == other.text_offset
- && core_offset == other.core_offset;
- }
- bool operator!=(const CableProperties &other) const
- {
- return !(*this == other);
- }
-};
-
-/**
- @brief Everything about a cable which its type decides, taken as one
- block so that changing the type is one single step.
-
- The cores go with the type rather than with the text fields: taking a
- type with fewer cores takes those cores off the drawing, and undoing
- has to bring their marks back along with the name and the colours.
-*/
-struct CableTypeState
-{
- ///Kind of cable, e.g. H07RN-F 4G1,5
- QString type;
- ///How many cores that type has
- int core_count = 0;
- ///The colour of each of them, in core order
- QStringList core_colors;
- ///The cores the cable really holds at this moment
- QList cores;
-
- bool operator==(const CableTypeState &other) const
- {
- return type == other.type
- && core_count == other.core_count
- && core_colors == other.core_colors
- && cores == other.cores;
- }
- bool operator!=(const CableTypeState &other) const
- {
- return !(*this == other);
- }
-};
-
-/**
- @brief The Cable class is the project wide object a drawn cable line
- stands for.
-
- It carries what the user fills in -- the type, the number, BMK,
- installation, location -- together with what QElectroTech works out:
- how many cores the type has, which colour each core has, which cores
- are wired to which conductor, and which folios the cable runs on.
-
- Properties belong to the cable, never to one of its parts: editing
- the type or the number anywhere changes it for every part.
-*/
-class Cable : public QObject
-{
- Q_OBJECT
-
- public:
- explicit Cable(QObject *parent = nullptr);
-
- //Identity
- QUuid uuid() const;
- void setUuid(const QUuid &uuid);
-
- //Properties the user fills in
- QString designation() const;
- void setDesignation(const QString &designation);
- /**
- True when the name of this cable was typed in rather
- than worked out of the numbering rule: numbering the
- whole project over asks before it takes such a name.
- */
- bool designationByHand() const;
- void setDesignationByHand(bool by_hand);
- QString bmk() const;
- void setBmk(const QString &bmk);
- QString installation() const;
- void setInstallation(const QString &installation);
- QString location() const;
- void setLocation(const QString &location);
- QString length() const;
- void setLength(const QString &length);
-
- ///Every field of the cable, and what of it is drawn on the sheet
- CableProperties properties() const;
- void setProperties(const CableProperties &properties);
-
- //Type, copied from the cable type file when the cable is created
- QString type() const;
- void setType(const QString &type);
- int coreCount() const;
- void setCoreCount(int count);
- QStringList coreColors() const;
- void setCoreColors(const QStringList &colors);
- ///Everything the type of this cable decides, taken as one block
- CableTypeState typeState() const;
- ///Put back a state taken with typeState(), in one single pass
- void applyTypeState(const CableTypeState &state);
-
- //Cores
- QList usedCores() const;
- bool hasCore(int core) const;
- CableCore core(int core) const;
- void setCore(const CableCore &core);
- ///Write a whole set of assignments in place of another
- void setCores(const QList &cores);
- void removeCore(int core);
- int usedCoreCount() const;
- int freeCoreCount() const;
- ///The lowest core number which is free, -1 when none is
- int firstFreeCore() const;
- ///The colour of a core, as written in the cable type file
- QString colorOfCore(int core) const;
- ///The text a conductor of this core shows, e.g. "15W1:br"
- QString labelOfCore(int core) const;
-
- //Parts
- QList parts() const;
- bool hasPart(const QUuid &part) const;
- CablePartData part(const QUuid &part) const;
- void addPart(const CablePartData &part);
- void removePart(const QUuid &part);
- void setPartGeometry(const QUuid &part, const QPointF &p1, const QPointF &p2);
- void setPartDiagram(const QUuid &part, const QUuid &diagram);
- ///Carry the colour labels of one section along when that
- ///section is moved as a whole (paste, duplicate, a selection
- ///being moved with everything else on it)
- void movePartCores(const QUuid &part, const QPointF &delta);
-
- //Serialisation
- QDomElement toXml(QDomDocument &document) const;
- void fromXml(const QDomElement &element);
-
- signals:
- void changed();
-
- private:
- QUuid m_uuid;
- QString m_designation;
- ///< where that name comes from (see designationByHand)
- bool m_designation_by_hand = false;
- QString m_bmk;
- QString m_installation;
- QString m_location;
- QString m_length;
- QString m_type;
- int m_core_count = 0;
- QStringList m_core_colors;
- QList m_cores;
- QList m_parts;
- ///What the label next to the trunk line is allowed to show
- bool m_show_designation = true;
- bool m_show_type = true;
- bool m_show_installation = true;
- bool m_show_location = true;
- bool m_show_length = true;
- ///The texts this cable writes its own way, by the names
- ///listed on CableTextFormat; everything not in here follows
- ///what the preferences say
- QHash m_texts;
- ///How far the label at the left of the line and the row of
- ///colours are nudged on the drawing: distances nothing else
- ///in the project ever looks at
- QPointF m_text_offset;
- QPointF m_core_offset;
-};
-
-/**
- @brief One core which was taken away from another cable.
-
- A conductor belongs to one cable only, so drawing a second cable
- across wires a first one already holds takes those cores away from
- it. That has to be remembered where it is decided, so that undoing
- the second cable gives the first one its cores back instead of
- leaving those conductors with a blank cable field forever.
-*/
-struct CableCoreTaken
-{
- ///The cable the core was taken from; null when it is gone
- QPointer cable;
- ///The core exactly as the other cable held it
- CableCore core;
-};
-
-#endif // CABLE_H
diff --git a/sources/cable/cablecopy.cpp b/sources/cable/cablecopy.cpp
deleted file mode 100644
index 2ac97c893..000000000
--- a/sources/cable/cablecopy.cpp
+++ /dev/null
@@ -1,454 +0,0 @@
-/*
- 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 .
-*/
-#include "cablecopy.h"
-
-#include "cablemanager.h"
-#include "cablepart.h"
-
-#include "../autoNum/assignvariables.h"
-#include "../autoNum/numerotationcontext.h"
-#include "../diagram.h"
-#include "../qetgraphicsitem/conductor.h"
-#include "../qetproject.h"
-
-#include
-#include
-
-#include
-
-/**
- @brief CableCopy::write
- Puts the cable lines selected on @a diagram into @a document, so a
- copy carries them the way it carries the elements and the texts of
- the same selection.
-
- Only the line which was picked is written, together with the whole
- cable it belongs to: the copy needs everything the cable knows, but
- only the one section it is standing on.
- @param diagram the folio being copied
- @param document the fragment the clipboard is about to hold
-*/
-void CableCopy::write(Diagram *diagram, QDomDocument &document)
-{
- if (!diagram) return;
- QDomElement root = document.documentElement();
- if (root.isNull()) return;
-
- QList parts;
- for (QGraphicsItem *item : diagram->selectedItems()) {
- if (auto *part = qgraphicsitem_cast(item)) {
- parts << part;
- }
- }
- if (parts.isEmpty()) return;
-
- QDomElement block = document.createElement(QStringLiteral("cables"));
- bool anything = false;
- for (CablePart *part : std::as_const(parts))
- {
- Cable *cable = part->cable();
- if (!cable) continue;
-
- QDomElement element = cable->toXml(document);
- const QPointF p1 = part->firstPoint();
- const QPointF p2 = part->secondPoint();
- element.setAttribute(QStringLiteral("line"), part->partUuid().toString());
- element.setAttribute(QStringLiteral("x1"), QString::number(p1.x(), 'f', 2));
- element.setAttribute(QStringLiteral("y1"), QString::number(p1.y(), 'f', 2));
- element.setAttribute(QStringLiteral("x2"), QString::number(p2.x(), 'f', 2));
- element.setAttribute(QStringLiteral("y2"), QString::number(p2.y(), 'f', 2));
- block.appendChild(element);
- anything = true;
- }
- if (anything) root.appendChild(block);
-}
-
-/**
- @brief CableCopy::read
- Builds a cable of its own for every line @a root carries, each of
- them standing exactly where the fragment says it stood.
-
- The cable keeps everything the source cable knows -- type,
- installation, place, length, how many cores, which colours -- and it
- also keeps where the colour labels of the copied section stand, so a
- copy arrives as a whole: onto a folio which holds no wire at all,
- into an empty part of a sheet, wherever. What it does not keep is
- any wire: a copy may not take a wire away from the cable it was
- copied from, not even for as long as it is being built, so every
- core is let go while the cable still answers to the source's
- identity -- no conductor of the source is ever repointed, not even
- for the length of one signal.
-
- Which wire each carried core belongs to is worked out later, in
- CableCopy::wire(), because that depends on where the line has
- finally been put -- and a copy put where there is no wire simply
- keeps its labels, to be wired when the line is pulled into place.
- @param diagram the folio which is about to hold those lines
- @param root the <diagram> element of a copy fragment
- @return the lines built, not yet wired
-*/
-QList CableCopy::read(Diagram *diagram, const QDomElement &root)
-{
- QList built;
- if (!diagram || !diagram->project()) return built;
-
- const QDomElement block = root.firstChildElement(QStringLiteral("cables"));
- if (block.isNull()) return built;
-
- QETProject *project = diagram->project();
- //One rule for the whole run. Every cable of a paste takes a
- //number from it and steps it on, so several cables pasted at
- //once do not all end up under the same number -- although the
- //project's own rule does not move until those cables really
- //exist, which is where the commands which add them take over.
- //A counter bound to the folio starts the folio the copies are
- //pasted onto at its own beginning, once for the whole run.
- NumerotationContext rule = autonum::resetFolioCounters(
- project->cableAutoNum(project->cableCurrentAutoNum()));
- for (QDomElement element = block.firstChildElement(QStringLiteral("cable"));
- !element.isNull();
- element = element.nextSiblingElement(QStringLiteral("cable")))
- {
- if (element.attribute(QStringLiteral("x1")).isEmpty()) continue;
-
- Cable *cable = project->newCable();
- cable->fromXml(element);
-
- //The section of the copy, standing where the section it was
- //copied from stood: the caller moves it to its final place,
- //and the colour labels come with it (CablePart::itemChange)
- CablePartData own;
- own.uuid = QUuid::createUuid();
- own.diagram = diagram->uuid();
- own.p1 = QPointF(element.attribute(QStringLiteral("x1")).toDouble(),
- element.attribute(QStringLiteral("y1")).toDouble());
- own.p2 = QPointF(element.attribute(QStringLiteral("x2")).toDouble(),
- element.attribute(QStringLiteral("y2")).toDouble());
-
- //The colour labels of that one section, carried over whole
- //and with every wire taken away
- const QUuid source_part(element.attribute(QStringLiteral("line")));
- QList carried;
- for (const CableCore &core : cable->usedCores())
- {
- if (core.part != source_part) continue;
-
- CableCore copy = core;
- copy.conductor = QUuid();
- copy.part = own.uuid;
- carried.append(copy);
- }
- //Goes first, while the copy still answers to the source's
- //identity: that way nothing of the source is left pointing
- //at a cable which is about to become somebody else
- cable->setCores(carried);
-
- //The copy is a cable of its own: nothing of the source's
- //identity, nothing of its sections and nothing of the wires
- //it wires may be taken over, only what the user filled in
- for (const CablePartData &other : cable->parts()) {
- cable->removePart(other.uuid);
- }
- cable->setUuid(QUuid::createUuid());
- //The copy is numbered by the rule like every other cable.
- //A project which has no rule gets no number invented for it,
- //and then what the copy was copied from keeps standing there
- //rather than being wiped out by an empty one.
- const QString designation =
- CableManager::nextDesignation(project, diagram, rule);
- if (!designation.isEmpty()) {
- cable->setDesignation(designation);
- //The copy is numbered by the rule, so whatever the
- //source was called by hand is not carried over
- cable->setDesignationByHand(false);
- }
- cable->addPart(own);
-
- auto *item = new CablePart(own);
- item->setCable(cable);
- diagram->addItem(item);
- built << item;
- }
- return built;
-}
-
-/**
- @brief CableCopy::wire
- Give the cores of every line in @a parts the wire each of them
- stands on now that the line is where it is going to stay.
-
- A copy arrives whole: it carries the colour labels it was copied
- with, and this is the moment they are matched against the wires of
- the new place. Matching never moves a label: each one goes on
- standing exactly where it was carried, and the wire it stands on --
- within reach, as everywhere else -- is the one which takes the cable
- field. A label standing on no wire at all simply goes on
- standing there -- that is what makes a copy useful onto a folio
- which holds no wire yet: the line is there, with its colours, ready
- to be pulled into place, and pulling it there wires it
- (CablePart::settleCores).
-
- A piece of wire belongs to one cable only, and a copy never takes
- one away: the wires of the folio it lands on which another cable
- already describes are left to that cable, nothing is asked about
- them and nothing is written for them. The wires which came along
- with the copy itself are different -- they are new wires on this
- folio, carrying the cable field they had back on the folio they
- come from, where they used to be one of that cable's own wires. If
- that were still believed here, every one of them would look like a
- wire another cable holds and the copy would be refused all of them,
- arriving without a single entry although it is standing on wires of
- its own. So they are let go of first, exactly like read() lets go
- of the wires the cores used to name.
- @param diagram the folio the lines are on
- @param parts the lines built by CableCopy::read()
- @param fresh the wires which came with this copy
- @return one entry per line which is going to stay, with what it
- had to give back to other cables, ready to be put on the undo stack
-*/
-QList CableCopy::wire(Diagram *diagram,
- const QList &parts,
- const QList &fresh)
-{
- QList wired;
- if (!diagram || !diagram->project()) {
- for (CablePart *part : parts) discard(diagram, part);
- return wired;
- }
- QETProject *project = diagram->project();
-
- //A wire which came with this copy is a new wire here: what it
- //says about a cable describes the folio it was copied from.
- //Done by identity rather than by position, because the wire it
- //was copied from may well stand in this very place -- and that
- //one goes on belonging to its own cable.
- for (Conductor *conductor : fresh)
- {
- if (!conductor) continue;
- if (conductor->properties().m_cable_uuid.isNull()) continue;
-
-
- conductor->setCableReference(QString(), QUuid(), -1);
- }
-
- for (CablePart *part : parts)
- {
- Cable *cable = part ? part->cable() : nullptr;
- if (!cable) {
- discard(diagram, part);
- continue;
- }
-
- //One wire a colour label is dropped on. Two labels may
- //not be given the same wire, so each one is handed out
- //once.
- struct Offered
- {
- int core;
- Conductor *conductor;
- };
- QList offered;
- QSet wires;
-
- const QLineF line(part->firstPoint(), part->secondPoint());
- const QList crossings = CableManager::crossedConductors(diagram, line);
- for (const CableCore &core : cable->usedCores())
- {
- if (core.part != part->partUuid()) continue;
- if (!core.conductor.isNull()) continue;
-
- //The wire this label stands on, worked out the way it
- //is worked out everywhere else (CablePart::wireFor):
- //the nearest place this line runs across a wire, close
- //enough for the slash and the wire to read as one
- //thing. The label itself is not moved to reach it --
- //a copy arrives with its colours where they were
- //carried, and no gesture moves one of them, so here
- //too the entry follows the label rather than the
- //label being sent off to find a wire. When that wire
- //has already been handed to another label of the same
- //copy, the next nearest one this label stands on wins,
- //rather than leaving it with no wire although one is
- //under it.
- QList open = crossings;
- Conductor *conductor = nullptr;
- while (true)
- {
- Conductor *nearest = part->wireFor(open, core, core.position);
- if (!nearest) break;
- if (!wires.contains(nearest->uuid())) {
- conductor = nearest;
- break;
- }
- for (int i = open.size() - 1; i >= 0; --i) {
- if (open.at(i).conductor == nearest) open.removeAt(i);
- }
- }
-
- if (!conductor) continue;
-
- //A wire another cable already describes stays with that
- //cable. Taking it away would empty a cable behind the
- //user's back, and asking about it on every paste turned
- //putting a copy into an existing drawing into a question
- //to answer first -- so a copy simply takes the wires
- //nobody holds, and its colour labels standing on the
- //others name nothing at all. An entry follows its label,
- //and here it comes to a stop. He can give such a label
- //one of those wires himself afterwards, by dragging it
- //there, which is where the question is still asked.
- const QUuid holder_uuid = conductor->properties().m_cable_uuid;
- if (!holder_uuid.isNull() && holder_uuid != cable->uuid())
- {
- const Cable *previous = CableManager::cableByUuid(project, holder_uuid);
- if (previous
- && previous->hasCore(conductor->properties().m_cable_slot))
- {
- continue;
- }
- }
-
- wires.insert(conductor->uuid());
- Offered item;
- item.core = core.core;
- item.conductor = conductor;
- offered.append(item);
- }
-
- QList bound;
- for (const Offered &item : std::as_const(offered))
- {
- //A wire is one piece of wire: whichever cable held it
- //before has to let it go.
- CableManager::claimConductor(project, item.conductor, cable);
-
- //A cable binds a conductor by its identity, so a
- //conductor read back from a file written before it had a
- //persistent one is given one to keep from now on.
- item.conductor->setUuid(item.conductor->uuid());
-
- CableCore core = cable->core(item.core);
- core.conductor = item.conductor->uuid();
- cable->setCore(core);
-
- Bound bind;
- bind.core = item.core;
- bind.conductor = item.conductor;
- bound.append(bind);
- }
-
- Wired wired_line;
- wired_line.part = part;
- wired_line.bound = bound;
- wired.append(wired_line);
- }
- return wired;
-}
-
-/**
- @brief CableCopy::repoint
- Work out again which wire each core of @a wired is on, using the
- identity the wire has right now.
-
- A copy is put on the undo stack just after it was wired, and the
- first run of that stack renews the identity of every conductor it
- pasted (PasteDiagramCommand::redo): a copy must not keep the
- identity of the conductor it was copied from. The cores of the copy
- would then go on naming an identity which no longer exists, and the
- next working out of the labels (CableManager::refreshLabels) would
- find nothing there and take the cable field away from those wires.
-
- So the cores are pointed at the very same wires again, with their
- new identity, and the cable fields are written out from the cables
- one more time afterwards: what the copy was given as it was put
- down still names the identity its wires had a moment earlier, and
- on a folio where those wires have their own new identity that
- leaves them without an entry although the copy is standing on them.
- Nothing moves and nothing is asked: an untouched copy is not
- changed by any of this at all.
- @param wired the lines CableCopy::wire() just gave wires to
-*/
-void CableCopy::repoint(const QList &wired)
-{
- for (const Wired &line : wired)
- {
- Cable *cable = line.part ? line.part->cable() : nullptr;
- if (!cable) continue;
-
- for (const Bound &bind : line.bound)
- {
- if (!bind.conductor) continue;
-
- const QUuid now = bind.conductor->uuid();
- const CableCore core = cable->core(bind.core);
- if (core.core != bind.core || core.conductor == now) continue;
-
-
- CableCore updated = core;
- updated.conductor = now;
- cable->setCore(updated);
- }
- }
-
- //With every core naming the identity its wire really has now,
- //the cable fields are written out from the cables once more:
- //this is what puts the entries a copy was given as it was put
- //down onto the wires it has actually landed on.
- Diagram *folio = nullptr;
- for (const Wired &line : wired) {
- if (line.part && line.part->diagram()) {
- folio = line.part->diagram();
- break;
- }
- }
- if (folio && folio->project()) {
- CableManager::refreshLabels(folio->project());
- }
-}
-
-/**
- @brief CableCopy::discard
- Take a line away again whole -- off the folio, out of the project,
- and deleted -- without putting anything on the undo stack, because
- nothing really happened: the user said no to the question above.
-
- Whatever the cable said while it was being built must not be left
- behind either: the project has to go on claiming exactly what it
- claimed before, or it would ask to be saved although nothing was
- changed at all.
- @param diagram the folio it was built on
- @param part the line to take away, may be null
-*/
-void CableCopy::discard(Diagram *diagram, CablePart *part)
-{
- if (!part) return;
- Cable *cable = part->cable();
- QETProject *project = diagram ? diagram->project() : nullptr;
-
- if (part->scene()) part->scene()->removeItem(part);
-
- if (cable && project && project->cables().contains(cable))
- {
- const bool options_modified = project->projectOptionsWereModified();
- project->removeCable(cable);
- project->setModified(options_modified);
- }
-
- delete part;
- delete cable;
-}
diff --git a/sources/cable/cablecopy.h b/sources/cable/cablecopy.h
deleted file mode 100644
index 040e7c402..000000000
--- a/sources/cable/cablecopy.h
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- 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 .
-*/
-#ifndef CABLECOPY_H
-#define CABLECOPY_H
-
-#include "cable.h"
-
-#include
-#include
-
-class CablePart;
-class Conductor;
-class Diagram;
-class QDomDocument;
-class QDomElement;
-class QUndoCommand;
-
-/**
- @brief Carrying a drawn cable line over into a copy -- through the
- clipboard (Ctrl+C then Ctrl+V) or as a duplicate (Ctrl+D).
-
- What travels is the line and everything which made the cable what it
- was: its type, its installation, the place it runs through, its
- length, how many cores it has, which colours those have -- and where
- the colour labels of the copied section stand, so a copy arrives as
- a whole even onto a folio which holds no wire at all. What does not
- travel is which wire each core is on: a copy is a cable of its own
- with a number of its own, and it takes the wire each colour label
- happens to stand on at the place it is put -- those of the folio it
- lands on which no other cable describes. A wire another cable
- already holds is left to that cable, and a label put where there is
- no wire goes on standing there, to be wired when the line is pulled
- into place.
-
- A cable line is not one of the folio's items (the cable owns it),
- so Diagram::toXml() never writes it: the block travels beside the
- folio's own XML rather than inside it, and Diagram::fromXml() reads
- it back only when its caller asks to be handed the lines it built.
-*/
-namespace CableCopy
-{
- ///Put the cable lines selected on @a diagram into @a document
- void write(Diagram *diagram, QDomDocument &document);
-
- ///Build new cables of their own for the lines @a root holds,
- ///at the coordinates the fragment carries, with the colour
- ///labels of the copied section carried over as they were.
- ///Nothing is asked and no wire is taken yet: that needs the
- ///place the lines have really been put at, which only the
- ///caller knows
- QList read(Diagram *diagram, const QDomElement &root);
-
- ///One core of a line, and the wire it was given
- struct Bound
- {
- int core = -1;
- Conductor *conductor = nullptr;
- };
-
- ///One line which was built, with what it had to take away
- struct Wired
- {
- QPointer part;
- QList taken;
- ///The wires the colour labels of this line were given,
- ///so they can be pointed at them again afterwards
- QList bound;
- };
-
- ///Give each colour label of a line the wire it stands on now
- ///that the line is where it will stay.
- ///
- ///@a fresh lists the wires which came along with the copy
- ///itself. They are new wires on this folio: the cable field
- ///they carry describes the folio they were copied from, where
- ///they used to be one of that cable's own wires -- and carried
- ///over, that reference would make every one of them look like
- ///a wire some other cable holds, so the copy would be refused
- ///all of them and arrive without a single entry although it is
- ///standing on wires of its own. They are let go of first, the
- ///same way read() lets go of the wires the cores used to name.
- ///
- ///A wire the folio already held, which another cable
- ///describes, is never taken away from that cable: nothing is
- ///asked and nothing is written for it, and the colour label
- ///standing on it simply names nothing until the user drags it
- ///onto a free wire himself, which is where the question is
- ///still asked. A label standing on no wire at all goes on
- ///standing where it is, to be wired when the line is pulled
- ///into place.
- QList wire(Diagram *diagram,
- const QList &parts,
- const QList &fresh);
-
- ///Point the cores @a wired lists at the wires they were given,
- ///again: putting the copy on the undo stack may have renewed
- ///the identity of a pasted conductor (PasteDiagramCommand::redo),
- ///and a cable binds a wire by its identity. Nothing is asked and
- ///nothing moves here. The cable fields are written out once more
- ///afterwards, from the cables themselves, so the entries a copy
- ///was given as it was put down are on the wires it really
- ///landed on rather than on the identity they had one moment
- ///earlier.
- void repoint(const QList &wired);
-
- ///Take a built line back off the folio without leaving a trace
- ///in the undo history, for a copy which was refused
- void discard(Diagram *diagram, CablePart *part);
-}
-
-#endif // CABLECOPY_H
diff --git a/sources/cable/cablecreatedialog.cpp b/sources/cable/cablecreatedialog.cpp
deleted file mode 100644
index 5b098ff68..000000000
--- a/sources/cable/cablecreatedialog.cpp
+++ /dev/null
@@ -1,1407 +0,0 @@
-/*
- 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 .
-*/
-#include "cablecreatedialog.h"
-
-#include "editcablecommand.h"
-#include "../diagram.h"
-#include "../qetgraphicsitem/conductor.h"
-#include "../qetproject.h"
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-
-/**
- @brief CableFilterProxy::CableFilterProxy
- @param parent
-*/
-CableFilterProxy::CableFilterProxy(QObject *parent) :
- QSortFilterProxyModel(parent)
-{
-}
-
-/**
- @brief CableFilterProxy::setTokens
- @param tokens every word the user typed, all of which have to be found
-*/
-void CableFilterProxy::setTokens(const QStringList &tokens)
-{
- if (m_tokens == tokens) return;
- m_tokens = tokens;
- beginFilterChange();
- endFilterChange(QSortFilterProxyModel::Direction::Rows);
-}
-
-/**
- @brief CableFilterProxy::setHideUnsuitable
- @param hide true to keep the rows which are of no use out of sight,
- false to show them -- struck through while a line was just drawn,
- plain while a type is being changed, where the loss was explained
- instead of being refused
-*/
-void CableFilterProxy::setHideUnsuitable(bool hide)
-{
- if (m_hide_unsuitable == hide) return;
- m_hide_unsuitable = hide;
- beginFilterChange();
- endFilterChange(QSortFilterProxyModel::Direction::Rows);
-}
-
-/**
- @brief CableFilterProxy::filterAcceptsRow
- @param source_row
- @param source_parent
- @return
-*/
-bool CableFilterProxy::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
-{
- const QModelIndex first = sourceModel()->index(source_row, 0, source_parent);
-
- if (m_hide_unsuitable && first.data(UnsuitableRole).toBool()) {
- return false;
- }
- if (m_tokens.isEmpty()) {
- return true;
- }
-
- const int columns = sourceModel()->columnCount();
- for (const QString &token : m_tokens)
- {
- bool found = false;
- for (int column = 0; column < columns; ++column)
- {
- const QString value = sourceModel()->index(source_row, column, source_parent)
- .data(Qt::DisplayRole).toString();
- if (value.contains(token, Qt::CaseInsensitive))
- {
- found = true;
- break;
- }
- }
- if (!found) {
- return false;
- }
- }
- return true;
-}
-
-/**
- @brief CableCreateDialog::CableCreateDialog
- @param diagram the folio the line was drawn on
- @param crossings the conductors that line runs across, in drawing order
- @param part the section which will carry the trunk line and its slashes
- @param parent
-*/
-CableCreateDialog::CableCreateDialog(Diagram *diagram,
- const QList &crossings,
- const CablePartData &part,
- QWidget *parent) :
- QDialog(parent),
- m_diagram(diagram),
- m_crossings(crossings),
- m_part(part)
-{
- build();
-}
-
-/**
- @brief CableCreateDialog::CableCreateDialog
- @param diagram the folio whose undo stack takes the change
- @param cable the cable whose type is being changed
- @param parent
-*/
-CableCreateDialog::CableCreateDialog(Diagram *diagram,
- Cable *cable,
- QWidget *parent) :
- QDialog(parent),
- m_diagram(diagram),
- m_cable(cable),
- m_change(cable)
-{
- build();
-}
-
-/**
- @brief CableCreateDialog::build
- Everything the two modes share: the header saying what this window
- is about, the tabs, the property fields and the buttons. What the
- header says and which tabs are built is where they part company --
- a window which settles a line just drawn knows how many conductors
- that line crossed, one which changes a cable knows how many cores
- that cable has.
-*/
-void CableCreateDialog::build()
-{
- setWindowTitle(tr("Cable"));
-
- auto *layout = new QVBoxLayout(this);
-
- if (changeMode())
- {
- //What this window is for, and how many cores the cable has
- //right now: the type he picks is measured against that number.
- const QString name = m_change->type().isEmpty()
- ? tr("(no type)") : m_change->type();
- m_header = new QLabel(tr("Change the type: %1, %n core(s)",
- "which type this window changes, and how many cores that cable has now",
- m_change->coreCount()).arg(name), this);
- }
- else
- {
- m_header = new QLabel(tr("%n recognized core(s)",
- "how many cores the line just crossed",
- m_crossings.size()), this);
- }
- QFont header_font = m_header->font();
- header_font.setBold(true);
- m_header->setFont(header_font);
- layout->addWidget(m_header);
-
- m_tabs = new QTabWidget(this);
- layout->addWidget(m_tabs);
-
- buildNewTab();
- //A window changing an existing cable has no line just drawn, so
- //there is no other cable to hand that line over to: the second
- //tab would only offer cables this one is to be replaced by,
- //which is not what changing a type means.
- if (!changeMode()) {
- buildExistingTab();
- }
- buildPropertyRow();
- layout->addWidget(m_property_row);
-
- m_buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
- connect(m_buttons, &QDialogButtonBox::accepted, this, &CableCreateDialog::accept);
- connect(m_buttons, &QDialogButtonBox::rejected, this, &CableCreateDialog::reject);
- layout->addWidget(m_buttons);
-
- connect(m_tabs, &QTabWidget::currentChanged, this, [this]() {selectionChanged();});
-
- loadTypeFile();
- refreshExisting();
- selectionChanged();
-
- resize(780, 540);
-}
-
-/**
- @brief CableCreateDialog::cell
- One read only cell of one of the two tables. A row which cannot be
- used is drawn struck through and greyed out, so the checkbox which
- keeps such rows visible never leaves the user to guess why they are
- there.
- @param value
- @param unsuitable
- @return
-*/
-QStandardItem *CableCreateDialog::cell(const QString &value, bool unsuitable) const
-{
- auto *item = new QStandardItem(value);
- item->setEditable(false);
- if (unsuitable)
- {
- QFont font = item->font();
- font.setStrikeOut(true);
- item->setFont(font);
- item->setForeground(QBrush(palette().color(QPalette::Disabled, QPalette::Text)));
- }
- return item;
-}
-
-/**
- @brief CableCreateDialog::buildNewTab
- The tab choosing the type of a brand new cable, read from the cable
- type file.
-*/
-void CableCreateDialog::buildNewTab()
-{
- auto *page = new QWidget(m_tabs);
- auto *layout = new QVBoxLayout(page);
- layout->setContentsMargins(4, 4, 4, 4);
-
- auto *search_row = new QHBoxLayout;
- search_row->addWidget(new QLabel(tr("Search:"), page));
- m_new_search = new QLineEdit(page);
- m_new_search->setClearButtonEnabled(true);
- m_new_search->setPlaceholderText(tr("Filter cable types"));
- search_row->addWidget(m_new_search, 1);
- layout->addLayout(search_row);
-
- //What the checkbox hides is what cannot be taken: too few cores
- //to carry the line which was just drawn, and -- when a type is
- //being changed rather than drawn -- too few cores to keep this
- //cable as it stands, since taking such a type takes cores off it.
- m_new_hide = new QCheckBox(changeMode()
- ? tr("Hide types which have fewer cores "
- "than this cable")
- : tr("Hide types which have fewer cores "
- "than recognized conductors"), page);
- m_new_hide->setChecked(true);
- layout->addWidget(m_new_hide);
-
- //Said up front rather than only when a struck through row is
- //picked: the user sees the lines crossed out the moment the
- //checkbox shows them, and asks why straight away.
- auto *why = new QLabel(changeMode()
- ? tr("Uncheck to also see the types which have "
- "fewer cores: taking them makes the cores "
- "beyond their number leave the cable.")
- : tr("A struck through line is a type which has "
- "fewer cores than recognized conductors: it "
- "cannot carry this cable."), page);
- why->setWordWrap(true);
- layout->addWidget(why);
-
- m_new_model = new QStandardItemModel(this);
- m_new_proxy = new CableFilterProxy(this);
- m_new_proxy->setSourceModel(m_new_model);
-
- m_new_table = new QTableView(page);
- m_new_table->setModel(m_new_proxy);
- m_new_table->setSelectionBehavior(QAbstractItemView::SelectRows);
- m_new_table->setSelectionMode(QAbstractItemView::SingleSelection);
- m_new_table->setEditTriggers(QAbstractItemView::NoEditTriggers);
- m_new_table->setAlternatingRowColors(true);
- m_new_table->setSortingEnabled(true);
- m_new_table->horizontalHeader()->setStretchLastSection(true);
- m_new_table->verticalHeader()->setVisible(false);
- layout->addWidget(m_new_table, 1);
-
- m_new_note = new QLabel(page);
- m_new_note->setWordWrap(true);
- m_new_note->setVisible(false);
- layout->addWidget(m_new_note);
-
- //A new line for the cable type file, filled in the way the
- //material window fills in one of its own articles: he adds the
- //type right here rather than leaving the window to find out how.
- //Beside it, the button rewriting the line he picked -- which
- //stays out of reach until a line really is picked, since there
- //would then be nothing to rewrite.
- auto *add_row = new QHBoxLayout;
- add_row->addStretch(1);
- auto *add_button = new QPushButton(tr("New entry"), page);
- add_button->setToolTip(tr("Add a new cable type to the list"));
- connect(add_button, &QPushButton::clicked,
- this, &CableCreateDialog::addTypeRecord);
- add_row->addWidget(add_button);
-
- m_edit_button = new QPushButton(tr("Edit entry"), page);
- m_edit_button->setToolTip(tr("Edit the cable type selected in "
- "the list."));
- m_edit_button->setEnabled(false);
- connect(m_edit_button, &QPushButton::clicked,
- this, &CableCreateDialog::editTypeRecord);
- add_row->addWidget(m_edit_button);
-
- layout->addLayout(add_row);
-
- m_tabs->addTab(page, tr("New cable"));
-
- connect(m_new_search, &QLineEdit::textChanged, this, [this](const QString &text) {
- m_new_proxy->setTokens(text.split(QLatin1Char(' '), Qt::SkipEmptyParts));
- });
- connect(m_new_hide, &QCheckBox::toggled, m_new_proxy, &CableFilterProxy::setHideUnsuitable);
- connect(m_new_table->selectionModel(), &QItemSelectionModel::currentChanged,
- this, [this]() {selectionChanged();});
- //A double click settles the choice, exactly the way clicking
- //OK would: he has come to pick that cable, and making him press
- //OK after every pick is one click too many. A row which cannot
- //carry this cable goes on being refused the same way it is
- //refused by that button, greyed out as it is.
- connect(m_new_table, &QTableView::doubleClicked, this, [this]() {
- if (m_buttons->button(QDialogButtonBox::Ok)->isEnabled()) {
- accept();
- }
- });
-}
-
-/**
- @brief CableCreateDialog::buildExistingTab
- The tab giving more cores to a cable the project already holds. That
- cable keeps its type, its designation, its installation and its
- location: choosing it is what inherits them.
-*/
-void CableCreateDialog::buildExistingTab()
-{
- auto *page = new QWidget(m_tabs);
- auto *layout = new QVBoxLayout(page);
- layout->setContentsMargins(4, 4, 4, 4);
-
- auto *search_row = new QHBoxLayout;
- search_row->addWidget(new QLabel(tr("Search:"), page));
- m_existing_search = new QLineEdit(page);
- m_existing_search->setClearButtonEnabled(true);
- m_existing_search->setPlaceholderText(tr("Filter existing cables"));
- search_row->addWidget(m_existing_search, 1);
- layout->addLayout(search_row);
-
- m_existing_hide = new QCheckBox(tr("Hide cables which have fewer "
- "free cores than recognized conductors"), page);
- m_existing_hide->setChecked(true);
- layout->addWidget(m_existing_hide);
-
- //Same as on the first tab: the reason a line is crossed out is
- //said where the crossed out lines appear, not only afterwards.
- auto *why = new QLabel(tr("A struck through line is a cable which has "
- "fewer free cores than recognized "
- "conductors: it cannot receive them."), page);
- why->setWordWrap(true);
- layout->addWidget(why);
-
- m_existing_model = new QStandardItemModel(this);
- m_existing_proxy = new CableFilterProxy(this);
- m_existing_proxy->setSourceModel(m_existing_model);
-
- m_existing_table = new QTableView(page);
- m_existing_table->setModel(m_existing_proxy);
- m_existing_table->setSelectionBehavior(QAbstractItemView::SelectRows);
- m_existing_table->setSelectionMode(QAbstractItemView::SingleSelection);
- m_existing_table->setEditTriggers(QAbstractItemView::NoEditTriggers);
- m_existing_table->setAlternatingRowColors(true);
- m_existing_table->setSortingEnabled(true);
- m_existing_table->horizontalHeader()->setStretchLastSection(true);
- m_existing_table->verticalHeader()->setVisible(false);
- layout->addWidget(m_existing_table, 1);
-
- m_existing_note = new QLabel(page);
- m_existing_note->setWordWrap(true);
- m_existing_note->setVisible(false);
- layout->addWidget(m_existing_note);
-
- m_tabs->addTab(page, tr("Existing cables"));
-
- connect(m_existing_search, &QLineEdit::textChanged, this, [this](const QString &text) {
- m_existing_proxy->setTokens(text.split(QLatin1Char(' '), Qt::SkipEmptyParts));
- });
- connect(m_existing_hide, &QCheckBox::toggled, m_existing_proxy, &CableFilterProxy::setHideUnsuitable);
- connect(m_existing_table->selectionModel(), &QItemSelectionModel::currentChanged,
- this, [this]() {selectionChanged();});
- //A double click settles the choice here too: the cable goes
- //straight onto the line he has just drawn, without a stop at
- //the OK button on the way -- and a cable with no room left for
- //these cores goes on being refused, since that button is greyed
- //out for it.
- connect(m_existing_table, &QTableView::doubleClicked, this, [this]() {
- if (m_buttons->button(QDialogButtonBox::Ok)->isEnabled()) {
- accept();
- }
- });
-}
-
-/**
- @brief CableCreateDialog::buildPropertyRow
- The properties of the cable itself, which belong to it rather than to
- one of its parts: whatever is typed here applies to the whole cable.
-
- There is one single field for the number of the cable, not one per
- name it goes by: the Désignation is the tag of the cable -- the thing
- one is tempted to call its BMK -- and it is what gets drawn next to
- the line and written into the conductors.
-*/
-void CableCreateDialog::buildPropertyRow()
-{
- m_property_row = new QWidget(this);
- auto *form = new QFormLayout(m_property_row);
- form->setContentsMargins(0, 0, 0, 0);
-
- m_designation = new QLineEdit(m_property_row);
- m_installation = new QLineEdit(m_property_row);
- m_location = new QLineEdit(m_property_row);
-
- form->addRow(tr("Designation"), m_designation);
- form->addRow(tr("Plant (=)"), m_installation);
- form->addRow(tr("Location (+)"), m_location);
-}
-
-/**
- @brief CableCreateDialog::loadTypeFile
- Read the cable type file the same way the material file is read, and
- fill the first tab with it. A file which is not there yet simply
- leaves the tab empty and says so.
-*/
-void CableCreateDialog::loadTypeFile()
-{
- m_new_model->clear();
-
- QString path = CableTypeList::configuredPath();
- if (path.isEmpty()) {
- path = CableTypeList::defaultPath();
- }
- m_types = CableTypeListData();
- CableTypeList::load(path, &m_types);
-
- m_new_model->setHorizontalHeaderLabels(CableTypeList::translatedHeader(m_types.columns));
-
- const int needed = neededCores();
- int current_row = -1;
- for (const CableTypeRecord &record : std::as_const(m_types.records))
- {
- const int cores = CableTypeList::coreCount(record);
- //A type saying how many cores it has, but fewer than the line
- //crosses, cannot carry the cable: too few cores were chosen.
- //When a type is being changed, such a row is not refused --
- //he may well want a cable with fewer cores -- but it is the
- //one the checkbox hides, and the one which has something to
- //say about what it takes off the cable.
- const bool fewer = cores > 0 && cores < needed;
- const bool unsuitable = fewer && !changeMode();
- if (changeMode() && !m_change->type().isEmpty()
- && CableTypeList::designation(record) == m_change->type()) {
- current_row = m_types.records.indexOf(record);
- }
-
- QList items;
- items.reserve(m_types.columns.size());
- for (const QString &column : std::as_const(m_types.columns))
- {
- items.append(cell(record.value(column), unsuitable));
- }
- if (!items.isEmpty())
- {
- items.first()->setData(fewer, CableFilterProxy::UnsuitableRole);
- m_new_model->appendRow(items);
- }
- }
-
- //The type the cable already has is the one he came here to
- //change, so it is the one standing under his finger from the
- //start -- and OK is enabled with something to say yes to.
- if (current_row >= 0 && m_new_proxy)
- {
- const QModelIndex source = m_new_model->index(current_row, 0);
- m_new_table->setCurrentIndex(m_new_proxy->mapFromSource(source));
- m_new_table->scrollTo(m_new_table->currentIndex());
- }
-}
-
-/**
- @brief CableCreateDialog::refreshExisting
- Fill the second tab with the cables the project already holds,
- saying how many of their cores are wired and how many are left.
-*/
-void CableCreateDialog::refreshExisting()
-{
- //No second tab when a type is being changed, so nothing to fill
- //-- and nothing was built which could be filled either.
- if (changeMode()) return;
-
- m_existing_model->clear();
- m_existing.clear();
-
- if (!m_diagram || !m_diagram->project()) return;
-
- m_existing_model->setHorizontalHeaderLabels({
- tr("Designation"),
- tr("Type"),
- tr("Cores"),
- tr("Plant"),
- tr("Location")
- });
-
- const int needed = m_crossings.size();
- for (Cable *cable : m_diagram->project()->cables())
- {
- if (!cable) continue;
-
- const bool unsuitable = cable->freeCoreCount() < needed;
- QList items;
- items << cell(cable->designation(), unsuitable)
- << cell(cable->type(), unsuitable)
- << cell(tr("%1 / %2")
- .arg(cable->usedCoreCount())
- .arg(cable->coreCount()), unsuitable)
- << cell(cable->installation(), unsuitable)
- << cell(cable->location(), unsuitable);
- items.first()->setData(unsuitable, CableFilterProxy::UnsuitableRole);
-
- m_existing_model->appendRow(items);
- m_existing.append(cable);
- }
-}
-
-/**
- @brief CableCreateDialog::currentSourceIndex
- @return the row currently chosen on whichever tab is in front, in the
- coordinates of the model behind the filter
-*/
-QModelIndex CableCreateDialog::currentSourceIndex() const
-{
- const bool on_new_tab = m_tabs->currentIndex() == 0;
- QTableView *table = on_new_tab ? m_new_table : m_existing_table;
- CableFilterProxy *proxy = on_new_tab ? m_new_proxy : m_existing_proxy;
-
- const QModelIndex proxy_index = table->currentIndex();
- if (!proxy_index.isValid()) return QModelIndex();
- return proxy->mapToSource(proxy_index);
-}
-
-/**
- @brief CableCreateDialog::currentSelectionIsUnsuitable
- @return true when the chosen row is one the filter only shows when
- the checkbox is off, that is one which cannot carry this cable
-*/
-bool CableCreateDialog::currentSelectionIsUnsuitable() const
-{
- const QModelIndex index = currentSourceIndex();
- if (!index.isValid()) return false;
-
- const bool on_new_tab = m_tabs->currentIndex() == 0;
- CableFilterProxy *proxy = on_new_tab ? m_new_proxy : m_existing_proxy;
- return proxy->index(index.row(), 0).data(CableFilterProxy::UnsuitableRole).toBool();
-}
-
-/**
- @brief CableCreateDialog::unsuitableNote
- @return what has to be said about the struck through row which was
- chosen, so the user learns why it cannot be taken
-*/
-QString CableCreateDialog::unsuitableNote() const
-{
- const int needed = neededCores();
- const QModelIndex index = currentSourceIndex();
- if (!index.isValid()) return QString();
-
- if (m_tabs->currentIndex() == 0)
- {
- const int cores = CableTypeList::coreCount(m_types.records.at(index.row()));
- //Changing a type may well take a cable with fewer cores:
- //nothing is refused, but what that costs has to be said out
- //loud before he says yes to it.
- if (changeMode())
- {
- return tr("This type has fewer cores than this cable: %1 instead of %2. "
- "The cores beyond %1 leave the cable and their marks "
- "disappear from the drawing.")
- .arg(cores)
- .arg(needed);
- }
-
- return tr("Too many cores selected: the type offers only %1, "
- "%2 conductors are recognized")
- .arg(cores)
- .arg(needed);
- }
-
- const Cable *cable = m_existing.value(index.row());
- return tr("Only %1 free cores, %2 requested")
- .arg(cable ? cable->freeCoreCount() : 0)
- .arg(needed);
-}
-
-/**
- @brief CableCreateDialog::selectionChanged
- Show the reason a struck through row cannot be taken, keep the OK
- button in step with it, and fill the property fields from whatever is
- chosen -- an existing cable hands its properties over, a new one gets
- its number worked out.
-*/
-void CableCreateDialog::selectionChanged()
-{
- for (QLabel *note : {m_new_note, m_existing_note})
- {
- if (!note) continue;
- note->clear();
- note->hide();
- }
-
- const bool on_new_tab = m_tabs->currentIndex() == 0;
- QLabel *note = on_new_tab ? m_new_note : m_existing_note;
- const QModelIndex index = currentSourceIndex();
-
- bool acceptable = index.isValid();
-
- if (on_new_tab && m_types.columns.isEmpty())
- {
- note->setText(tr("No cable type available: configure the cable "
- "type file in the preferences."));
- note->show();
- acceptable = false;
- }
- else if (acceptable && currentSelectionIsUnsuitable())
- {
- note->setText(unsuitableNote());
- note->show();
- //Changing a type may well take one with fewer cores: what
- //that costs is said out loud here, and he may still take it.
- acceptable = !changeMode();
- }
- else if (!acceptable)
- {
- note->setText(on_new_tab ? tr("Choose a cable type.")
- : tr("Choose an existing cable."));
- note->show();
- }
-
- //A type with more cores than this cable holds now loses nothing:
- //the extra ones wait in the list of cores which are not placed
- //yet, and he brings them in one by one.
- if (acceptable && on_new_tab && changeMode() && index.isValid()
- && index.row() < m_types.records.size())
- {
- const int cores = CableTypeList::coreCount(m_types.records.at(index.row()));
- if (cores > m_change->coreCount())
- {
- note->setText(tr("%1 cores instead of %2: the extra cores "
- "stay unplaced.")
- .arg(cores)
- .arg(m_change->coreCount()));
- note->show();
- }
- }
-
- m_buttons->button(QDialogButtonBox::Ok)->setEnabled(acceptable);
-
- //The button rewriting a line of the file works on the line he
- //picked and on nothing else: greyed out while his finger has
- //chosen no line yet, and greyed out on the tab holding the
- //cables of the project, which are not lines of that file.
- if (m_edit_button)
- {
- m_edit_button->setEnabled(on_new_tab && index.isValid()
- && index.row() < m_types.records.size());
- }
-
- if (!acceptable) return;
-
- if (on_new_tab)
- {
- if (changeMode())
- {
- //The cable already has its number, its installation and
- //its place: those are what it holds, not something this
- //window works out again.
- fillDesignation(m_change->designation(),
- m_change->designationByHand());
- m_installation->setText(m_change->installation());
- m_location->setText(m_change->location());
- return;
- }
-
- fillDesignation(m_diagram
- ? CableManager::nextDesignation(m_diagram->project(), m_diagram.data())
- : QString(),
- false);
- m_installation->clear();
- m_location->clear();
- return;
- }
-
- if (const Cable *cable = m_existing.value(index.row()))
- {
- fillDesignation(cable->designation(), cable->designationByHand());
- m_installation->setText(cable->installation());
- m_location->setText(cable->location());
- }
-}
-
-/**
- @brief CableCreateDialog::fillDesignation
- Put a name into the field and keep hold of what this window put
- there: whatever he types over it is a name he chose rather than one
- the program handed out.
- @param text the name to show
- @param by_hand whether that name was typed in by him
-*/
-void CableCreateDialog::fillDesignation(const QString &text, bool by_hand)
-{
- m_designation->setText(text);
- m_designation_prefill = text;
- m_designation_prefill_by_hand = by_hand;
-}
-
-/**
- @brief CableCreateDialog::nextFreeSlots
- @param count how many cores are needed
- @return the lowest core numbers no conductor shows yet
-*/
-QList CableCreateDialog::nextFreeSlots(int count) const
-{
- QList core_slots;
- if (!m_cable) return core_slots;
-
- const int total = m_cable->coreCount();
- for (int core = 0; core < total && core_slots.size() < count; ++core)
- {
- if (!m_cable->hasCore(core)) {
- core_slots.append(core);
- }
- }
- return core_slots;
-}
-
-/**
- @brief CableCreateDialog::assignCrossings
- Give the recognised conductors to the cable, one after the other
- along the line, so each core takes the colour the type lists at its
- place. A conductor another cable was holding is taken away from it:
- a piece of wire belongs to one cable only.
- @param slots the core numbers being handed out, in drawing order
-*/
-void CableCreateDialog::assignCrossings(const QList &core_slots)
-{
- if (!m_cable || !m_diagram || !m_diagram->project()) return;
- QETProject *project = m_diagram->project();
-
- int position = 0;
- for (const CableCrossing &crossing : std::as_const(m_crossings))
- {
- if (position >= core_slots.size()) break;
-
- Conductor *conductor = crossing.conductor;
- if (!conductor) continue;
-
- //Already one of this cable's cores: it keeps the core it has,
- //drawing the same cable twice must not give it two.
- if (conductor->properties().m_cable_uuid == m_cable->uuid())
- {
- ++position;
- continue;
- }
-
- CableManager::claimConductor(project, conductor, m_cable);
-
- //A cable binds a conductor by its identity, so a conductor
- //read back from a file written before it had a persistent one
- //is given one to keep from now on.
- conductor->setUuid(conductor->uuid());
-
- CableCore core;
- core.core = core_slots.at(position);
- core.part = m_part.uuid;
- core.conductor = conductor->uuid();
- core.position = crossing.position;
- m_cable->setCore(core);
- ++position;
- }
-}
-
-/**
- @brief CableCreateDialog::coresTakenFromOthers
- Which cores of other cables the wires this line crosses already
- belong to.
-
- A conductor is one piece of wire, so handing it to the cable being
- drawn takes that core away from whichever cable held it before. The
- list is worked out before anything is changed, both to be able to
- ask the user first and to be able to give those cores back when he
- undoes the line.
- @param target the cable about to claim those conductors; a null one
- is a cable which does not exist yet, and then every core another
- cable holds is one which would be taken away
- @return one entry per core which is about to change hands
-*/
-QList CableCreateDialog::coresTakenFromOthers(const QUuid &target) const
-{
- QList taken;
- if (!m_diagram || !m_diagram->project()) return taken;
-
- QETProject *project = m_diagram->project();
- for (const CableCrossing &crossing : std::as_const(m_crossings))
- {
- Conductor *conductor = crossing.conductor;
- if (!conductor) continue;
-
- const auto properties = conductor->properties();
- if (properties.m_cable_uuid.isNull()
- || properties.m_cable_uuid == target) {
- continue;
- }
-
- Cable *previous = CableManager::cableByUuid(project, properties.m_cable_uuid);
- if (!previous || !previous->hasCore(properties.m_cable_slot)) continue;
-
- CableCoreTaken item;
- item.cable = previous;
- item.core = previous->core(properties.m_cable_slot);
- taken.append(item);
- }
- return taken;
-}
-
-/**
- @brief CableCreateDialog::accept
- Settle the cable the drawing gets: a new one from the chosen type, or
- the chosen existing one, filled with the recognised conductors.
-
- Nothing is written before the user has been asked about the cores
- other cables are about to lose: saying yes to that question has to be
- the last thing he does before the change really happens, so that
- saying no leaves the project exactly as it was.
-*/
-void CableCreateDialog::accept()
-{
- //Changing the type of a cable which already exists has nothing
- //to do with conductors a line crosses: it takes the chosen type
- //onto that cable, and that alone.
- if (changeMode())
- {
- acceptTypeChange();
- return;
- }
-
- if (!m_diagram || !m_diagram->project()) {
- QDialog::reject();
- return;
- }
- QETProject *project = m_diagram->project();
- const int needed = m_crossings.size();
- QList core_slots;
-
- const QModelIndex index = currentSourceIndex();
- if (!index.isValid() || currentSelectionIsUnsuitable()) return;
-
- Cable *chosen = nullptr;
- if (m_tabs->currentIndex() == 0)
- {
- if (index.row() >= m_types.records.size()) return;
- const CableTypeRecord record = m_types.records.at(index.row());
- const int cores = CableTypeList::coreCount(record);
- if (cores > 0 && cores < needed) return;
- }
- else
- {
- chosen = m_existing.value(index.row());
- if (!chosen) return;
- if (chosen->freeCoreCount() < needed) return;
- }
-
- //The wires this line crosses which another cable already holds
- //are about to change hands, and the drawing already shows that
- //as the label of the first cable vanishing: better to say so
- //here than to let the user discover it afterwards.
- m_taken = coresTakenFromOthers(chosen ? chosen->uuid() : QUuid());
- if (!m_taken.isEmpty())
- {
- const QString text = tr(
- "%n crossed conductor(s) already bound to another cable\n\n"
- "If you confirm, the other cable will lose these cores. Do you want to continue?",
- "ask before taking cores away from another cable",
- m_taken.size());
-
- if (QMessageBox::warning(this, tr("Cable"), text,
- QMessageBox::Yes | QMessageBox::No,
- QMessageBox::No) != QMessageBox::Yes) {
- m_taken.clear();
- return;
- }
- }
-
- if (m_tabs->currentIndex() == 0)
- {
- const CableTypeRecord record = m_types.records.at(index.row());
- const int cores = CableTypeList::coreCount(record);
-
- m_cable = project->newCable();
- m_cable->setType(CableTypeList::designation(record));
- //A type saying how many cores it has keeps its own count, so
- //"5 of 8" stays true; a type saying nothing gets the number of
- //conductors which were recognised, since that is all we know.
- m_cable->setCoreCount(qMax(cores, needed));
- m_cable->setCoreColors(CableTypeList::coreColors(record));
- m_cable->setDesignation(CableManager::nextDesignation(project, m_diagram.data()));
- m_cable->addPart(m_part);
- core_slots = nextFreeSlots(needed);
- }
- else
- {
- m_cable = chosen;
- if (!m_cable->hasPart(m_part.uuid)) {
- m_cable->addPart(m_part);
- }
- core_slots = nextFreeSlots(needed);
- }
-
- m_cable->setDesignation(m_designation->text().trimmed());
- //A name this window filled in is the program's, a name he typed
- //over it is his own -- and it stays his own when he puts the
- //very same name back into the field
- m_cable->setDesignationByHand(
- m_designation->text().trimmed() != m_designation_prefill
- || m_designation_prefill_by_hand);
- m_cable->setInstallation(m_installation->text().trimmed());
- m_cable->setLocation(m_location->text().trimmed());
-
- assignCrossings(core_slots);
-
- QDialog::accept();
-}
-
-/**
- @brief CableCreateDialog::neededCores
- @return how many cores whatever he picks has to hold at least: the
- conductors the line just crossed, or -- when a type is being changed
- -- the cores this cable holds right now
-*/
-int CableCreateDialog::neededCores() const
-{
- return changeMode() ? m_change->coreCount() : m_crossings.size();
-}
-
-/**
- @brief CableCreateDialog::typeFilePath
- @return where the cable type file stands, read the same way the type
- tab reads it
-*/
-QString CableCreateDialog::typeFilePath() const
-{
- QString path = CableTypeList::configuredPath();
- if (path.isEmpty()) {
- path = CableTypeList::defaultPath();
- }
- return path;
-}
-
-/**
- @brief CableCreateDialog::addTypeRecord
- Fill in a new line of the cable type file, the way the material
- window adds one of its own articles: the form is appended to the
- file, the search is cleared so nothing hides what was just written,
- and the file is read again so the new line comes back from where it
- really stands -- and it is the one under his finger.
-*/
-void CableCreateDialog::addTypeRecord()
-{
- QStringList columns = m_types.columns;
- if (columns.isEmpty()) {
- columns = CableTypeList::defaultColumns();
- }
-
- CableTypeEntryDialog entry(columns, this);
- if (entry.exec() != QDialog::Accepted) {
- return;
- }
-
- const QString path = typeFilePath();
- QString error;
- if (!CableTypeList::appendRecord(path, entry.record(), &error))
- {
- QMessageBox::critical(this,
- tr("Cannot be written"),
- tr("Cannot write to the file:\n%1\n%2")
- .arg(path, error));
- return;
- }
-
- //A search hiding the line he just filled in would leave him
- //with a form he completed and no line to see.
- m_new_search->clear();
- loadTypeFile();
-
- const CableTypeRecord written = entry.record();
- for (int row = 0; row < m_types.records.size(); ++row)
- {
- if (!(m_types.records.at(row) == written)) continue;
-
- const QModelIndex source = m_new_model->index(row, 0);
- m_new_table->setCurrentIndex(m_new_proxy->mapFromSource(source));
- m_new_table->scrollTo(m_new_table->currentIndex());
- break;
- }
-
- selectionChanged();
-}
-
-/**
- @brief CableCreateDialog::editTypeRecord
- Rewrite the line of the cable type file he picked: the very window
- which adds a line opens, already filled in with what that line says,
- and only his yes writes it back -- saying no leaves the file exactly
- as it was.
-
- What he writes replaces the line he started from and nothing else.
- The other lines of the file are read again first, so whatever he
- saved from his spreadsheet in the meantime is written back too
- rather than being thrown away.
-*/
-void CableCreateDialog::editTypeRecord()
-{
- const QModelIndex index = currentSourceIndex();
- if (!index.isValid() || index.row() >= m_types.records.size()) return;
-
- const CableTypeRecord before = m_types.records.at(index.row());
-
- QStringList columns = m_types.columns;
- if (columns.isEmpty()) {
- columns = CableTypeList::defaultColumns();
- }
-
- CableTypeEntryDialog entry(columns, this);
- entry.setValues(before);
- if (entry.exec() != QDialog::Accepted) {
- return;
- }
-
- CableTypeRecord after = entry.record();
- //Cells standing beyond the header belong to the user, and the
- //form knows nothing about them: they must not vanish from the
- //line just because they could not be shown.
- after.extra = before.extra;
-
- const QString path = typeFilePath();
- QString error;
- if (!CableTypeList::updateRecord(path, before, after, &error))
- {
- QMessageBox::critical(this,
- tr("Cannot be written"),
- tr("Cannot write to the file:\n%1\n%2")
- .arg(path, error));
- return;
- }
-
- //A search hiding the line he just wrote would leave him with a
- //form he filled in and no line to see.
- m_new_search->clear();
- loadTypeFile();
-
- for (int row = 0; row < m_types.records.size(); ++row)
- {
- if (!(m_types.records.at(row) == after)) continue;
-
- const QModelIndex source = m_new_model->index(row, 0);
- m_new_table->setCurrentIndex(m_new_proxy->mapFromSource(source));
- m_new_table->scrollTo(m_new_table->currentIndex());
- break;
- }
-
- selectionChanged();
-}
-
-/**
- @brief CableCreateDialog::acceptTypeChange
- Take the chosen type onto the cable whose type is being changed:
- its name, how many cores it has and the colour of each of them,
- together with the cores that cable holds -- the ones beyond the new
- count leave it, the ones which stay keep their place and their wire,
- and the added ones wait in the list of cores not placed yet.
-
- Everything lands as one single undo step, together with whatever he
- typed into the property fields: the type, the cores and the fields
- all come back together or not at all.
-*/
-void CableCreateDialog::acceptTypeChange()
-{
- if (!m_change) {
- QDialog::reject();
- return;
- }
-
- const QModelIndex index = currentSourceIndex();
- if (!index.isValid() || index.row() >= m_types.records.size()) return;
-
- const CableTypeRecord record = m_types.records.at(index.row());
-
- const QString type = CableTypeList::designation(record);
- int count = CableTypeList::coreCount(record);
- //A type saying how many cores it has takes that count; one
- //saying nothing leaves this cable the size it already is.
- if (count <= 0) count = m_change->coreCount();
- const QStringList colors = CableTypeList::coreColors(record);
-
- const CableTypeState before = m_change->typeState();
- CableTypeState after = before;
- after.type = type;
- after.core_count = count;
- after.core_colors = colors;
- after.cores.clear();
- for (const CableCore &core : std::as_const(before.cores))
- {
- if (core.core < count) {
- after.cores.append(core);
- }
- }
-
- //Cores beyond the new count take their mark off the drawing.
- //He is only asked about that when a wire really loses its
- //label: a core which stood nowhere was not on the drawing at
- //all, and nothing he can see goes away with it.
- int wired = 0;
- for (const CableCore &core : std::as_const(before.cores))
- {
- if (core.core >= count && !core.conductor.isNull()) {
- ++wired;
- }
- }
- if (wired > 0)
- {
- const QString text = tr(
- "%n core(s) of this cable bound to a conductor. "
- "With the chosen type, they leave this cable and their mark "
- "disappears from the drawing. Continue?",
- "ask before cores lose the mark they show on the drawing",
- wired);
-
- if (QMessageBox::warning(this, tr("Cable"), text,
- QMessageBox::Yes | QMessageBox::No,
- QMessageBox::Yes) != QMessageBox::Yes) {
- return;
- }
- }
-
- //The type itself is written by the type step rather than by the
- //fields, so the two cannot write it twice and undo cannot leave
- //half of it standing.
- const CableProperties properties_before = m_change->properties();
- CableProperties properties_after = properties_before;
- properties_after.designation = m_designation->text().trimmed();
- //A name this window filled in is the one the cable already had;
- //anything he typed over it is a name of his own
- properties_after.designation_by_hand =
- properties_after.designation != m_designation_prefill
- || m_designation_prefill_by_hand;
- properties_after.installation = m_installation->text().trimmed();
- properties_after.location = m_location->text().trimmed();
-
- QList steps;
- if (properties_after != properties_before) {
- steps.append(new ChangeCablePropertiesCommand(m_change,
- properties_before,
- properties_after));
- }
- if (before != after) {
- steps.append(new ChangeCableTypeCommand(m_change, before, after));
- }
-
- //Nothing changed at all: saying yes to an unchanged form is
- //still saying yes, but it is not a step which has to be kept.
- if (steps.isEmpty())
- {
- QDialog::accept();
- return;
- }
-
- QUndoCommand *step = steps.size() == 1
- ? steps.takeFirst()
- : new BatchCommand(steps, QCoreApplication::translate("ChangeCableTypeCommand",
- "Change the cable type"));
-
- //The folio is the usual place this step is taken back from. When
- //no folio is known -- a cable picked without a line -- the
- //project keeps them all anyway, and only when there is neither
- //does the change stand without a way back.
- QUndoStack *stack = m_diagram ? &m_diagram->undoStack() : nullptr;
- if (!stack)
- {
- auto *project = qobject_cast(m_change->parent());
- if (project && project->undoStack()) {
- stack = project->undoStack();
- }
- }
-
- if (stack) {
- stack->push(step);
- } else {
- if (properties_after != properties_before) {
- m_change->setProperties(properties_after);
- }
- m_change->applyTypeState(after);
- delete step;
- }
-
- QDialog::accept();
-}
-
-/**
- @brief CableTypeEntryDialog::CableTypeEntryDialog
- Build one field per column of the cable type file. Two columns get
- the shape they need -- a number which counts, and colours written
- the way the file separates them -- and every other column is a plain
- field, since it may be one the user invented rather than one
- QElectroTech knows.
- @param columns the columns of the file, in file order
- @param parent
-*/
-CableTypeEntryDialog::CableTypeEntryDialog(const QStringList &columns, QWidget *parent) :
- QDialog(parent),
- m_columns(columns)
-{
- setWindowTitle(tr("New entry"));
-
- auto *main_layout = new QVBoxLayout(this);
-
- auto *intro = new QLabel(tr("Enter the cable type to add. Fields "
- "left empty stay empty in the file."), this);
- intro->setWordWrap(true);
- m_intro = intro;
- main_layout->addWidget(intro);
-
- auto *grid = new QGridLayout;
- for (int i = 0; i < m_columns.size(); ++i)
- {
- const QString column = m_columns.at(i);
-
- auto *label = new QLabel(CableTypeList::translatedColumn(column), this);
- label->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
-
- QWidget *field = nullptr;
- if (column == QLatin1String("cores"))
- {
- auto *spin = new QSpinBox(this);
- spin->setRange(0, 99);
- spin->setSpecialValueText(QStringLiteral("—"));
- spin->setToolTip(tr("How many cores this type has. The dash means "
- "the file does not specify."));
- field = spin;
- }
- else
- {
- auto *edit = new QLineEdit(this);
- edit->setClearButtonEnabled(true);
- if (column == QLatin1String("core_colors"))
- {
- edit->setPlaceholderText(QStringLiteral("br,sw,gr,bl,gnge"));
- edit->setToolTip(tr("The color of each core, separated by a "
- "comma and in the order of the cores."));
- }
- field = edit;
- }
-
- label->setBuddy(field);
- grid->addWidget(label, i, 0, Qt::AlignRight | Qt::AlignVCenter);
- grid->addWidget(field, i, 1);
- m_fields.append(field);
- }
- grid->setColumnStretch(1, 1);
- main_layout->addLayout(grid);
-
- auto *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
- buttons->button(QDialogButtonBox::Ok)->setText(tr("Save"));
- buttons->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
- connect(buttons, &QDialogButtonBox::accepted, this, &CableTypeEntryDialog::accept);
- connect(buttons, &QDialogButtonBox::rejected, this, &CableTypeEntryDialog::reject);
- main_layout->addWidget(buttons);
-
- if (!m_fields.isEmpty()) {
- m_fields.first()->setFocus();
- }
-
- setMinimumWidth(420);
-}
-
-/**
- @brief CableTypeEntryDialog::record
- @return the line the form describes. Empty fields are left out: an
- empty column never means anything for a type being created.
-*/
-CableTypeRecord CableTypeEntryDialog::record() const
-{
- CableTypeRecord record;
-
- for (int i = 0; i < m_columns.size() && i < m_fields.size(); ++i)
- {
- const QString column = m_columns.at(i);
-
- QString value;
- if (auto *spin = qobject_cast(m_fields.at(i)))
- {
- //Zero means "not said": a type may leave its size to the
- //drawing, and an empty cell says exactly that.
- if (spin->value() > 0) {
- value = QString::number(spin->value());
- }
- }
- else if (auto *edit = qobject_cast(m_fields.at(i)))
- {
- value = edit->text();
- value.remove(QLatin1Char('\r'));
- value.remove(QLatin1Char('\n'));
- value = value.trimmed();
- }
-
- if (!value.isEmpty()) {
- record.setValue(column, value);
- }
- }
-
- return record;
-}
-
-/**
- @brief CableTypeEntryDialog::setValues
- Fill the form with a line which is already in the file. The window
- then says it edits rather than adds, so the two can never be told
- apart by mistake: what he writes is only written when he says yes to
- it, and only about the line he started from.
- @param values the line as it stands in the file
-*/
-void CableTypeEntryDialog::setValues(const CableTypeRecord &values)
-{
- setWindowTitle(tr("Edit entry"));
- if (m_intro)
- {
- m_intro->setText(tr("Edit the cable type selected in the list. "
- "Fields left empty stay empty in the file."));
- }
-
- for (int i = 0; i < m_columns.size() && i < m_fields.size(); ++i)
- {
- const QString value = values.value(m_columns.at(i));
-
- if (auto *spin = qobject_cast(m_fields.at(i)))
- {
- bool ok = false;
- const int count = value.toInt(&ok);
- spin->setValue(ok ? qBound(spin->minimum(), count, spin->maximum())
- : spin->minimum());
- }
- else if (auto *edit = qobject_cast(m_fields.at(i)))
- {
- edit->setText(value);
- }
- }
-}
-
-/**
- @brief CableTypeEntryDialog::accept
- Refuse an empty form: a type with nothing said about it is not worth
- a line in the file.
-*/
-void CableTypeEntryDialog::accept()
-{
- if (record().values.isEmpty())
- {
- QMessageBox::warning(this,
- tr("No details"),
- tr("Enter at least one detail "
- "to create an entry."));
- return;
- }
-
- QDialog::accept();
-}
diff --git a/sources/cable/cablecreatedialog.h b/sources/cable/cablecreatedialog.h
deleted file mode 100644
index a18dd3094..000000000
--- a/sources/cable/cablecreatedialog.h
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- 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 .
-*/
-#ifndef CABLECREATEDIALOG_H
-#define CABLECREATEDIALOG_H
-
-#include "../cablelist/cabletypelist.h"
-#include "cable.h"
-#include "cablemanager.h"
-
-#include
-#include
-#include
-#include
-
-class Diagram;
-class QCheckBox;
-class QDialogButtonBox;
-class QLabel;
-class QLineEdit;
-class QStandardItem;
-class QStandardItemModel;
-class QTableView;
-class QTabWidget;
-class QWidget;
-class QPushButton;
-
-/**
- @brief Filter keeping the rows the user is looking for.
-
- Every word he types has to be found somewhere in the row, whatever
- the column it sits in, the same way the material file is searched.
- On top of that it can hide the rows which are of no use to the cable
- being drawn -- the checkbox each of the two tabs carries.
-*/
-class CableFilterProxy : public QSortFilterProxyModel
-{
- Q_OBJECT
-
- public:
- static constexpr int UnsuitableRole = Qt::UserRole + 1;
-
- explicit CableFilterProxy(QObject *parent = nullptr);
-
- void setTokens(const QStringList &tokens);
- void setHideUnsuitable(bool hide);
-
- protected:
- bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
-
- private:
- QStringList m_tokens;
- bool m_hide_unsuitable = true;
-};
-
-/**
- @brief The small window filling in one line of the cable type file.
-
- It is the cable counterpart of the window the material list opens to
- add one of its own articles: one field per column of the file, in
- file order, and a column the program doesn't know still gets its
- field since it belongs to the user rather than to QElectroTech. The
- two columns a cable type is read by get the shape they need -- a
- number which counts, and colours written the way the file separates
- them.
-*/
-class CableTypeEntryDialog : public QDialog
-{
- Q_OBJECT
-
- public:
- /**
- @param columns the columns of the cable type file, in file
- order
- @param parent
- */
- explicit CableTypeEntryDialog(const QStringList &columns, QWidget *parent = nullptr);
-
- ///The line the form describes, empty fields left out
- CableTypeRecord record() const;
- /**
- Fill the form with a line which is already in the file,
- for the window where that line is edited rather than
- added: the window then says so, and every field carries
- what the file holds about that column.
- @param values the line as it stands in the file
- */
- void setValues(const CableTypeRecord &values);
-
- void accept() override;
-
- private:
- QStringList m_columns;
- ///One field per column, in file order, for the tab key
- QList m_fields;
- ///What the window says it is about, changed when editing
- QLabel *m_intro = nullptr;
-};
-
-/**
- @brief The dialog which opens as soon as a cable line has been drawn.
-
- It says how many cores were recognised, then lets the user either
- pick a type from the cable type file -- a brand new cable -- or pick
- one of the cables the project already holds and give it more cores.
- Both tabs carry a search field and the checkbox hiding what cannot be
- used, exactly like the material selection does.
-
- The very same window opens to change the type of a cable which
- already exists: it then shows the type tab alone, filled in with what
- that cable holds, and takes the chosen type onto the cable -- cores
- reworked, marks redrawn -- as one single undo step. It carries the
- button filling in a new line of the cable type file, the way the
- material window carries its own.
-*/
-class CableCreateDialog : public QDialog
-{
- Q_OBJECT
-
- public:
- CableCreateDialog(Diagram *diagram,
- const QList &crossings,
- const CablePartData &part,
- QWidget *parent = nullptr);
-
- /**
- The same window, opened to change the type of a cable which
- already exists rather than to settle a line just drawn.
- @param diagram the folio whose undo stack takes the change
- @param cable the cable whose type is being changed
- @param parent
- */
- CableCreateDialog(Diagram *diagram,
- Cable *cable,
- QWidget *parent = nullptr);
-
- ///The cable the user settled on, null when he cancelled
- Cable *cable() const {return m_cable;}
- ///The cores this cable took away from cables the project
- ///already held, so that undo gives them back
- QList takenCores() const {return m_taken;}
-
- void accept() override;
-
- private:
- void build();
- void buildNewTab();
- void buildExistingTab();
- void buildPropertyRow();
- void loadTypeFile();
- void refreshExisting();
- void selectionChanged();
- ///Fill the name field in and remember what this window put there
- void fillDesignation(const QString &text, bool by_hand);
- void assignCrossings(const QList &core_slots);
- QList coresTakenFromOthers(const QUuid &target) const;
- QList nextFreeSlots(int count) const;
- QModelIndex currentSourceIndex() const;
- bool currentSelectionIsUnsuitable() const;
- QString unsuitableNote() const;
- QStandardItem *cell(const QString &value, bool unsuitable) const;
- ///True while this window changes the type of an existing cable
- bool changeMode() const {return m_change != nullptr;}
- ///How many cores whatever he picks has to hold at least
- int neededCores() const;
- ///Where the cable type file stands
- QString typeFilePath() const;
- ///The button filling in a new line of the cable type file
- void addTypeRecord();
- ///The button rewriting the line of the cable type file he picked
- void editTypeRecord();
- ///Take the chosen type onto the cable being changed
- void acceptTypeChange();
-
- //ATTRIBUTES
- private:
- QPointer m_diagram;
- QList m_crossings;
- CablePartData m_part;
- Cable *m_cable = nullptr;
- ///The cable whose type is changed, null while a line is drawn
- QPointer m_change;
- ///The cores other cables lost to this one, given back by undo
- QList m_taken;
-
- QLabel *m_header = nullptr;
- QTabWidget *m_tabs = nullptr;
- QDialogButtonBox *m_buttons = nullptr;
-
- QLineEdit *m_new_search = nullptr;
- QCheckBox *m_new_hide = nullptr;
- QTableView *m_new_table = nullptr;
- QStandardItemModel *m_new_model = nullptr;
- CableFilterProxy *m_new_proxy = nullptr;
- QLabel *m_new_note = nullptr;
- ///The button rewriting the chosen line of the file, which
- ///can only work on a line which was picked
- QPushButton *m_edit_button = nullptr;
- CableTypeListData m_types;
-
- QLineEdit *m_existing_search = nullptr;
- QCheckBox *m_existing_hide = nullptr;
- QTableView *m_existing_table = nullptr;
- QStandardItemModel *m_existing_model = nullptr;
- CableFilterProxy *m_existing_proxy = nullptr;
- QLabel *m_existing_note = nullptr;
- QList m_existing;
-
- QWidget *m_property_row = nullptr;
- QLineEdit *m_designation = nullptr;
- /**
- The name exactly as this window filled it in, and where
- that name came from: whatever he types over it from then
- on is a name of his own (see accept())
- */
- QString m_designation_prefill;
- bool m_designation_prefill_by_hand = false;
- QLineEdit *m_installation = nullptr;
- QLineEdit *m_location = nullptr;
-};
-
-#endif // CABLECREATEDIALOG_H
diff --git a/sources/cable/cablemanager.cpp b/sources/cable/cablemanager.cpp
deleted file mode 100644
index e3728d5af..000000000
--- a/sources/cable/cablemanager.cpp
+++ /dev/null
@@ -1,635 +0,0 @@
-/*
- 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 .
-*/
-#include "cablemanager.h"
-
-#include "cable.h"
-#include "../autoNum/assignvariables.h"
-#include "../autoNum/numerotationcontextcommands.h"
-#include "../diagram.h"
-#include "../qetgraphicsitem/conductor.h"
-#include "../qetgraphicsitem/element.h"
-#include "../qetgraphicsitem/terminal.h"
-#include "../qetproject.h"
-
-#include
-#include
-
-#include
-#include
-
-namespace {
-
-/**
- @brief Where two segments cross, if they do at all.
-
- Done by hand rather than with QLineF::intersects() because the
- collinear case has to be a miss: a cable drawn exactly on top of a
- conductor follows it, it does not cross it, and counting it would
- claim a core the cable never reaches.
- @param a1 start of the first segment
- @param a2 end of the first segment
- @param b1 start of the second segment
- @param b2 end of the second segment
- @param result receives the crossing point when there is one
- @return true when the two segments cross within their own length
-*/
-bool segmentIntersection(const QPointF &a1, const QPointF &a2,
- const QPointF &b1, const QPointF &b2,
- QPointF *result)
-{
- const QPointF r = a2 - a1;
- const QPointF s = b2 - b1;
- const QPointF u = b1 - a1;
-
- const qreal denominator = r.x() * s.y() - r.y() * s.x();
- if (qFuzzyIsNull(denominator)) {
- return false;
- }
-
- const qreal t = (u.x() * s.y() - u.y() * s.x()) / denominator;
- const qreal v = (u.x() * r.y() - u.y() * r.x()) / denominator;
- if (t < 0.0 || t > 1.0 || v < 0.0 || v > 1.0) {
- return false;
- }
-
- if (result) {
- *result = a1 + t * r;
- }
- return true;
-}
-
-/**
- @brief How far a point stands from a segment, in scene units
-*/
-qreal distanceToSegment(const QPointF &point, const QPointF &a, const QPointF &b)
-{
- const QPointF ab = b - a;
- const qreal length_squared = ab.x() * ab.x() + ab.y() * ab.y();
- if (qFuzzyIsNull(length_squared)) {
- return QLineF(point, a).length();
- }
-
- const QPointF ap = point - a;
- qreal t = (ap.x() * ab.x() + ap.y() * ab.y()) / length_squared;
- t = qBound(0.0, t, 1.0);
- return QLineF(point, a + t * ab).length();
-}
-
-/**
- @brief Whether some cable of the project already stands under this
- number.
-
- A number which is taken must not be handed out twice. That happens
- while a whole run of cables is numbered one after the other -- none
- of them has been added to the project by anything but its own
- creation yet -- and it happens when a rule has simply been given a
- value which some cable carries already.
- @param project
- @param designation the number to look for
- @return true when a cable of that project stands under it
-*/
-bool designationIsTaken(QETProject *project, const QString &designation)
-{
- if (!project || designation.isEmpty()) return false;
-
- for (Cable *cable : project->cables())
- {
- if (cable && cable->designation() == designation) {
- return true;
- }
- }
- return false;
-}
-
-} // namespace
-
-/**
- @brief CableManager::crossedConductors
- Tell which conductors a line about to become a cable runs across, in
- the order they are met along the line. That order is what gives each
- core its place, and therefore its colour, so it has to be the order
- of the drawing and not the order the conductors happen to be stored
- in.
- @param diagram the folio the line is drawn on
- @param line the line, in scene coordinates
- @return the crossings, ordered from the start of the line
-*/
-QList CableManager::crossedConductors(Diagram *diagram, const QLineF &line)
-{
- QList crossings;
- if (!diagram) {
- return crossings;
- }
-
- const QPointF start = line.p1();
- if (qFuzzyCompare(start, line.p2())) {
- return crossings;
- }
-
- const QPointF direction = line.p2() - start;
- for (Conductor *conductor : diagram->conductors())
- {
- QPointF where;
- if (!crossingOf(diagram, line, conductor, &where)) continue;
-
- CableCrossing crossing;
- crossing.conductor = conductor;
- crossing.position = where;
- crossings.append(crossing);
- }
-
- std::sort(crossings.begin(), crossings.end(),
- [&start, &direction](const CableCrossing &a_, const CableCrossing &b_) {
- const QPointF da = a_.position - start;
- const QPointF db = b_.position - start;
- const qreal ta = da.x() * direction.x() + da.y() * direction.y();
- const qreal tb = db.x() * direction.x() + db.y() * direction.y();
- return ta < tb;
- });
-
- return crossings;
-}
-
-/**
- @brief CableManager::crossingOf
- Where one line runs across one conductor, if it does at all.
-
- Done by hand rather than with QLineF::intersects() because the
- collinear case has to be a miss: a cable drawn exactly on top of a
- conductor follows it, it does not cross it, and counting it would
- claim a core the cable never reaches.
- @param diagram the folio the line is drawn on
- @param line the line, in scene coordinates
- @param conductor the wire being asked about
- @param result receives the crossing point, in scene coordinates
- @return true when the line really runs across that wire
-*/
-bool CableManager::crossingOf(Diagram *diagram,
- const QLineF &line,
- Conductor *conductor,
- QPointF *result)
-{
- if (!conductor || !diagram) return false;
-
- const QPointF start = line.p1();
- const QPointF end = line.p2();
- if (qFuzzyCompare(start, end)) return false;
-
- //Work in the conductor's own coordinates: its path is stored
- //there, and a conductor is never where it was when it was
- //created once the whole drawing has been moved about.
- const QPointF a = conductor->mapFromScene(start);
- const QPointF b = conductor->mapFromScene(end);
-
- const QPainterPath path = conductor->path();
- for (int i = 0; i + 1 < path.elementCount(); ++i)
- {
- const QPainterPath::Element &e1 = path.elementAt(i);
- const QPainterPath::Element &e2 = path.elementAt(i + 1);
- //A MoveTo ends the segment before it: the jump to the new
- //subpath is not drawn, so nothing can cross it.
- if (e2.type == QPainterPath::MoveToElement) {
- continue;
- }
- QPointF where;
- if (segmentIntersection(a, b,
- QPointF(e1.x, e1.y),
- QPointF(e2.x, e2.y),
- &where))
- {
- if (result) {
- *result = conductor->mapToScene(where);
- }
- return true;
- }
- }
- return false;
-}
-
-/**
- @brief CableManager::distanceTo
- How far a point of the sheet stands from a wire, in scene units.
- @param conductor
- @param scene_pos
- @return that distance, which is where the wire passes when the point
- is beside it rather than in front of one of its ends
-*/
-qreal CableManager::distanceTo(Conductor *conductor, const QPointF &scene_pos)
-{
- if (!conductor) {
- return std::numeric_limits::max();
- }
-
- qreal best = std::numeric_limits::max();
- const QPainterPath path = conductor->path();
- for (int i = 0; i + 1 < path.elementCount(); ++i)
- {
- const QPainterPath::Element &e1 = path.elementAt(i);
- const QPainterPath::Element &e2 = path.elementAt(i + 1);
- if (e2.type == QPainterPath::MoveToElement) continue;
-
- const qreal d = distanceToSegment(
- scene_pos,
- conductor->mapToScene(QPointF(e1.x, e1.y)),
- conductor->mapToScene(QPointF(e2.x, e2.y)));
- if (d < best) {
- best = d;
- }
- }
- return best;
-}
-
-namespace {
-
-/**
- @brief The end of @a conductor which does not touch @a element.
-
- Which end that is tells where the wire goes on its own sheet: a wire
- ending on a report arrow points away from it, and that is the end
- which says something about where it comes from or where it runs to.
- @param conductor the wire to look at
- @param element the report arrow it ends on (may be nullptr)
- @return the scene position of the other end, (0, 0) when there is none
-*/
-QPointF farEndPosition(const Conductor *conductor, Element *element)
-{
- const Terminal *far = nullptr;
- if (conductor->terminal1
- && conductor->terminal1->parentElement() != element) {
- far = conductor->terminal1;
- } else {
- far = conductor->terminal2 ? conductor->terminal2 : conductor->terminal1;
- }
- return far ? far->scenePos() : QPointF();
-}
-
-/**
- @brief Whether wire @a a runs before wire @a b on its own sheet.
-
- Both are looked at from @a element: what tells them apart is where
- their other end goes. Putting the wires which come in to a report
- and the wires which go out of its partner in this order is what
- pairs a continuation up with the core it belongs to when several
- cores run through one and the same report.
- @param a @param b the two wires to compare
- @param element the report arrow both end on (may be nullptr)
- @return true when @a a comes first
-*/
-bool farBefore(const Conductor *a, const Conductor *b, Element *element)
-{
- const QPointF pa = farEndPosition(a, element);
- const QPointF pb = farEndPosition(b, element);
- if (pa.y() != pb.y()) return pa.y() < pb.y();
- if (pa.x() != pb.x()) return pa.x() < pb.x();
- return a->uuid() < b->uuid();
-}
-
-} // namespace
-
-/**
- @brief CableManager::refreshLabels
- Rewrite the cable field of every conductor of the project from the
- cables themselves.
-
- The rule is one-directional and therefore cannot drift: a conductor
- which some cable claims gets the label that cable generates, a
- conductor which no cable claims any more -- because the cable was
- delete, the core was dragged away or the type lost that core -- has
- the generated label taken away. A conductor which never belonged to
- a cable keeps whatever it had, since only one with a reference is
- touched.
-
- A core which ends on a folio report goes on over there: the wire
- coming out of the linked arrow shows the same piece of copper, so
- it belongs to the same core and carries the same cable field. What
- the links say is worked out from scratch on every pass, and a wire
- which some cable already claims is never taken away from it, so the
- reference appears wherever a report carries a core and disappears
- again by itself as soon as the cable, the core or the link itself
- is gone. Chains of reports are followed as far as they go, so a
- cable may run over any number of sheets.
- @param project
-*/
-void CableManager::refreshLabels(QETProject *project)
-{
- if (!project) return;
-
- struct Binding {
- QUuid cable;
- int slot = -1;
- QString label;
- };
-
- QHash bindings;
- for (Cable *cable : project->cables())
- {
- if (!cable) continue;
- for (const CableCore &core : cable->usedCores())
- {
- if (core.conductor.isNull()) continue;
- Binding binding;
- binding.cable = cable->uuid();
- binding.slot = core.core;
- binding.label = cable->labelOfCore(core.core);
- bindings.insert(core.conductor, binding);
- }
- }
-
- //A core which ends on a folio report continues on the wire which
- //leaves the linked arrow on the other sheet. That wire shows the
- //same piece of copper, so it gets the same reference as the core
- //it continues. The walk repeats while it finds new wires, so a
- //chain of reports is followed across any number of sheets, and
- //it only ever fills a wire no cable has claimed yet: two cores
- //can never fight over the same wire, and a reference worked out
- //here is taken away again by the same pass as soon as the cable,
- //the core or the link does.
- QHash conductors_by_uuid;
- QHash> endings_on_report;
- for (Diagram *diagram : project->diagrams())
- {
- if (!diagram) continue;
- for (Conductor *conductor : diagram->conductors())
- {
- if (conductor) conductors_by_uuid.insert(conductor->uuid(), conductor);
- }
- }
-
- //Where a wire goes on its own sheet (farBefore, above): putting the
- //wires which come in and the wires which go out in the same order
- //is what pairs a continuation up with its core when several cores
- //run through one and the same report.
-
- bool grew = true;
- while (grew)
- {
- grew = false;
- endings_on_report.clear();
- for (const QUuid &source_uuid : bindings.keys())
- {
- Conductor *source = conductors_by_uuid.value(source_uuid);
- if (!source) continue;
-
- Terminal *ends[2] = {source->terminal1, source->terminal2};
- for (Terminal *end : ends)
- {
- if (!end) continue;
- Element *element = end->parentElement();
- //An arrow taken off the sheet carries nothing over
- //any more, whatever still links it
- if (!element || !element->scene()) continue;
- if (!(element->linkType() & Element::AllReport)) continue;
-
- QList &in = endings_on_report[element];
- if (!in.contains(source)) in.append(source);
- }
- }
-
- for (auto it = endings_on_report.constBegin();
- it != endings_on_report.constEnd(); ++it)
- {
- Element *element = it.key();
- QList in = it.value();
- std::sort(in.begin(), in.end(),
- [&](const Conductor *a, const Conductor *b) {
- return farBefore(a, b, element);
- });
-
- for (Element *partner : element->linkedElements())
- {
- if (!partner || partner == element || !partner->scene()) continue;
-
- QList out = partner->conductors();
- std::sort(out.begin(), out.end(),
- [&](const Conductor *a, const Conductor *b) {
- return farBefore(a, b, partner);
- });
-
- //One core coming in may branch out into several wires
- //over there -- they all show the same piece of copper.
- //Several cores coming in take one wire each, in the
- //order they run on their own sheet.
- const int count = (in.size() == 1)
- ? out.size()
- : qMin(in.size(), out.size());
- for (int i = 0; i < count; ++i)
- {
- Conductor *continued = out.at(i);
- if (!continued || bindings.contains(continued->uuid())) continue;
- const int from = qMin(i, in.size() - 1);
- bindings.insert(continued->uuid(),
- bindings.value(in.at(from)->uuid()));
- grew = true;
- }
- }
- }
- }
-
- for (Diagram *diagram : project->diagrams())
- {
- if (!diagram) continue;
- for (Conductor *conductor : diagram->conductors())
- {
- if (!conductor) continue;
-
- const auto it = bindings.constFind(conductor->uuid());
- if (it != bindings.constEnd())
- {
- const ConductorProperties &properties = conductor->properties();
- conductor->setCableReference(it->label, it->cable, it->slot);
- }
- else if (!conductor->properties().m_cable_uuid.isNull())
- {
- conductor->setCableReference(QString(), QUuid(), -1);
- }
- }
- }
-}
-
-/**
- @brief CableManager::claimConductor
- Make a conductor a core of @a new_owner, taking it away from whichever
- cable held it before. A conductor is a single piece of wire: two
- cables cannot both claim it, and the one that loses it must not go
- on drawing a label for it.
- @param project
- @param conductor
- @param new_owner the cable which is about to use the conductor
-*/
-void CableManager::claimConductor(QETProject *project, Conductor *conductor, Cable *new_owner)
-{
- if (!project || !conductor) return;
-
- const QUuid owner_uuid = conductor->properties().m_cable_uuid;
- if (owner_uuid.isNull() || (new_owner && owner_uuid == new_owner->uuid())) {
- return;
- }
-
- if (Cable *previous = cableByUuid(project, owner_uuid)) {
- previous->removeCore(conductor->properties().m_cable_slot);
- }
-}
-
-/**
- @brief CableManager::cableByUuid
- @param project
- @param uuid
- @return the cable with that identity, or nullptr
-*/
-Cable *CableManager::cableByUuid(QETProject *project, const QUuid &uuid)
-{
- if (!project || uuid.isNull()) return nullptr;
- for (Cable *cable : project->cables()) {
- if (cable && cable->uuid() == uuid) return cable;
- }
- return nullptr;
-}
-
-/**
- @brief CableManager::conductorByUuid
- @param project
- @param uuid
- @return the conductor with that identity, or nullptr. Needed because
- a cable remembers its cores by identity: only the drawing knows where
- they ended up.
-*/
-Conductor *CableManager::conductorByUuid(QETProject *project, const QUuid &uuid)
-{
- if (!project || uuid.isNull()) return nullptr;
- for (Diagram *diagram : project->diagrams()) {
- if (!diagram) continue;
- for (Conductor *conductor : diagram->conductors()) {
- if (conductor && conductor->uuid() == uuid) return conductor;
- }
- }
- return nullptr;
-}
-
-/**
- @brief CableManager::nextDesignation
- Work out the number of the next cable drawn on a folio.
-
- When the project has a numbering rule for cables, that rule decides:
- whatever it holds at this moment is the number, read by the very
- same machinery the element numbering reads its own. When it has
- none, the plain letter W is handed out: a project which numbers its
- cables by no rule gets no numbers invented for it, and W -- the one
- part of the old numbers which was never a number -- is all such a
- cable is called until a rule is defined.
-
- A rule whose counter belongs to one folio is read from the beginning
- of the folio the cable is drawn on, so that the number this cable
- gets is the first free one of its own folio rather than whatever the
- project's rule happens to hold after numbering other folios.
-
- Nothing here moves the project's own rule on: that belongs to the
- moment the cable really comes into being.
- @param project
- @param diagram the folio the cable is being drawn on
- @return the number
-*/
-QString CableManager::nextDesignation(QETProject *project, Diagram *diagram)
-{
- NumerotationContext context;
- if (project) {
- context = project->cableAutoNum(project->cableCurrentAutoNum());
- }
- //The folio-bound parts are put back to the start of that folio;
- //the stepping of the overload below then walks past every
- //number of this folio which is already taken
- context = autonum::resetFolioCounters(context);
- return nextDesignation(project, diagram, context);
-}
-
-/**
- @brief CableManager::nextDesignation
- Work the number of a cable out of a rule which the caller keeps in
- @p context -- the project's own rule as it stands, or a copy of it
- which the caller is numbering a whole run of cables with.
-
- Whatever the rule holds is the number, unless some cable already
- stands under it: then @p context is stepped on and the rule is read
- once more, so that a paste which brings in several cables at once
- numbers each of them differently although the project's own rule
- does not move until those cables really exist. A rule which cannot
- step -- one with nothing in it which counts -- hands out what it
- says and says no more, for what it says is all it knows.
-
- A project which numbers its cables by no rule is not read at all
- and is handed the plain letter W instead: no numbers are invented
- where none have been defined.
- @param project
- @param diagram the folio the cable is being drawn on, which every
- folio-bound part of a rule is read from
- @param context the rule to read; stepped on when what it says is
- already taken
- @return the number
-*/
-QString CableManager::nextDesignation(QETProject *project,
- Diagram *diagram,
- NumerotationContext &context)
-{
- if (!project || !diagram) return QString();
-
- const QString key = project->cableCurrentAutoNum();
- //Whether there was a rule to read at all, taken down before the
- //loop: stepping the rule on empties it, and that must not look
- //like a project which never had one.
- const bool ruled = !key.isEmpty() && !context.isEmpty();
- QString previous;
-
- for (int attempt = 0; ruled && attempt < 1000 && !context.isEmpty(); ++attempt)
- {
- //The rule is read on a copy: reading it fills in what its
- //folio-bound parts are worth here and now, and that must
- //not creep into the rule itself.
- NumerotationContext evaluated = context;
- const QString formula = autonum::numerotationContextToFormula(evaluated);
- autonum::sequentialNumbers seq;
- autonum::setSequential(formula, seq, evaluated, diagram, key);
- const QString candidate =
- autonum::AssignVariables::formulaToLabel(formula, seq, diagram);
-
- if (candidate.isEmpty()) break;
- if (!designationIsTaken(project, candidate)) return candidate;
-
- //Taken already: step the rule on and read it once more.
- if (candidate == previous) return candidate;
- previous = candidate;
-
- NumerotationContextCommands ncc(context, diagram);
- const NumerotationContext following = ncc.next();
- if (following.size() != context.size()) break;
- context = following;
- }
-
- //No rule at all: the plain letter W, and nothing more. The old
- //numbers read folio + W + index, and W is the only part of them
- //which was never a number -- so it is what a project which has
- //defined no numbering rule calls its cables, until it defines
- //one.
- if (!ruled) {
- return QStringLiteral("W");
- }
-
- //A rule which says nothing usable here: no number at all. The
- //number is what the rule says, and a rule which says nothing
- //leaves the field to the user rather than having it filled in
- //with something it never held.
- return QString();
-}
diff --git a/sources/cable/cablemanager.h b/sources/cable/cablemanager.h
deleted file mode 100644
index 00055f6dc..000000000
--- a/sources/cable/cablemanager.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- 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 .
-*/
-#ifndef CABLEMANAGER_H
-#define CABLEMANAGER_H
-
-#include
-#include
-#include
-#include
-
-class Cable;
-class Conductor;
-class Diagram;
-class NumerotationContext;
-class QETProject;
-
-/**
- @brief One place where a drawn cable line crosses a conductor.
-*/
-struct CableCrossing
-{
- Conductor *conductor = nullptr;
- /// Scene coordinates of the crossing, where the slash is drawn
- QPointF position;
-};
-
-/**
- @brief The CableManager works out everything which follows from the
- cables of a project, so nothing has to be maintained by hand.
-
- It detects which conductors a line about to be drawn crosses, and it
- turns the cables back into the cable field of every conductor they
- own -- both directions of one single rule: the cable is the only
- source of truth.
-*/
-class CableManager
-{
- public:
- ///How far a colour label may stand from where a line crosses
- ///a wire for that wire to count as the one the label stands
- ///on: one scene unit, and that is already generous. The grid
- ///is there to put a label exactly on the crossing, and it
- ///does: a label drawn on a wire, slid along its own line or
- ///carried there by a paste lands on that crossing to the
- ///last decimal. The single unit only soaks up the rounding
- ///which comes of projecting a label onto its line, and a
- ///terminal which is a hair off the grid. Anything beyond it
- ///is beside the wire rather than on it, so a label one
- ///whole grid step away -- however the grid is set -- gives
- ///up its entry. One number for every place a label is given
- ///to a wire: drawing one, dragging one and wiring a copy
- ///must not disagree about it.
- static constexpr qreal bind_tolerance = 1.0;
-
- //Drawing
- static QList crossedConductors(Diagram *diagram, const QLineF &line);
- static bool crossingOf(Diagram *diagram,
- const QLineF &line,
- Conductor *conductor,
- QPointF *result);
- static qreal distanceTo(Conductor *conductor, const QPointF &scene_pos);
-
- //Keeping the drawing in step with the cables
- static void refreshLabels(QETProject *project);
- static void claimConductor(QETProject *project, Conductor *conductor, Cable *new_owner);
-
- //Lookup
- static Cable *cableByUuid(QETProject *project, const QUuid &uuid);
- static Conductor *conductorByUuid(QETProject *project, const QUuid &uuid);
- static QString nextDesignation(QETProject *project, Diagram *diagram);
- /**
- Work the number of a next cable out of a rule which is
- @em not the project's own state but the one @p context
- holds: the rule is read as it stands there and what it
- hands out is the number, unless some cable already
- stands under that number -- which is what happens while
- a whole run of cables is numbered one after the other
- before any of them has really been added. Then @p
- context is stepped on and the rule is read once more.
-
- When the project numbers its cables by no rule at all,
- @p context is not read and the plain letter W is handed
- out instead: no numbers are invented where none have
- been defined.
-
- A caller numbering a run of cables keeps the same @p
- context for all of them, so each of them gets a number
- of its own; a caller numbering one cable lets it go
- again afterwards. The project's own rule is never
- moved on here -- that belongs to the moment the cable
- comes into being.
- */
- static QString nextDesignation(QETProject *project,
- Diagram *diagram,
- NumerotationContext &context);
-};
-
-#endif // CABLEMANAGER_H
diff --git a/sources/cable/cablepart.cpp b/sources/cable/cablepart.cpp
deleted file mode 100644
index ef1b7401d..000000000
--- a/sources/cable/cablepart.cpp
+++ /dev/null
@@ -1,3377 +0,0 @@
-/*
- 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 .
-*/
-#include "cablepart.h"
-
-#include "cable.h"
-#include "cablemanager.h"
-#include "cablepropertiesdialog.h"
-#include "editcablecommand.h"
-#include "../autoNum/assignvariables.h"
-#include "../diagram.h"
-#include "../properties/xrefproperties.h"
-#include "../qetapp.h"
-#include "../qetproject.h"
-#include "../qetgraphicsitem/conductor.h"
-#include "../utils/qetutils.h"
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-
-#include
-
-namespace {
-
- ///Half size of the slash drawn where a core crosses a conductor
-const qreal slash_half = 3.5;
- ///How far from a slash or its colour the mouse still counts as holding it
-const qreal slash_grab = 6.0;
- ///How far the colour of a core may stand from the wire it names:
- ///close enough to read as one thing, far enough away that the tail of
- ///a letter like "g" does not run into the wire. The text is turned by
- ///a quarter, so that tail is its right hand side, which is the side
- ///facing the wire.
-const qreal core_label_side = 2.5;
- ///How far above the line the colour of a core is written, so
- ///the line passes beside the letters instead of through them
-const qreal core_label_gap = 4.0;
- ///Radius of the blue point drawn at each end of a picked line, and
- ///how far from such a point the mouse still counts as holding it.
- ///Ten across, which is the size QET's own handles use.
-const qreal grip_radius = 5.0;
-const qreal grip_grab = 6.0;
- ///How far from the trunk line the mouse still counts as aiming at
- ///it while a core waits to be put down there: the core follows the
- ///line only while the mouse is near enough for the click to read as
- ///belonging to it, and the slash itself is drawn on the line, at a
- ///grid step, so what is shown is always what would be kept.
-const qreal place_grab = 30.0;
-
-} // namespace
-
- ///The one line which waits for the click putting a core down, if any
-QPointer CablePart::s_placing;
-
-/**
- @brief CablePart::CablePart
- @param data the section as the cable holds it
- @param parent
-*/
-CablePart::CablePart(const CablePartData &data, QGraphicsItem *parent) :
- CablePart(parent)
-{
- setLine(data.p1, data.p2);
- m_part_uuid = data.uuid;
- m_pending_cable = QUuid();
-}
-
-/**
- @brief CablePart::CablePart
- An empty part, waiting for its geometry: used while reading a project.
- @param parent
-*/
-CablePart::CablePart(QGraphicsItem *parent) :
- QetGraphicsItem(parent)
-{
- //ItemSendsGeometryChanges is what makes itemChange() hear about
- //position changes at all -- without it Qt silently never calls
- //it for a setPos(), and then this item is moved only in the
- //screen's opinion: the line and its colour labels are drawn at
- //the new place because the whole item is transformed, while
- //m_p1/m_p2 and CableCore::position go on naming the old one.
- //A paste would therefore wire its labels against the coordinates
- //the line was copied from, and the next copy would write those
- //stale coordinates into the clipboard -- which is exactly the
- //paste landing away from the group it belongs to.
- setFlags(ItemIsMovable | ItemIsSelectable | ItemSendsGeometryChanges);
- setAcceptHoverEvents(true);
- m_part_uuid = QUuid::createUuid();
-}
-
-CablePart::~CablePart()
-{
-}
-
-/**
- @brief CablePart::setCable
- Bind this section to its cable. The item follows every change of the
- cable, because the number and the type it draws live there.
- @param cable
-*/
-void CablePart::setCable(Cable *cable)
-{
- if (m_cable == cable) {
- return;
- }
- if (m_cable) {
- disconnect(m_cable, nullptr, this, nullptr);
- }
- m_cable = cable;
- m_pending_cable = QUuid();
- if (m_cable)
- {
- connect(m_cable, &Cable::changed, this, [this]() {
- prepareGeometryChange();
- update();
- });
- }
- setUpXrefHooks();
- prepareGeometryChange();
- update();
-}
-
-/**
- @brief CablePart::cable
- @return the cable this section belongs to, or nullptr
-*/
-Cable *CablePart::cable() const
-{
- return m_cable.data();
-}
-
-/**
- @brief CablePart::partUuid
- @return which section of the cable this item draws
-*/
-QUuid CablePart::partUuid() const
-{
- return m_part_uuid;
-}
-
-/**
- @brief CablePart::refreshPending
- Bind the item to its cable once the whole project is read: a folio is
- loaded before the cables it holds sections of, so at that moment the
- cable is only known by its identity.
-*/
-void CablePart::refreshPending()
-{
- if (m_cable) return;
- if (!diagram() || !diagram()->project()) return;
-
- if (Cable *cable = CableManager::cableByUuid(diagram()->project(), m_pending_cable)) {
- setCable(cable);
- }
-}
-
-/**
- @brief CablePart::setLine
- Change where the trunk line runs. Purely optical: no core is added,
- none is dropped, nothing is reassigned -- a cable keeps its cores
- wherever its line happens to run.
- @param p1 scene coordinates of one end
- @param p2 scene coordinates of the other end
-*/
-void CablePart::setLine(const QPointF &p1, const QPointF &p2)
-{
- if (m_p1 == p1 && m_p2 == p2) return;
- prepareGeometryChange();
- m_p1 = p1;
- m_p2 = p2;
- syncGeometry();
- update();
-}
-
-/**
- @brief CablePart::line
- @return the trunk line, in scene coordinates
-*/
-QLineF CablePart::line() const
-{
- return QLineF(m_p1, m_p2);
-}
-
-/**
- @brief CablePart::textFont
- @return the font a text of the cable is written with when the cable
- itself asks for none: what the preferences offer on the "Textes"
- page, and -- until he has picked something there -- the font the
- folio writes its own texts with, a shade smaller: the way every
- cable has always been drawn, so that merely opening the
- preferences changes nothing.
-*/
-QFont CablePart::textFont() const
-{
- return QETApp::cableTextsFont();
-}
-
-/**
- @brief CablePart::coreFont
- @return the font the colours of the cores are written with when the
- cable itself asks for none -- all of them together, since the
- colours of one cable are read as one and are never set apart.
-*/
-QFont CablePart::coreFont() const
-{
- return fontOf(QStringLiteral("cores"));
-}
-
-/**
- @brief CablePart::fontOf
- @param key the name of a text of the cable: "designation", "type",
- "installation", "location", "length" or "cores"
- @return the font that text is really written with: the one this
- cable asks for, or -- when it asks for none -- the one the
- preferences offer
-*/
-QFont CablePart::fontOf(const QString &key) const
-{
- if (m_cable)
- {
- const CableTextFormat format = m_cable->properties().texts.value(key);
- if (!format.font.isEmpty())
- {
- QFont font;
- if (QETUtils::fontFromString(font, format.font)) {
- return font;
- }
- }
- }
-
- //The reference to the other folios this cable runs on has a
- //font of its own, set with the button next to its text under
- //Cross-references -- he sets it there and not in the cable itself.
- //When he has left that alone, the reference follows the texts
- //of the cable like every other line of the label does.
- if (key == QLatin1String("xref") && m_cable && diagram() && diagram()->project())
- {
- const QString description = diagram()->project()
- ->defaultXRefProperties(QStringLiteral("cable")).font();
- if (!description.isEmpty())
- {
- QFont font;
- if (QETUtils::fontFromString(font, description)) {
- return font;
- }
- }
- }
-
- return key == QLatin1String("cores") ? QETApp::cableCoreFont() : textFont();
-}
-
-/**
- @brief CablePart::alignOf
- How one text lines up at the left end of the trunk line. A cable
- which says nothing about it follows the preferences, which is where
- -- until he has said otherwise there either -- every text ends where
- the line begins, the way it always did.
- @param key the name of a text of the cable
- @return Qt::AlignLeft, Qt::AlignHCenter or Qt::AlignRight
-*/
-Qt::Alignment CablePart::alignOf(const QString &key) const
-{
- int alignment = 0;
- if (m_cable) {
- alignment = m_cable->properties().texts.value(key).alignment;
- }
- if (!alignment) {
- return QETApp::cableTextAlignment();
- }
- switch (alignment)
- {
- case int(Qt::AlignLeft): return Qt::AlignLeft;
- case int(Qt::AlignHCenter): return Qt::AlignHCenter;
- case int(Qt::AlignRight): return Qt::AlignRight;
- default: return Qt::AlignRight;
- }
-}
-
-/**
- @brief CablePart::textOffset
- @return how far the whole label at the left of the line stands from
- where it has always stood: what the form offers as "Position du
- texte", and zero for a cable which has never been nudged -- which
- is where every cable starts, so that merely opening a project
- moves nothing.
-*/
-QPointF CablePart::textOffset() const
-{
- return m_cable ? m_cable->properties().text_offset : QPointF();
-}
-
-/**
- @brief CablePart::coreOffset
- @return how far the row of colours stands from the slashes it names:
- the same kind of nudge, kept apart from the label's because the two
- are set apart in the form and are moved one without the other.
-*/
-QPointF CablePart::coreOffset() const
-{
- return m_cable ? m_cable->properties().core_offset : QPointF();
-}
-
-/**
- @brief CablePart::textLeft
- Where a text of this width starts, at the left end of the trunk
- line -- the same place for every text, so that a cable whose
- fields are set apart still reads as one label rather than as five.
- @param key the name of a text of the cable
- @param width how wide that text is, in scene units
- @return the x of the left of the text, in scene coordinates
-*/
-qreal CablePart::textLeft(const QString &key, qreal width) const
-{
- const qreal line_left = qMin(m_p1.x(), m_p2.x());
- if (alignOf(key) == Qt::AlignLeft) {
- return line_left + 6.0;
- }
- if (alignOf(key) == Qt::AlignHCenter) {
- return line_left - width * 0.5;
- }
- return line_left - 6.0 - width;
-}
-
-/**
- @brief CablePart::trunkIsHorizontal
- @return true when the trunk line runs along the sheet rather than up
- and down it: which way it runs is what tells the label where it may
- sit, and where the colour of a core may be written.
-*/
-bool CablePart::trunkIsHorizontal() const
-{
- return qAbs(m_p2.y() - m_p1.y()) <= qAbs(m_p2.x() - m_p1.x());
-}
-
-/**
- @brief CablePart::labelLines
- @return what of the cable the label at the left of the trunk line
- writes, in the order the lines are stacked: where the cable runs
- first, then what it is called. Every field the user switched off,
- and every field he left empty, is left out.
-
- Neither the type of the cable, nor its length, nor the references to
- the other folios this cable runs on is among them: all three are
- written under the line rather than over it -- the type first, the
- length under it and the references at the very bottom -- so what is
- above the line stays the description of where the cable belongs and
- nothing else. See refLines() for those underneath.
-
- Each line comes with the rectangle it is written in, worked out with
- its own font and its own way of lining up: those lines may differ
- from each other as much as he likes, and they are stacked one
- under the other by their own height rather than by one common size.
-*/
-QList CablePart::labelLines() const
-{
- if (!m_cable) return QList();
-
- const CableProperties properties = m_cable->properties();
- QList lines;
- const auto write = [this, &lines](const QString &text, const QString &key)
- {
- if (text.isEmpty()) return;
- LabelLine line;
- line.text = text;
- line.key = key;
- line.font = fontOf(key);
- line.align = alignOf(key);
- const QFontMetricsF metrics(line.font);
- line.width = metrics.horizontalAdvance(text);
- line.height = metrics.height();
- lines.append(line);
- };
-
- if (properties.show_installation) {
- write(properties.installation, QStringLiteral("installation"));
- }
- if (properties.show_location) {
- write(properties.location, QStringLiteral("location"));
- }
- if (properties.show_designation) {
- write(properties.designation, QStringLiteral("designation"));
- }
- if (lines.isEmpty()) return lines;
-
- qreal total_height = 0.0;
- for (const LabelLine &line : lines) {
- total_height += line.height;
- }
- const qreal top = trunkIsHorizontal()
- ? (qMin(m_p1.y(), m_p2.y()) - 2.0 - total_height)
- : qMin(m_p1.y(), m_p2.y());
-
- qreal y = top;
- for (LabelLine &line : lines)
- {
- line.rect = QRectF(textLeft(line.key, line.width), y,
- line.width, line.height);
- y += line.height;
- }
-
- //Where the label as a whole has been nudged to: every line of it
- //goes with the rest, so those three lines keep reading as one
- //label rather than as three which have drifted apart.
- const QPointF nudge = textOffset();
- if (!nudge.isNull())
- {
- for (LabelLine &line : lines) {
- line.rect.translate(nudge);
- }
- }
- return lines;
-}
-
-/**
- @brief CablePart::refLines
- @return one line per other folio this cable runs on, stacked under
- the length of the cable -- under the type when the length is not
- drawn, straight under the trunk line when neither is.
-
- That is the bottom of everything the cable says about itself: above
- the line stands where it belongs, under the line what it is and how
- long it runs, and last comes where it goes on. Each line names the
- section standing there, and each of them is the line he double
- clicks to be taken to it.
-
- Every entry comes in the order crossRefs() lists them, so the index
- of a line here is the index of that reference there.
-*/
-QList CablePart::refLines() const
-{
- if (!m_cable) return QList();
-
- QList lines;
- for (const CrossRef &ref : crossRefs())
- {
- if (ref.text.isEmpty()) continue;
- LabelLine line;
- line.text = ref.text;
- line.key = QStringLiteral("xref");
- line.font = fontOf(line.key);
- line.align = alignOf(line.key);
- const QFontMetricsF metrics(line.font);
- line.width = metrics.horizontalAdvance(line.text);
- line.height = metrics.height();
- lines.append(line);
- }
- if (lines.isEmpty()) return lines;
-
- //The same stack as typeRect() and lengthRect() build, only
- //continued: what those two write between themselves decides how
- //far down the references start, so no two of them can overlap
- //whatever fonts he has chosen for them.
- qreal y = underTop();
- if (!typeText().isEmpty()) {
- y += QFontMetricsF(fontOf(QStringLiteral("type"))).height();
- }
- if (!lengthText().isEmpty()) {
- y += QFontMetricsF(fontOf(QStringLiteral("length"))).height();
- }
-
- for (LabelLine &line : lines)
- {
- line.rect = QRectF(textLeft(line.key, line.width), y,
- line.width, line.height);
- y += line.height;
- }
-
- const QPointF nudge = textOffset();
- if (!nudge.isNull())
- {
- for (LabelLine &line : lines) {
- line.rect.translate(nudge);
- }
- }
- return lines;
-}
-
-/**
- @brief CablePart::crossRefs
- @return every other folio this cable runs on, one entry each, in the
- order the folios turn -- empty when the cable runs on this folio
- alone, which is what a cable normally does.
-
- The reference needs no data of its own: a cable standing on two
- folios is one cable drawn as several lines, so every one of those
- lines knows its siblings and they all get the same list, whichever
- way they were put together -- a line drawn onto a cable which was
- already there, or a cable drawn first and joined later.
-*/
-QList CablePart::crossRefs() const
-{
- QList refs;
- if (!m_cable || !diagram() || !diagram()->project()) return refs;
-
- QETProject *project = diagram()->project();
-
- //Which section stands on which other folio. The first section of
- //a folio is the one %l and %c name, when the format asks for
- //them: it is the line he has to look for over there.
- QHash elsewhere;
- const QList parts = m_cable->parts();
- for (const CablePartData &part : parts)
- {
- Diagram *other = project->diagramByUuid(part.diagram);
- if (!other || other == diagram()) continue;
- if (!elsewhere.contains(other)) {
- elsewhere.insert(other, part);
- }
- }
- if (elsewhere.isEmpty()) return refs;
-
- //The format this project asks for such a reference, chosen under
- //Cross-references for the type "Cable": one text per folio, written
- //out with that folio's own numbers and fields.
- const XRefProperties xref = project->defaultXRefProperties(QStringLiteral("cable"));
- const QString format = xref.masterLabel();
- if (format.isEmpty()) return refs;
-
- //The folios are walked over in the order they turn, so the lines
- //read the way the pages do.
- const QList folios = project->diagrams();
- for (Diagram *other : folios)
- {
- if (!elsewhere.contains(other)) continue;
-
- autonum::FormulaContext context;
- const BorderTitleBlock &border = other->border_and_titleblock;
- context.folio = border.folio();
- context.folio_index = other->folioIndex();
- context.folio_total = border.folioTotal();
- context.plant = border.plant();
- context.locmach = border.locmach();
- context.title_block_fields = border.additionalFields();
- context.project_properties = project->projectProperties();
- const CablePartData part = elsewhere.value(other);
- context.has_element = true;
- context.element_position = other->convertPosition((part.p1 + part.p2) * 0.5);
-
- //A fresh set of numbers for every line: two references of
- //the same cable must not count one after the other.
- autonum::sequentialNumbers seq;
- CrossRef ref;
- ref.diagram = other;
- ref.part = part.uuid;
- ref.text = autonum::AssignVariables::formulaToLabel(format, seq, context);
- if (ref.text.isEmpty()) continue;
- refs.append(ref);
- }
- return refs;
-}
-
-/**
- @brief CablePart::crossRefAt
- @param scene_pos a point of the sheet, in scene coordinates
- @return the index of the reference standing at that very place, -1
- when none does -- measured on the lines which are really drawn, so
- the hand of the mouse, the double click and the drawing can never
- disagree about which line is which.
-*/
-int CablePart::crossRefAt(const QPointF &scene_pos) const
-{
- int index = 0;
- for (const LabelLine &line : refLines())
- {
- if (line.rect.contains(scene_pos)) return index;
- ++index;
- }
- return -1;
-}
-
-/**
- @brief CablePart::goToCrossRef
- Send the mouse to the section of the cable which stands on the folio
- the reference at that place points at, the way a cross reference of
- an element does: the folio is shown and the line is picked.
- @param scene_pos a point of the sheet, in scene coordinates
- @return true when the click stood on a reference
-*/
-bool CablePart::goToCrossRef(const QPointF &scene_pos)
-{
- const int index = crossRefAt(scene_pos);
- if (index < 0) return false;
-
- const QList refs = crossRefs();
- if (index >= refs.size()) return false;
-
- const CrossRef ref = refs.at(index);
- if (!ref.diagram) return false;
-
- const QList items = ref.diagram->items();
- for (QGraphicsItem *item : items)
- {
- auto *section = qgraphicsitem_cast(item);
- if (section && section->partUuid() == ref.part) {
- QetGraphicsItem::showItem(section);
- return true;
- }
- }
- return false;
-}
-
-/**
- @brief CablePart::pdfRefs
- Where the PDF export has to put a clickable link for every reference
- this section writes: the rectangle of the text on this folio, and on
- the folio it points at the rectangle of the section it names -- the
- very section a double click on the drawing sends him to (see
- goToCrossRef), so that the export and the drawing can never disagree
- about where the link leads.
-
- One entry per reference drawn, in the order it is drawn, so that the
- export and the drawing count the same way (see crossRefAt).
- @return those entries, empty when this cable runs on this folio alone
-*/
-QList CablePart::pdfRefs() const
-{
- QList out;
- if (!m_cable) return out;
-
- const QList lines = refLines();
- const QList refs = crossRefs();
- const int count = qMin(lines.size(), refs.size());
-
- for (int i = 0; i < count; ++i)
- {
- const CrossRef &ref = refs.at(i);
- if (!ref.diagram || lines.at(i).rect.isEmpty()) continue;
-
- PdfRef link;
- link.diagram = ref.diagram;
- link.here = lines.at(i).rect;
-
- //The section standing over there, which is what the reader
- //has to be brought to. It is looked for the same way a click
- //on the drawing looks for it, so link and click agree.
- const QList items = ref.diagram->items();
- for (QGraphicsItem *item : items)
- {
- auto *section = qgraphicsitem_cast(item);
- if (section && section->partUuid() == ref.part) {
- link.there = section->mapRectToScene(section->boundingRect());
- break;
- }
- }
- out.append(link);
- }
- return out;
-}
-
-/**
- @brief CablePart::setUpXrefHooks
- Keep the reference lines up to date with what the project does: a
- folio which is added, removed or moved changes which lines there are
- and what they say, and so does the format chosen under Cross-references.
-
- Only this line is redrawn, which is enough: the references of a
- cable are worked out again from the cable itself every time its
- label is drawn.
-*/
-void CablePart::setUpXrefHooks()
-{
- for (const QMetaObject::Connection &connection : std::as_const(m_xref_connections)) {
- disconnect(connection);
- }
- m_xref_connections.clear();
-
- Diagram *folio = diagram();
- if (!m_cable || !folio || !folio->project()) return;
-
- QETProject *project = folio->project();
- const auto rebuild = [this]() {
- prepareGeometryChange();
- setUpXrefHooks();
- update();
- };
-
- m_xref_connections << connect(project, &QETProject::projectDiagramsOrderChanged, this, rebuild);
- m_xref_connections << connect(project, &QETProject::diagramAdded, this, rebuild);
- m_xref_connections << connect(project, &QETProject::diagramRemoved, this, rebuild);
- m_xref_connections << connect(project, &QETProject::XRefPropertiesChanged, this, rebuild);
-
- //%F names the other folio by its label, which is read off that
- //folio itself: only when the format really asks for it does that
- //folio have to be listened to as well.
- const XRefProperties xref = project->defaultXRefProperties(QStringLiteral("cable"));
- if (xref.masterLabel().contains(QLatin1String("%F")))
- {
- for (Diagram *other : project->diagrams()) {
- m_xref_connections << connect(other, &Diagram::diagramInformationChanged, this, rebuild);
- }
- }
-}
-
-/**
- @brief CablePart::typeText
- @return the type of the cable, an empty string when the drawing is
- not meant to show it or the cable says nothing about it
-*/
-QString CablePart::typeText() const
-{
- if (!m_cable) return QString();
-
- const CableProperties properties = m_cable->properties();
- if (!properties.show_type || properties.type.isEmpty()) {
- return QString();
- }
- return properties.type;
-}
-
-/**
- @brief CablePart::lengthText
- @return how long the cable is, an empty string when the drawing is
- not meant to show it or the cable says nothing about it
-*/
-QString CablePart::lengthText() const
-{
- if (!m_cable) return QString();
-
- const CableProperties properties = m_cable->properties();
- if (!properties.show_length || properties.length.isEmpty()) {
- return QString();
- }
- return properties.length;
-}
-
-/**
- @brief CablePart::labelRect
- The rectangle holding the label of the cable, drawn at the left of
- the trunk line -- in scene coordinates, like the line itself, so the
- item never has to be moved for its text to follow.
-
- Across a horizontal line the whole label stands above the line: text
- straddling the line is what the eyes have to fight with. The type
- and the length of the cable stand under the line, one under the
- other, and are therefore not measured here.
- @return an empty rectangle when the cable says nothing to show
-*/
-QRectF CablePart::labelRect() const
-{
- QRectF rect;
- for (const LabelLine &line : labelLines())
- {
- rect = rect.isEmpty() ? line.rect : rect.united(line.rect);
- }
- return rect;
-}
-
-/**
- @brief CablePart::underTop
- Where the first line written under the trunk line starts.
-
- Across a horizontal line that is just under the line itself; along a
- line which runs up and down the sheet "under" has no meaning, so it
- is just under the rest of the label instead -- which keeps those
- lines at the left of the line either way.
- @return the y of the top of that first line, in scene coordinates
-*/
-qreal CablePart::underTop() const
-{
- if (trunkIsHorizontal()) {
- return qMax(m_p1.y(), m_p2.y()) + 2.0;
- }
-
- const QRectF label = labelRect();
- //labelRect() already carries the nudge of the label, and the
- //lines under it are nudged once more further down: what is asked
- //for here is where they would stand without it, so that they go
- //with the label exactly once rather than twice.
- return label.isEmpty() ? qMin(m_p1.y(), m_p2.y())
- : label.bottom() + 2.0 - textOffset().y();
-}
-
-/**
- @brief CablePart::typeRect
- The rectangle the type of the cable is written in. It sits under the
- trunk line rather than with the rest of the label, so the type reads
- as what the line carries while everything above the line says where
- the cable belongs -- and it is the first of the lines written under
- the line, so the length can stand under it.
- @return an empty rectangle when the type is not drawn
-*/
-QRectF CablePart::typeRect() const
-{
- const QString type = typeText();
- if (type.isEmpty()) {
- return QRectF();
- }
-
- const QFontMetricsF metrics(fontOf(QStringLiteral("type")));
- const qreal width = metrics.horizontalAdvance(type);
- const qreal height = metrics.height();
-
- return QRectF(textLeft(QStringLiteral("type"), width), underTop(),
- width, height).translated(textOffset());
-}
-
-/**
- @brief CablePart::lengthRect
- The rectangle the length of the cable is written in. It stands under
- the type of the cable, which itself stands straight under the trunk
- line: the type is what the cable is, the length is one more thing
- said about it. Where the type is not drawn, the length takes its
- place and sits straight under the line.
- @return an empty rectangle when the length is not drawn
-*/
-QRectF CablePart::lengthRect() const
-{
- const QString length = lengthText();
- if (length.isEmpty()) {
- return QRectF();
- }
-
- const QFontMetricsF metrics(fontOf(QStringLiteral("length")));
- const qreal width = metrics.horizontalAdvance(length);
- const qreal height = metrics.height();
-
- //The type may well be written in another font than this, so how
- //far down the length hangs has to be measured from the type and
- //not from itself.
- qreal top = underTop();
- if (!typeText().isEmpty()) {
- top += QFontMetricsF(fontOf(QStringLiteral("type"))).height();
- }
-
- return QRectF(textLeft(QStringLiteral("length"), width), top,
- width, height).translated(textOffset());
-}
-
-/**
- @brief CablePart::boundingRect
-*/
-QRectF CablePart::boundingRect() const
-{
- QRectF rect(QPointF(qMin(m_p1.x(), m_p2.x()), qMin(m_p1.y(), m_p2.y())),
- QPointF(qMax(m_p1.x(), m_p2.x()), qMax(m_p1.y(), m_p2.y())));
- rect.adjust(-14.0, -14.0, 14.0, 14.0);
-
- const QRectF label = labelRect();
- if (!label.isEmpty()) {
- rect = rect.united(label);
- }
- const QRectF type = typeRect();
- if (!type.isEmpty()) {
- rect = rect.united(type);
- }
- const QRectF length = lengthRect();
- if (!length.isEmpty()) {
- rect = rect.united(length);
- }
- for (const LabelLine &line : refLines()) {
- rect = rect.united(line.rect);
- }
-
- if (m_cable)
- {
- for (const CableCore &core : m_cable->usedCores())
- {
- if (core.part != m_part_uuid || !coreDrawn(core)) continue;
- const QPointF at = (core.core == m_dragged_core) ? m_drag_scene : coreDrawPosition(core);
- rect = rect.united(coreLabelRect(at, m_cable->colorOfCore(core.core))
- .adjusted(-3.0, -3.0, 3.0, 3.0));
- }
- //The core which is only waiting to be put down is drawn
- //too, so its colour has to be inside what is repainted --
- //right from the moment he asks for it, whether the mouse
- //already stands at the line or still has to come there
- if (m_pending_core >= 0)
- {
- rect = rect.united(coreLabelRect(m_drag_scene,
- m_cable->colorOfCore(m_pending_core))
- .adjusted(-3.0, -3.0, 3.0, 3.0));
- }
- }
- return rect;
-}
-
-/**
- @brief CablePart::coreLabelPivot
- The point the colour of a core is written around before being turned
- by a quarter: at the left of its slash -- or rather close enough to
- the wire that it reads as one thing with it -- and lifted, so its
- lower end stops just above the line instead of being cut through by
- it.
-
- Being turned by a quarter, the tail of a letter like "g" points to
- the right, which is the side facing the wire: that is what the small
- distance kept here has to clear.
- @param at where the slash stands, in scene coordinates
- @param color the colour being written, e.g. "br"
- @return the centre of the text, in scene coordinates -- nudged by
- whatever the form offers as "Position des couleurs", which moves the
- colours and never the slash: the slash marks where the wire starts,
- and that is not for a distance to decide.
-*/
-QPointF CablePart::coreLabelPivot(const QPointF &at, const QString &color) const
-{
- const QFontMetricsF metrics(coreFont());
- const qreal length = metrics.horizontalAdvance(color);
- const qreal thickness = metrics.height();
-
- return QPointF(at.x() - core_label_side - thickness * 0.5,
- at.y() - core_label_gap - length * 0.5) + coreOffset();
-}
-
-/**
- @brief CablePart::coreLabelRect
- The rectangle the colour of a core is written in: the text stands at
- the left of its slash and is turned by a quarter, so it runs along
- the line instead of across it.
- @param at where the slash stands, in scene coordinates
- @param color the colour being written, e.g. "br"
- @return the rectangle in scene coordinates, empty when there is
- nothing to write
-*/
-QRectF CablePart::coreLabelRect(const QPointF &at, const QString &color) const
-{
- if (color.isEmpty()) return QRectF();
-
- const QFontMetricsF metrics(coreFont());
- const qreal width = metrics.horizontalAdvance(color);
- const qreal height = metrics.height();
-
- //The text is drawn around this point and then turned by -90°, so
- //its screen rectangle is height wide and text length tall.
- const QPointF pivot = coreLabelPivot(at, color);
- return QRectF(pivot.x() - height * 0.5, pivot.y() - width * 0.5, height, width);
-}
-
-/**
- @brief CablePart::coreDrawPosition
- Where a stored core is drawn right now. While the trunk line is
- being dragged the label stands where it stood, carried along by
- however far the line has come: the line, the description at its left
- and the colours of its cores are one thing and move as one thing,
- so no label ever jumps, ever lands on a neighbour's wire or ever
- disappears -- and letting go of the line needs no correction.
- @param core the stored core
- @return where it is drawn at this moment
-*/
-QPointF CablePart::coreDrawPosition(const CableCore &core) const
-{
- if (m_line_moved)
- {
- for (const CableCore &dragged : std::as_const(m_line_drag_cores))
- {
- if (dragged.core == core.core && dragged.part == core.part) {
- return dragged.position;
- }
- }
- }
- return core.position;
-}
-
-/**
- @brief CablePart::coreDrawn
- Says whether a core is drawn at all. While the line is dragged, the
- labels are drawn as they stand during that drag -- which is every
- one of them: carrying the line along never leaves any of them out.
- @param core the stored core
- @return true when there is something to draw
-*/
-bool CablePart::coreDrawn(const CableCore &core) const
-{
- if (!m_line_moved) return true;
-
- for (const CableCore &dragged : std::as_const(m_line_drag_cores))
- {
- if (dragged.core == core.core && dragged.part == core.part) {
- return true;
- }
- }
- return false;
-}
-
-/**
- @brief CablePart::dragPosition
- Where the label of a core being dragged is drawn: still exactly
- where it stood, taken along by however far the mouse has come since
- it was picked up -- and only by whole grid steps, counted from the
- place it stood, the same as anything else drawn on the sheet moves.
-
- So picking a label up never shifts it by a hair, even when the
- mouse closes on it a little to one side, and letting go can never
- take it back: what is drawn while he drags is exactly what is kept
- when he lets go. A pull shorter than half a grid step moves nothing
- at all, which is how every other item on the folio behaves.
-
- The label follows the line and nothing else: pulling it upwards,
- downwards, sideways or diagonally slides it along its own line,
- never takes it off it, and never past either end of it -- a colour
- label belongs to the line it is written on. Holding Ctrl lets go of
- the grid steps, as everywhere else on the sheet.
- @param scene_pos where the mouse stands, in scene coordinates
- @return where the label is drawn at this moment
-*/
-QPointF CablePart::dragPosition(const QPointF &scene_pos) const
-{
- //Where it was picked up: still the place the cable says it
- //stands, since nothing is written while he drags.
- QPointF anchor = m_drag_scene;
- if (m_cable && m_dragged_core >= 0) {
- const CableCore held = m_cable->core(m_dragged_core);
- if (held.core == m_dragged_core) anchor = held.position;
- }
-
- const QPointF axis = m_p2 - m_p1;
- const qreal length_squared = axis.x() * axis.x() + axis.y() * axis.y();
- if (qFuzzyIsNull(length_squared)) return anchor;
-
- const qreal length = std::sqrt(length_squared);
- const QPointF shift = scene_pos - m_press_scene;
- const qreal along = (shift.x() * axis.x() + shift.y() * axis.y()) / length;
-
- const qreal stepped = stepAlong(along);
-
- const QPointF carried = anchor + (axis / length) * stepped;
-
- //And no further than the line itself: a colour label belongs to
- //the line it is written on and stops at either end of it, so it
- //goes on standing over a place the line really runs across and
- //an entry can always follow it. Where it was picked up lay on
- //the line, so this never moves a label which behaves -- it only
- //stops one being pulled past the end.
- const qreal share = ((carried.x() - m_p1.x()) * axis.x()
- + (carried.y() - m_p1.y()) * axis.y())
- / length_squared;
- if (share <= 0.0) return m_p1;
- if (share >= 1.0) return m_p2;
- return m_p1 + axis * share;
-}
-
-/**
- @brief CablePart::stepAlong
- Snap a distance measured along the line to a grid step, in the
- coordinates of the sheet: a horizontal line steps along x, a
- vertical one along y, and holding Ctrl lets go of the grid the same
- way Diagram::snapToGrid does everywhere else on the sheet.
- @param along how far from the first end of the line, in scene units
- @return that distance at a grid step
-*/
-qreal CablePart::stepAlong(qreal along) const
-{
- const QPointF probe = trunkIsHorizontal() ? QPointF(along, 0.0)
- : QPointF(0.0, along);
- const QPointF stepped_probe = Diagram::snapToGrid(probe);
- return trunkIsHorizontal() ? stepped_probe.x()
- : stepped_probe.y();
-}
-
-/**
- @brief CablePart::placePosition
- Where the mouse would put down a core which is waiting to be put
- down: the mouse itself projected onto the line, snapped to a grid
- step and stopped at either end of the line, so what the drawing
- shows while he aims is exactly what the click leaves behind.
-
- Unlike dragPosition this asks where the mouse *is* rather than how
- far it has come, since nothing is held yet to measure that from.
- @param scene_pos where the mouse stands, in scene coordinates
- @return where the slash would be drawn
-*/
-QPointF CablePart::placePosition(const QPointF &scene_pos) const
-{
- const QPointF axis = m_p2 - m_p1;
- const qreal length_squared = axis.x() * axis.x() + axis.y() * axis.y();
- if (qFuzzyIsNull(length_squared)) return m_p1;
-
- const qreal length = std::sqrt(length_squared);
- const QPointF shift = scene_pos - m_p1;
- const qreal along = (shift.x() * axis.x() + shift.y() * axis.y()) / length;
-
- //At a grid step like anything else drawn, and never beyond
- //either end: a colour label belongs to the line it is written
- //on and stops where the line stops.
- const qreal stepped = qBound(0.0, stepAlong(along), length);
- return m_p1 + (axis / length) * stepped;
-}
-
-/**
- @brief CablePart::shape
- Only the trunk line, its text and its slashes are clickable, not the
- whole rectangle around them: a cable drawn across a dense folio must
- not steal the clicks meant for what it passes over.
-*/
-QPainterPath CablePart::shape() const
-{
- QPainterPath trunk;
- trunk.moveTo(m_p1);
- trunk.lineTo(m_p2);
-
- QPainterPathStroker stroker;
- stroker.setWidth(10.0);
- stroker.setCapStyle(Qt::FlatCap);
-
- QPainterPath result = stroker.createStroke(trunk);
-
- const QRectF label = labelRect();
- if (!label.isEmpty()) {
- result.addRect(label);
- }
- const QRectF type = typeRect();
- if (!type.isEmpty()) {
- result.addRect(type);
- }
- const QRectF length = lengthRect();
- if (!length.isEmpty()) {
- result.addRect(length);
- }
- //The references to the other folios stand under those and are
- //clicked the same way.
- for (const LabelLine &line : refLines()) {
- result.addRect(line.rect);
- }
-
- if (m_cable)
- {
- for (const CableCore &core : m_cable->usedCores())
- {
- if (core.part != m_part_uuid || !coreDrawn(core)) continue;
- const QPointF at = (core.core == m_dragged_core) ? m_drag_scene : coreDrawPosition(core);
- //The colour beside the slash is held by the same grab as
- //the slash: it is read far more often than it is moved,
- //and clicking the letters has to do what clicking the
- //slash does. The label stands to the left of the slash,
- //so the grab reaches that way rather than the other.
- result.addRect(QRectF(at - QPointF(18.0, slash_grab),
- QSizeF(24.0, slash_grab * 2.0)));
- const QRectF text = coreLabelRect(at, m_cable->colorOfCore(core.core));
- if (!text.isEmpty()) {
- result.addRect(text.adjusted(-3.0, -3.0, 3.0, 3.0));
- }
- }
- }
-
- //While the line is picked, its two end points are part of what
- //may be clicked: they reach out over the one unit of stroke the
- //trunk itself is, and a finger aiming at a blue point never
- //lands that exactly.
- if (isSelected())
- {
- result.addEllipse(m_p1, grip_grab, grip_grab);
- result.addEllipse(m_p2, grip_grab, grip_grab);
- }
- return result;
-}
-
-/**
- @brief CablePart::labelAt
- Whether the colour label of that core is standing at that point:
- either its slash, or the text beside it, both of which read as one
- thing with the wire the core names.
-
- One core at a time, so that the test can never be answered by a
- neighbour standing closer to the point -- dropping one colour
- straight onto another has to be seen as exactly that.
- @param core which core is being asked about
- @param local a point of this item, scene coordinates
- @return true when the point is on that label
-*/
-bool CablePart::labelAt(int core, const QPointF &local) const
-{
- if (!m_cable) return false;
-
- const CableCore at_core = m_cable->core(core);
- if (at_core.core != core || at_core.part != m_part_uuid || !coreDrawn(at_core)) {
- return false;
- }
-
- const QPointF at = coreDrawPosition(at_core);
- if (QLineF(at, local).length() <= slash_grab) return true;
-
- const QRectF text = coreLabelRect(at, m_cable->colorOfCore(core));
- return !text.isEmpty() && text.adjusted(-3.0, -3.0, 3.0, 3.0).contains(local);
-}
-
-/**
- @brief CablePart::coreAt
- @param local a point of this item, scene coordinates
- @return the core whose slash or colour stands there, -1 when none does
-*/
-int CablePart::coreAt(const QPointF &local) const
-{
- if (!m_cable) return -1;
-
- for (const CableCore &core : m_cable->usedCores())
- {
- if (core.part != m_part_uuid || !coreDrawn(core)) continue;
- if (core.core != m_dragged_core)
- {
- if (labelAt(core.core, local)) return core.core;
- continue;
- }
-
- //The one being dragged is drawn where the mouse holds it
- //right now, not where the cable still says it stands
- const QPointF at = m_drag_scene;
- if (QLineF(at, local).length() <= slash_grab) {
- return core.core;
- }
- const QRectF text = coreLabelRect(at, m_cable->colorOfCore(core.core));
- if (!text.isEmpty() && text.adjusted(-3.0, -3.0, 3.0, 3.0).contains(local)) {
- return core.core;
- }
- }
- return -1;
-}
-
-/**
- @brief CablePart::drawLabel
- @param painter
-*/
-void CablePart::drawLabel(QPainter *painter, const QColor &ink) const
-{
- if (!m_cable) return;
-
- const QList lines = labelLines();
- const QRectF type_rect = typeRect();
- const QRectF length_rect = lengthRect();
- const QList ref_lines = refLines();
- if (lines.isEmpty() && type_rect.isEmpty() && length_rect.isEmpty()
- && ref_lines.isEmpty()) return;
-
- painter->save();
- painter->setPen(ink);
-
- //Every line brings the font it is written with: they are not
- //measured with the same yard, so they may not be written with
- //one either.
- for (const LabelLine &line : lines)
- {
- painter->setFont(line.font);
- painter->drawText(line.rect, line.align | Qt::AlignVCenter, line.text);
- }
- if (!type_rect.isEmpty()) {
- painter->setFont(fontOf(QStringLiteral("type")));
- painter->drawText(type_rect, alignOf(QStringLiteral("type")) | Qt::AlignVCenter,
- typeText());
- }
- if (!length_rect.isEmpty()) {
- painter->setFont(fontOf(QStringLiteral("length")));
- painter->drawText(length_rect, alignOf(QStringLiteral("length")) | Qt::AlignVCenter,
- lengthText());
- }
- //Last of all, at the very bottom: which other folios this cable
- //runs on, one line each.
- for (const LabelLine &line : ref_lines)
- {
- painter->setFont(line.font);
- painter->drawText(line.rect, line.align | Qt::AlignVCenter, line.text);
- }
- painter->restore();
-}
-
-/**
- @brief CablePart::drawCores
- One slash per core the cable really wires, with the colour of that
- core beside it -- the same colour which is written into the cable
- field of the conductor underneath. Plus, while a core is waiting to
- be put down, a paler copy of the very same marks wherever the mouse
- stands near the line.
- @param painter
-*/
-void CablePart::drawCores(QPainter *painter, const QColor &ink) const
-{
- if (!m_cable) return;
-
- for (const CableCore &core : m_cable->usedCores())
- {
- if (core.part != m_part_uuid || !coreDrawn(core)) continue;
-
- const bool dragged = (core.core == m_dragged_core);
- const QPointF at = dragged ? m_drag_scene : coreDrawPosition(core);
-
- drawCoreMark(painter, ink, at, m_cable->colorOfCore(core.core));
- }
-
- //A core which is waiting to be put down shows where it would
- //land: the same slash and the same colour as any other, only
- //paler, since nothing of it is written yet. It shows as soon as
- //he asks for the core, and not only once the mouse has come
- //near enough -- he picks it out of a list, and the line may well
- //be across the screen from where that list stands.
- if (m_pending_core >= 0)
- {
- painter->save();
- painter->setOpacity(painter->opacity() * 0.6);
- drawCoreMark(painter, ink, m_drag_scene,
- m_cable->colorOfCore(m_pending_core));
- painter->restore();
- }
-}
-
-/**
- @brief CablePart::drawCoreMark
- One slash, with the colour of the core it marks written beside it:
- the colour stands at the left of its slash and is turned by a
- quarter, the way a measurement is written along the line it belongs
- to. The same drawing serves a core which is already on the sheet and
- one which is only being aimed at.
- @param painter
- @param ink the colour everything of this line is drawn with
- @param at where the slash stands, in scene coordinates
- @param color the colour of that core, e.g. "br"
-*/
-void CablePart::drawCoreMark(QPainter *painter, const QColor &ink,
- const QPointF &at, const QString &color) const
-{
- const QFont core_font = coreFont();
- const QFontMetricsF metrics(core_font);
-
- painter->save();
- painter->setFont(core_font);
- painter->setPen(QPen(ink, 1.0));
- painter->drawLine(QPointF(at.x() - slash_half, at.y() + slash_half),
- QPointF(at.x() + slash_half, at.y() - slash_half));
-
- if (!color.isEmpty())
- {
- const qreal width = metrics.horizontalAdvance(color);
- const qreal height = metrics.height();
- const QPointF pivot = coreLabelPivot(at, color);
-
- painter->save();
- painter->translate(pivot);
- painter->rotate(-90.0);
- painter->drawText(QRectF(-width * 0.5, -height * 0.5, width, height),
- Qt::AlignCenter, color);
- painter->restore();
- }
- painter->restore();
-}
-
-/**
- @brief CablePart::paint
- @param painter
- @param option
- @param widget
-*/
-void CablePart::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
-{
- Q_UNUSED(option)
- Q_UNUSED(widget)
-
- painter->setRenderHint(QPainter::Antialiasing, true);
- painter->setFont(textFont());
-
- // The folio is painted into a buffer whose lightness is inverted to the
- // palette afterwards (PaletteGraphicsView): what is black in the buffer
- // comes out as the Text color, what is white comes out as the sheet.
- // The palette of the option, on the other hand, is the palette of the
- // view's widgets, so it names colors which are already final and land
- // on the sheet color -- an ink taken from it vanishes. Conductors are
- // drawn the same way: black when they stand, red when they are picked.
- const QColor ink = isSelected() ? Qt::red : Qt::black;
-
- //One unit, like every other line of the drawing: a cable trunk is
- //a line, not a highlight, and it has to line up with the wires it
- //runs across rather than stand out over them.
- QPen pen(ink, 1.0);
- pen.setCapStyle(Qt::FlatCap);
- pen.setJoinStyle(Qt::MiterJoin);
- painter->setPen(pen);
- painter->drawLine(m_p1, m_p2);
-
- drawLabel(painter, ink);
-
- drawCores(painter, ink);
-
- //The two points every line of the drawing shows once it is
- //picked: one at each end, in the same blue QET's own handles
- //use. Pulling one of them changes how long the line is, the
- //same way an end of a conductor is pulled (mousePressEvent
- //and mouseMoveEvent). They belong to the line and are painted
- //by it, so nothing else has to be put into the folio for them.
- if (isSelected())
- {
- painter->save();
- painter->setPen(Qt::NoPen);
- painter->setBrush(Qt::blue);
- painter->drawEllipse(m_p1, grip_radius, grip_radius);
- painter->drawEllipse(m_p2, grip_radius, grip_radius);
- painter->restore();
- }
-}
-
-/**
- @brief CablePart::name
- @return the cable number, so the cable shows under its own name in
- every list which names items
-*/
-QString CablePart::name() const
-{
- return m_cable ? m_cable->designation() : QString();
-}
-
-/**
- @brief CablePart::syncGeometry
- Tell the cable where this section runs. The cable is the one place
- the drawing is read back from, so nothing has to be collected from
- the folios at saving time.
-*/
-void CablePart::syncGeometry()
-{
- if (!m_cable) return;
- m_cable->setPartGeometry(m_part_uuid, m_p1, m_p2);
-}
-
-/**
- @brief CablePart::mousePressEvent
- While a core waits to be put down, the click belongs to that: on the
- line -- or near enough for the mouse to be pointing at it -- the core
- is left standing there, right click calls it off, and a click anywhere
- else calls it off as well and goes on doing what it does for everybody
- (left clicking on a slash or on its colour holds that core, on one of
- the two blue points at the ends it holds that end, anywhere else on
- the line it moves the line).
-
- Otherwise: left clicking on a slash or on its colour holds that core:
- it can be dragged onto another conductor to give it that conductor.
-
- On one of the two blue points at the ends of a picked line it holds
- that end instead, so the line can be made longer or shorter the same
- way an end of a conductor is pulled (mouseMoveEvent).
-
- Left clicking anywhere else on the line moves the line.
- @param event
-*/
-void CablePart::mousePressEvent(QGraphicsSceneMouseEvent *event)
-{
- //A core which waits to be put down takes the whole click: on
- //the line it is left standing there, anywhere else -- and on
- //the right button, which cancels everywhere on the sheet --
- //nothing is put down. The click itself goes on doing whatever
- //it does for everybody else.
- if (m_pending_core >= 0)
- {
- if (!m_cable)
- {
- cancelPlacement();
- event->ignore();
- return;
- }
- //Where he aims is read off the click itself as well, so a
- //dialog closing with the mouse already over the line needs
- //no move first and a stale position is never taken for a
- //place he never pointed at.
- m_drag_scene = placePosition(event->scenePos());
- m_pending_valid = QLineF(event->scenePos(), m_drag_scene).length()
- <= place_grab;
-
- if (event->button() == Qt::LeftButton && m_pending_valid)
- {
- const int core = m_pending_core;
- const QPointF at = m_drag_scene;
- cancelPlacement();
- m_just_placed = placeCore(core, at);
- update();
- event->accept();
- return;
- }
- const bool cancel = (event->button() == Qt::RightButton);
- cancelPlacement();
- if (cancel) {
- event->accept();
- } else {
- event->ignore();
- }
- return;
- }
-
- //The second of two quick clicks arrives as a double click: by
- //then the first one has already put a core down, and that must
- //not read as a request to open the dialog a second time.
- m_just_placed = false;
-
- if (event->button() != Qt::LeftButton) {
- QetGraphicsItem::mousePressEvent(event);
- return;
- }
-
- m_press_scene = event->scenePos();
- m_move_p1 = m_p1;
- m_move_p2 = m_p2;
- m_line_moved = false;
- m_line_drag_cores.clear();
- //Nothing is carried along until he picks up a whole line: a
- //press on a colour or on one of the blue points works on that
- //one thing and leaves every other line standing.
- m_carried.clear();
- //Where every colour label stands when the gesture starts: a
- //line which is moved carries all of them along with it, and
- //undo has to know where they were before that. A line which is
- //stretched does not touch them, and records the same set
- //unchanged.
- m_cores_before = m_cable ? m_cable->usedCores() : QList();
- //Whether the sheet has already been told that the rest of the
- //selection comes along with this line (see carrySelection): every
- //press starts from nothing, so the next gesture tells it again.
- m_mover_started = false;
- m_told_mover = QPointF();
-
- //One of the two blue points at the ends, when the line is
- //picked and only one of the two is under the finger: pulling
- //it changes how long the line is. The line comes before
- //whatever stands on it: a colour label parked right at the end
- //may not take that point away, since the line could not be
- //made longer from that end any more -- the labels would have
- //to be pushed aside first, and there is nothing to push them
- //with. The label is still picked up beside the point, by its
- //letters or by its slash a little further along the line.
- //When both ends are that close, which only happens on a very
- //short line, nothing is stretched: it is moved like any other
- //line instead.
- int grip = 0;
- if (isSelected())
- {
- const bool first = QLineF(event->scenePos(), m_p1).length() <= grip_grab;
- const bool second = QLineF(event->scenePos(), m_p2).length() <= grip_grab;
- if (first != second) grip = first ? 1 : 2;
- }
- m_stretch_end = grip;
-
- if (grip > 0)
- {
- update();
- event->accept();
- return;
- }
-
- const int core = coreAt(event->scenePos());
- if (core >= 0)
- {
- m_dragged_core = core;
- //The label stays exactly where it stands until the mouse
- //really moves: picking it up must not shift it by a hair.
- m_drag_scene = m_cable->core(core).position;
- if (!isSelected()) {
- setSelected(true);
- }
- update();
- event->accept();
- return;
- }
-
- QetGraphicsItem::mousePressEvent(event);
-
- //Every other line he has picked up at the same time: dragging
- //one line takes the whole selection with it, and all of them go
- //back to where they stood if he calls the gesture off. The
- //selection is read after the click has been dealt with, since
- //clicking an unselected line leaves that one alone selected.
- m_carried.clear();
- if (isSelected() && scene())
- {
- const QList selected = scene()->selectedItems();
- for (QGraphicsItem *item : selected)
- {
- auto *other = qgraphicsitem_cast(item);
- if (!other || other == this || !other->m_cable) continue;
-
- CarriedLine carried;
- carried.part = other;
- carried.p1 = other->m_p1;
- carried.p2 = other->m_p2;
- carried.cores = other->m_cable->usedCores();
- m_carried.append(carried);
- }
- }
-}
-
-/**
- @brief CablePart::mouseMoveEvent
- Three things the mouse can carry: the colour of a core (which stays
- on its line and never past either end of it), one of the two blue
- points at the ends (which makes the line longer or shorter -- that
- changes nothing else: every colour label goes on standing exactly
- where the user put it, and the line may not be brought in past the
- outermost of those labels, which would otherwise be left standing
- beyond the end of their own line), and the whole line (which is
- translated as a piece, taking its colour labels with it). The line
- in each case is snapped to the grid.
- @param event
-*/
-void CablePart::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
-{
- //Where a core waiting to be put down would stand: on the line
- //itself, at a grid step, and drawn from the very first moment
- //it is asked for -- how near the mouse stands decides nothing
- //but whether a click keeps it or sends it away (see
- //mousePressEvent), never whether he can see it.
- if (m_pending_core >= 0)
- {
- if (!m_cable) {
- cancelPlacement();
- } else {
- m_drag_scene = placePosition(event->scenePos());
- update();
- }
- event->accept();
- return;
- }
-
- if (m_dragged_core >= 0)
- {
- m_drag_scene = dragPosition(event->scenePos());
- update();
- event->accept();
- return;
- }
-
- if (m_stretch_end > 0 && (event->buttons() & Qt::LeftButton))
- {
- //The end under the finger follows the mouse, snapped to the
- //grid like anything else drawn on the folio, and the line
- //keeps its right angle: the direction the mouse covers the
- //most decides which way the line runs, the same way the
- //drawing tool decides it -- so pulling the end of a
- //horizontal line far enough upwards turns the line
- //vertical instead of hanging it into the air at an angle.
- const QPointF fixed = (m_stretch_end == 1) ? m_move_p2 : m_move_p1;
- QPointF moving = Diagram::snapToGrid(event->scenePos());
- const QPointF to = moving - fixed;
-
- bool horizontal;
- if (qAbs(to.x()) > qAbs(to.y())) horizontal = true;
- else if (qAbs(to.y()) > qAbs(to.x())) horizontal = false;
- else horizontal = trunkIsHorizontal();
- moving = horizontal ? QPointF(moving.x(), fixed.y())
- : QPointF(fixed.x(), moving.y());
-
- //Never shorter than his own colour labels: every one of
- //them which stood on this part of the line when the
- //gesture began has to go on standing on it, or it would be
- //left beyond the end of the line it belongs to and could
- //no longer be wired. He may pull the line out as far as he
- //likes, and he may bring either end in as far as he likes
- //up to the outermost of those labels -- which are found
- //along the line as it runs now, since the direction can
- //change from one move to the next.
- const QPointF started_from = (m_stretch_end == 1) ? m_move_p1 : m_move_p2;
- const qreal at_fixed = horizontal ? fixed.x() : fixed.y();
- const qreal at_start = horizontal ? started_from.x() : started_from.y();
- const qreal first = qMin(at_fixed, at_start);
- const qreal last = qMax(at_fixed, at_start);
-
- bool any_label = false;
- qreal low = at_fixed, high = at_fixed;
- for (const CableCore &core : m_cores_before)
- {
- if (core.part != m_part_uuid) continue;
-
- const qreal at = horizontal ? core.position.x() : core.position.y();
- if (at < first || at > last) continue;
-
- if (!any_label) { low = high = at; any_label = true; }
- else { low = qMin(low, at); high = qMax(high, at); }
- }
-
- if (any_label)
- {
- //The end sits on the grid, so the limit it stops
- //against is taken from it on the far side of the
- //label, never in front of it.
- QSettings settings;
- const int step = horizontal
- ? settings.value(QStringLiteral("diagrameditor/Xgrid"),
- Diagram::xGrid).toInt()
- : settings.value(QStringLiteral("diagrameditor/Ygrid"),
- Diagram::yGrid).toInt();
- if (step > 0) {
- high = std::ceil(high / step) * step;
- low = std::floor(low / step) * step;
- }
-
- qreal at_end = horizontal ? moving.x() : moving.y();
- at_end = at_end > at_fixed ? qMax(at_end, high)
- : qMin(at_end, low);
- moving = horizontal ? QPointF(at_end, moving.y())
- : QPointF(moving.x(), at_end);
- }
-
- const QPointF p1 = (m_stretch_end == 1) ? moving : fixed;
- const QPointF p2 = (m_stretch_end == 1) ? fixed : moving;
-
- if (p1 != m_p1 || p2 != m_p2)
- {
- setLine(p1, p2);
- m_line_moved = true;
- //Only the line is stretched: every colour label goes on
- //standing exactly where the user left it, none is
- //carried anywhere and none is worked out again. What
- //the drawing showed of them before the gesture goes on
- //being what the cable holds -- which is what the
- //release below records, along with the line.
- m_line_drag_cores = m_cores_before;
- update();
- }
- event->accept();
- return;
- }
-
- if (isSelected() && event->buttons() & Qt::LeftButton)
- {
- //The line follows the mouse, snapped to the grid like anything
- //else drawn on the folio, and keeps its length: it is purely
- //optical, so nothing else has to follow it.
- const QPointF target = Diagram::snapToGrid(
- m_move_p1 + (event->scenePos() - m_press_scene));
- const QPointF delta = target - m_move_p1;
-
- if (target != m_p1 || m_move_p2 + delta != m_p2)
- {
- setLine(target, m_move_p2 + delta);
- m_line_moved = true;
- //Where every colour label stands on the line at this
- //moment: carried along by however far the line has
- //come, so line, description and colours move as one
- //piece and no label ever jumps or disappears.
- m_line_drag_cores = m_cores_before;
- reconcileCores(m_line_drag_cores, delta);
- update();
- }
- //Every other line he picked up with this one comes along,
- //on every step of the gesture and with the same step: a
- //selection of lines is one thing to move. It happens even
- //when the line under his finger itself stands still -- a
- //pull back to where it began -- or those would be left
- //standing out where the gesture reached.
- carryCompanions(delta);
- //Everything else he marked at the same time comes along too
- //-- the elements and the texts of the same selection -- so
- //that one gesture moves all of it rather than two halves of
- //the selection going different ways.
- carrySelection(delta);
- event->accept();
- return;
- }
-
- QetGraphicsItem::mouseMoveEvent(event);
-}
-
-/**
- @brief CablePart::mouseReleaseEvent
- @param event
-*/
-void CablePart::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
-{
- //Nothing has been put down by this gesture, so nothing has to
- //be settled either: the core is placed by the press itself.
- if (m_pending_core >= 0)
- {
- event->accept();
- return;
- }
-
-
- if (m_dragged_core >= 0 && event->button() == Qt::LeftButton)
- {
- const int core = m_dragged_core;
- m_dragged_core = -1;
-
- //What was drawn while he dragged is what is kept: the label
- //goes on standing where he left it, and letting go can never
- //take it back to where it was picked up. A press which never
- //went anywhere is a click: it keeps the label exactly where
- //it already stands and never makes it name some other wire
- //for having been touched.
- const CableCore held = m_cable ? m_cable->core(core) : CableCore();
- const bool moved_label = (held.core == core) && (m_drag_scene != held.position);
-
- if (moved_label) {
- rebindCore(core, m_drag_scene);
- }
- update();
- event->accept();
- return;
- }
-
- //The steps of this line, held back until the gesture is over:
- //when the sheet is moving elements together with the lines, its
- //own undo waits for them so that one single undo step takes all
- //of it back.
- QList pending;
- bool cancelled = false;
-
- if (event->button() == Qt::LeftButton && m_line_moved && diagram())
- {
- const QPointF after_p1 = m_p1;
- const QPointF after_p2 = m_p2;
- //One undo step for one gesture: taking the drag back puts
- //every line of it exactly where it started -- a line which
- //moved takes its colour labels back with it, since they are
- //written on that line, and one which was stretched leaves
- //them exactly where he put them.
- if (after_p1 != m_move_p1 || after_p2 != m_move_p2)
- {
- QList after = m_line_drag_cores;
-
- //Stretching only changes how long the line is: no
- //colour label has been carried anywhere, so nothing of
- //the cable has to be worked out again, no wire changes
- //hands and nothing at all is asked. One step which puts
- //the line -- and only the line -- back where it was.
- if (m_stretch_end > 0)
- {
- pending.append(
- new MoveCablePartCommand(m_cable, this,
- m_move_p1, m_move_p2,
- after_p1, after_p2,
- m_cores_before, after));
- }
- else
- {
- //The lines he picked up with this one settle first,
- //one after the other: a wire one of them lets go of
- //is then already free for the next one instead of
- //being asked about as if it were still taken. Each
- //of them asks before taking anything away from a
- //cable which is not part of this gesture.
- QList steps;
- cancelled = !settleCompanions(steps);
-
- //What the drawing already showed during the drag goes
- //into the cable now -- and which wire each core names
- //is worked out at this moment, and only at it, from
- //where its colour label has come to rest: that is
- //what makes the entries of the wires right again
- //after a move, instead of leaving them standing on
- //the wires the line has gone away from. No label is
- //moved for it.
- if (!cancelled)
- {
- readyToSettle(after);
- cancelled = !settleCores(after);
- }
-
- if (cancelled)
- {
- //He called the whole gesture off: every line of
- //it goes back to where he picked it up, nothing
- //at all is written, and there is no step to undo
- //-- the drawing is as it was before he started.
- putCompanionsBack();
- if (m_cable) {
- m_cable->setCores(m_cores_before);
- }
- setLine(m_move_p1, m_move_p2);
- for (QUndoCommand *step : steps) {
- delete step;
- }
- }
- else
- {
- if (m_cable) {
- m_cable->setCores(after);
- }
- steps.append(new MoveCablePartCommand(m_cable, this,
- m_move_p1, m_move_p2,
- after_p1, after_p2,
- m_cores_before, after));
- //Given to the whole gesture rather than pushed
- //here, since the sheet may still have the rest
- //of the selection to write down with it.
- pending = steps;
- steps.clear();
- }
- }
- }
- }
-
- //The lines he picked up with this one go on standing where the
- //gesture left them, and none of them is carried any more --
- //whether it was written down or called off.
- for (const CarriedLine &carried : std::as_const(m_carried))
- {
- if (!carried.part) continue;
- carried.part->m_line_moved = false;
- carried.part->m_line_drag_cores.clear();
- carried.part->update();
- }
- m_carried.clear();
-
- m_line_moved = false;
- m_stretch_end = 0;
- m_line_drag_cores.clear();
-
- //One step for one gesture, however mixed the selection is: when
- //the sheet is moving the elements he marked together with these
- //lines, its own undo waits for the steps above and pushes
- //everything at once, so a single undo takes the whole selection
- //back. When nothing else was marked they are pushed here exactly
- //as they always were -- and he called the gesture off when
- //nothing at all is written, the elements included.
- if (diagram())
- {
- ElementsMover &mover = diagram()->elementsMover();
- if (cancelled)
- {
- qDeleteAll(pending);
- pending.clear();
- mover.cancelMovement();
- }
- else if (!pending.isEmpty() && !mover.isReady())
- {
- mover.addExtraCommands(pending);
- pending.clear();
- }
- else if (pending.size() == 1)
- {
- diagram()->undoStack().push(pending.takeFirst());
- }
- else if (!pending.isEmpty())
- {
- //One step however many lines he moved: taking the
- //gesture back has to take all of it back, or half the
- //selection would stay where the drag left it.
- diagram()->undoStack().push(new BatchCommand(
- pending,
- QCoreApplication::translate(
- "BatchCommand", "Move %n cable(s)",
- "Un geste déplace plusieurs lignes à la fois.",
- pending.size())));
- pending.clear();
- }
- }
- else
- {
- qDeleteAll(pending);
- pending.clear();
- }
-
- QetGraphicsItem::mouseReleaseEvent(event);
-
- //The sheet has been told everything this gesture had to tell it:
- //the next press starts from nothing again (see carrySelection).
- m_mover_started = false;
- m_told_mover = QPointF();
-}
-
-/**
- @brief CablePart::mouseDoubleClickEvent
- Opening the cable is what a double click on the line is for. On one
- of the colour labels it does nothing: they are part of a core, and
- the user asking for the whole cable by clicking twice in the same
- place never said so. On a line which points at another folio it goes
- to that folio instead: the reference leads somewhere, and asking
- twice in it is asking to be taken there.
- @param event
-*/
-void CablePart::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
-{
- //The click which put a core down is reported once more as a
- //double click when he clicks twice fast: it was there to place
- //something and must not go on to open the dialog.
- if (m_just_placed)
- {
- m_just_placed = false;
- event->accept();
- return;
- }
-
- if (event->button() == Qt::LeftButton && coreAt(event->scenePos()) >= 0)
- {
- event->accept();
- return;
- }
-
- //A line which points at another folio leads there: asking twice
- //in that very line is asking to be taken to the section standing
- //over there, the way a cross reference of an element does, rather
- //than to open this cable.
- if (event->button() == Qt::LeftButton && goToCrossRef(event->scenePos()))
- {
- event->accept();
- return;
- }
- QetGraphicsItem::mouseDoubleClickEvent(event);
-}
-
-/**
- @brief CablePart::hoverMoveEvent
- The hand over a line which points at another folio: it says the line
- leads somewhere before he clicks and finds out. While a core waits to
- be put down the cross of that gesture is showing, and it stays.
- @param event
-*/
-void CablePart::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
-{
- QetGraphicsItem::hoverMoveEvent(event);
-
- if (m_pending_core >= 0) return;
-
- if (crossRefAt(event->scenePos()) >= 0) {
- setCursor(Qt::PointingHandCursor);
- } else {
- unsetCursor();
- }
-}
-
-/**
- @brief CablePart::rebindCore
- Let go of a core the user dragged: it lands where he let it go, and
- whether it takes the wire standing there is decided by what he did
- rather than by anything being looked up for him.
-
- Nothing here ever moves the label to reach a wire. It goes on
- standing at the place he dropped it -- on the line, at the grid,
- the very place it has been drawn for every moment of the drag -- so
- that a label never crawls back to the wire he pulled it away from
- and never goes off to look for one of its own. What changes, if
- anything, is which wire names it:
-
- - On a wire this line runs across: the core takes it, and the
- field of the wire it left is emptied by the refresh which
- follows.
- - On a wire another cable holds: he is asked about it first. Yes
- hands the wire over, and the other cable's core becomes a free
- slot of that cable -- its slash and its colour leave the sheet
- with it, which is what he saw happen. No leaves the other cable
- exactly as it was and makes this core the free one instead: it
- names no wire at all any more, so its own slash and colour go off
- the sheet as well, its entry leaves the wire it came from, and it
- waits as a free slot in the list of existing cables until he puts
- it back.
- - Directly on the colour label of another core of the same cable:
- the two exchange their wires, which is a deliberate exchange --
- he put one colour on top of the other, so he means the second one
- to take what the first is giving up.
- - On a wire another core of this same cable already holds, anywhere
- but on its label: nothing changes.
- - On nothing at all: the label stays exactly where he dropped it,
- and it stops naming a wire -- an entry follows its label and
- stops where its label stopped, so that no entry is left standing
- on a wire the label has gone away from. A label which has not
- been moved keeps what it had, though: picking one up and letting
- it go again leaves the drawing exactly as it was.
-
- Only the label he dragged may move: no other core of this cable is
- hunted down. A core is taken away from its own cable at one single
- place -- when he says no to the wire he has dropped its label on,
- so that it names nothing -- and that is the very thing the other
- cable's core becomes when he says yes to taking its wire. Both
- answers therefore leave the drawing the same way: a free slot, no
- slash, no colour, and the entry gone from the wire it stood on.
-
- Whatever comes out of it is kept as one undo step, the whole set of
- assignments before and after the gesture.
- @param core which core is being moved
- @param dropped_at scene coordinates where the slash was let go
- @param before_cores the set of assignments as it stood before the
- core was added to the cable, when a core is being put down rather
- than moved; nullptr otherwise
-*/
-bool CablePart::rebindCore(int core, const QPointF &dropped_at,
- const QList *before_cores)
-{
- if (!m_cable || !diagram() || !diagram()->project()) {
- return true;
- }
-
- const CableCore moved = m_cable->core(core);
- if (moved.core != core) {
- return true;
- }
-
- //What the cable held before this gesture, so undo can put it
- //back: the set as it stands while a core already on the sheet
- //is merely dragged, or -- when a core is being put down for the
- //first time -- the set from before it was added, which takes
- //the addition back as well.
- const QList before = before_cores ? *before_cores
- : m_cable->usedCores();
-
- //Where the label has actually been drawn: on the line, at whole
- //grid steps from where it stood. It is taken as it is and never
- //worked out again, so letting go cannot shift it by a hair.
- const QPointF at = dropped_at;
- CableCore landed = moved;
- landed.position = at;
-
- //Which wire of this line stands at that very place, worked out
- //from where the label stands and never the other way round:
- //the label is not sent off to look for a wire, it names the
- //one it happens to be standing on.
- const QList crossings = CableManager::crossedConductors(
- diagram(), QLineF(m_p1, m_p2));
- Conductor *target = wireFor(crossings, moved, at);
-
- //Set when he refuses a wire another cable holds: this core then
- //names no wire at all any more and goes back to being a free
- //slot of the cable, so it is taken out rather than written.
- bool freed = false;
-
- if (target)
- {
- //Which core of this cable, if any, already stands on that
- //wire -- everything but the one being dragged.
- int holder = -1;
- for (const CableCore &other : m_cable->usedCores())
- {
- if (other.core == core || other.part != m_part_uuid) continue;
- if (other.conductor == target->uuid()) {
- holder = other.core;
- }
- }
-
- if (holder < 0)
- {
- const ClaimAnswer answer = mayClaim(target);
- if (answer == ClaimAnswer::Cancel)
- {
- //He called the drop off: the label goes on standing
- //where he picked it up, and nothing is written at all.
- return false;
- }
- if (answer == ClaimAnswer::Take)
- {
- //A wire is one piece of wire: whichever cable held it
- //before has to let it go.
- CableManager::claimConductor(diagram()->project(), target, m_cable.data());
-
- //A conductor keeps the identity a cable binds it by, even
- //when it was read back from a file written before it had
- //one.
- target->setUuid(target->uuid());
-
- landed.conductor = target->uuid();
- }
- else
- {
- //He said no: the other cable goes on holding that
- //wire, and this label has come to rest on it anyway.
- //So this core names no wire at all any more and goes
- //back to being a free slot of this cable -- exactly
- //what the other cable's core becomes when he says
- //yes -- taking its slash and its colour off the sheet
- //with it, while its entry leaves the wire the label
- //has gone away from. Only the other cable keeps its
- //wire, and he can put this core back wherever he
- //likes from the list of existing cables.
- freed = true;
- }
- }
- else if (labelAt(holder, at))
- {
- //One colour dropped straight onto another: the two
- //exchange their wires, so neither is left standing on
- //a wire the drawing does not name with it. The dragged
- //label stays where he put it, on the wire it has just
- //taken, and the other one comes to the place it gives
- //up -- that exchange is what he asked for.
- CableCore swapped = m_cable->core(holder);
- if (swapped.core == holder)
- {
- CableManager::claimConductor(diagram()->project(), target, m_cable.data());
- target->setUuid(target->uuid());
-
- landed.conductor = target->uuid();
-
- swapped.conductor = moved.conductor;
- swapped.position = moved.position;
- m_cable->setCore(swapped);
- }
- }
- //Held by another core of this cable but not dropped on its
- //label: leave everything alone, the dragged label merely
- //stays where it was let go.
- }
- else if (at != moved.position && !moved.conductor.isNull())
- {
- //He let go where this line runs across nothing, having
- //really brought the label there: the entry of the wire it
- //came from goes with it, while the label itself stays
- //exactly where he put it and merely names no wire at all
- //any more. Not moved means not touched at all, so picking
- //a label up and letting it go again never empties
- //anything.
- landed.conductor = QUuid();
- }
-
- if (freed)
- {
- //No wire names this core any more: it goes back to being a
- //free slot of the cable and stops being drawn, and the
- //refresh which follows empties the cable field of the wire
- //it used to name -- an entry follows its label, and this
- //label has come to rest where it may not name anything.
- m_cable->removeCore(core);
- }
- else
- {
-
- if (landed != moved) {
- m_cable->setCore(landed);
- }
- }
-
- const QList after = m_cable->usedCores();
- if (after != before) {
- //A core which has just been put down for the first time is
- //not being moved, and the step which takes it back is named
- //for what it did rather than for where it went.
- const QString text = before_cores
- ? QCoreApplication::translate("ChangeCableCoresCommand",
- "Place a cable core")
- : QString();
- diagram()->undoStack().push(
- new ChangeCableCoresCommand(m_cable, before, after, text));
- }
- return true;
-}
-
-/**
- @brief CablePart::takeCoreOffLine
- Take one core off this very line: its colour label disappears from
- the sheet and the core goes back to being a free slot of its cable,
- listed among the ones which stand nowhere -- the exact opposite of
- putting one down from that list.
-
- Only a core standing on this line is taken off, never one of the
- other sections of the same cable: a cable running over two folios
- stands on two lines, and the one whose menu was asked for is the one
- he is working on.
-
- Whatever it named goes with it: the refresh which follows empties
- the cable field of the wire it used to name, since an entry follows
- the label it stands for, and this label no longer stands anywhere.
-
- One undo step, and a single undo puts the label back exactly where
- it stood.
- @param core which core standing on this line is taken off
- @return true when a core was taken off
-*/
-bool CablePart::takeCoreOffLine(int core)
-{
- if (!m_cable || !diagram() || !diagram()->project()) return false;
-
- const CableCore there = m_cable->core(core);
- if (there.core != core || there.part != m_part_uuid) return false;
-
-
- const QList before = m_cable->usedCores();
- m_cable->removeCore(core);
- const QList after = m_cable->usedCores();
- if (after == before) return false;
-
- diagram()->undoStack().push(new ChangeCableCoresCommand(
- m_cable, before, after,
- QCoreApplication::translate("ChangeCableCoresCommand",
- "Remove a core from the line")));
- return true;
-}
-
-/**
- @brief CablePart::reconcileCores
- Where the colour labels of this line stand while the line is being
- moved: exactly where they stood, taken along by however far the line
- has come.
-
- Every label is carried along on every single step of the gesture --
- the trunk line, the description above it and the colour labels of the
- cores are one thing and move as one thing. Nothing is worked out
- again while he drags, so no label ever jumps, ever lands on a
- neighbour's wire or ever disappears because the line came a little
- too short: pulling the line back puts every label exactly back where
- it was.
-
- The place a label belongs to is worked out afterwards, when the
- line is let go (settleCores), and while a single core is moved on
- its own -- gestures which are about one wire rather than about the
- whole line, and which read the wire off the place of the label
- rather than sending the label off to find one.
- @param cores the whole set of assignments, changed in place
- @param delta how far the line has come in this gesture
-*/
-void CablePart::reconcileCores(QList &cores, const QPointF &delta) const
-{
- for (CableCore &core : cores)
- {
- if (core.part != m_part_uuid) continue;
-
- core.position += delta;
- }
-}
-
-/**
- @brief CablePart::carryCompanions
- Every other line he picked up with this one comes along, with the
- same step and with the colours of its own cores: a selection of
- lines is one thing to move.
-
- Nothing of them is written while he drags -- they are drawn where
- the gesture has brought them, exactly the way this line draws its
- own labels, so pulling back puts every one of them back where it
- was and letting go works the entries out afterwards.
- @param delta how far the line under his finger has come
-*/
-void CablePart::carryCompanions(const QPointF &delta)
-{
- for (const CarriedLine &carried : std::as_const(m_carried))
- {
- CablePart *other = carried.part;
- if (!other) continue;
-
- other->setLine(carried.p1 + delta, carried.p2 + delta);
- other->m_line_moved = true;
- other->m_line_drag_cores = carried.cores;
- other->reconcileCores(other->m_line_drag_cores, delta);
- other->update();
- }
-}
-
-/**
- @brief CablePart::carrySelection
- Everything else he marked at the same time comes along too: the
- elements, the texts and the images of the same selection are moved
- by the very step this line has taken, so that one gesture moves
- whatever the selection is made of instead of two halves of it going
- different ways -- the sheet has its own way of moving those, and
- this is the only place which knows how far the lines have come.
-
- The step handed over is the part of the gesture the sheet has not
- been told yet: what this line measures is how far it has come all in
- altogether, from where the press found it, while the sheet takes one
- step at a time.
- @param total_delta how far this line has come since the press
-*/
-void CablePart::carrySelection(const QPointF &total_delta)
-{
- if (!diagram()) return;
-
- ElementsMover &mover = diagram()->elementsMover();
- if (!m_mover_started)
- {
- //Once per gesture rather than once per step: when nothing
- //but lines is marked the sheet has nothing to move of its
- //own, and must not be asked to start over at every step.
- m_mover_started = true;
- m_told_mover = QPointF();
- mover.beginMovement(diagram(), this);
- }
-
- const QPointF step = total_delta - m_told_mover;
- m_told_mover = total_delta;
- if (!step.isNull()) {
- mover.continueMovement(step);
- }
-}
-
-/**
- @brief CablePart::settleCompanions
- Where every other line of this gesture comes to rest, worked out
- exactly the way this line's own is: from where its colour labels
- have come to stand, asking before anything changes hands.
-
- The lines are settled one after the other rather than all at once,
- so that a wire one of them lets go of is already free for the next
- one instead of being asked about as if it were still taken.
- @param steps receives one undo step per line he let go of
- @return false when he cancelled one of their questions, in which
- case nothing at all has to be written and the caller puts every
- line back where it was picked up
-*/
-bool CablePart::settleCompanions(QList &steps)
-{
- for (const CarriedLine &carried : std::as_const(m_carried))
- {
- CablePart *other = carried.part;
- if (!other || !other->m_cable) continue;
-
- QList after = other->m_line_drag_cores;
- other->readyToSettle(after);
-
- if (!other->settleCores(after)) {
- return false;
- }
-
- other->m_cable->setCores(after);
- steps.append(new MoveCablePartCommand(other->m_cable, other,
- carried.p1, carried.p2,
- other->m_p1, other->m_p2,
- carried.cores, after));
- }
- return true;
-}
-
-/**
- @brief CablePart::putCompanionsBack
- Put every other line of this gesture back to where he picked it
- up, cores included: the wire fields are rewritten along the way,
- so that not one entry of the refused move is left standing.
-*/
-void CablePart::putCompanionsBack()
-{
- for (const CarriedLine &carried : std::as_const(m_carried))
- {
- CablePart *other = carried.part;
- if (!other) continue;
-
- other->setLine(carried.p1, carried.p2);
- if (other->m_cable) {
- other->m_cable->setCores(carried.cores);
- }
- other->m_line_moved = false;
- other->m_line_drag_cores.clear();
- other->update();
- }
-}
-
-/**
- @brief CablePart::beginLineGesture
- Where this line stood when the sheet began moving the selection it
- is part of, kept here so that every step of that movement can carry
- the line -- and its colour labels, which are written on it -- along
- with it, exactly as if the finger had been on the line itself.
-
- Nothing is carried along by this line: the sheet carries every line
- of the selection itself, one after the other, so no line takes its
- neighbours along a second time.
-*/
-void CablePart::beginLineGesture()
-{
- m_move_p1 = m_p1;
- m_move_p2 = m_p2;
- m_line_moved = false;
- m_line_drag_cores.clear();
- m_carried.clear();
- m_cores_before = m_cable ? m_cable->usedCores() : QList();
- //Where its own labels stand when the gesture begins: they go on
- //standing wherever the line is carried to, and undo has to know
- //where they were before that.
- m_line_drag_cores = m_cores_before;
- m_stretch_end = 0;
- m_dragged_core = -1;
-}
-
-/**
- @brief CablePart::continueLineGesture
- Take one more step of a move the sheet is driving: the line follows
- by that much and every colour label standing on it comes along with
- it, so line, description and colours move as one piece and no label
- ever jumps, lands on a neighbour's wire or disappears under his
- hand.
- @param delta how far to come, in scene units
-*/
-void CablePart::continueLineGesture(const QPointF &delta)
-{
- if (!m_cable || delta.isNull()) return;
-
- setLine(m_p1 + delta, m_p2 + delta);
- m_line_moved = true;
- //Step by step rather than measured from where the gesture
- //began: the sheet hands one step over at a time
- reconcileCores(m_line_drag_cores, delta);
- update();
-}
-
-/**
- @brief CablePart::settleLineGesture
- Where this line comes to rest, worked out the same way as after a
- drag of its own: which wire every colour label has come to stand on,
- asked before anything changes hands, and no label moved for it.
-
- The step is handed over rather than pushed, so that the movement of
- the whole selection ends up as one single undo step.
- @param steps receives the undo step of this line, if any
- @return false when he called the gesture off -- this line is already
- back where it began then, and the sheet puts the rest back too
-*/
-bool CablePart::settleLineGesture(QList &steps)
-{
- if (!m_line_moved) return true;
- //Back where it began: nothing was carried anywhere, so there is
- //nothing to work out and nothing to undo
- if (m_p1 == m_move_p1 && m_p2 == m_move_p2) return true;
-
- if (!m_cable || !diagram())
- {
- abortLineGesture();
- return true;
- }
-
- QList after = m_line_drag_cores;
- readyToSettle(after);
-
- if (!settleCores(after))
- {
- abortLineGesture();
- return false;
- }
-
- m_cable->setCores(after);
- steps.append(new MoveCablePartCommand(m_cable, this,
- m_move_p1, m_move_p2,
- m_p1, m_p2,
- m_cores_before, after));
- return true;
-}
-
-/**
- @brief CablePart::abortLineGesture
- Put this line back where the gesture found it: the line goes back
- to its place and its cable gets the labels it held at that moment,
- whether they were written down during this gesture or not.
-*/
-void CablePart::abortLineGesture()
-{
- if (m_line_moved)
- {
- if (m_cable) {
- m_cable->setCores(m_cores_before);
- }
- setLine(m_move_p1, m_move_p2);
- update();
- }
- m_line_moved = false;
- m_line_drag_cores.clear();
- m_cores_before.clear();
-}
-
-/**
- @brief CablePart::doneLineGesture
- The gesture is over for this line: forget where it began, the step
- which was worked out from it having been handed over already.
-*/
-void CablePart::doneLineGesture()
-{
- m_line_moved = false;
- m_line_drag_cores.clear();
- m_cores_before.clear();
- m_stretch_end = 0;
- m_dragged_core = -1;
-}
-
-/**
- @brief CablePart::readyToSettle
- Make a set of carried cores ready to be written into the cable:
- this line's own labels wherever the gesture has brought them, and
- every other section of the cable exactly as the cable holds it at
- this moment.
-
- The other sections are read back rather than carried, because
- another line of this very gesture may have just settled and
- written them -- and a core which is no longer there has just been
- taken by another cable of the gesture, which makes it that
- cable's free slot rather than one of ours.
- @param cores the carried set, corrected in place
-*/
-void CablePart::readyToSettle(QList &cores) const
-{
- if (!m_cable) return;
-
- for (int i = cores.size() - 1; i >= 0; --i)
- {
- const bool mine = cores.at(i).part == m_part_uuid;
- if (m_cable->hasCore(cores.at(i).core)) {
- if (!mine) {
- cores[i] = m_cable->core(cores.at(i).core);
- }
- } else {
- cores.removeAt(i);
- }
- }
-}
-
-/**
- @brief CablePart::wireFor
- Which wire this line runs across at the very place a colour label
- stands, so that the entry of a wire follows the label instead of
- the label being sent off to find a wire.
-
- The nearest crossing which is close enough wins, and where two are
- as close as that, the wire the core already names wins the draw: a
- label standing between two crossings goes on naming the one it had
- rather than sliding onto the other, while a label dropped straight
- onto another wire does take that one. Close enough means close
- enough for the slash and the wire to read as one thing -- and
- nothing at all that close means no wire: a label is never moved to
- reach one, it keeps whatever it already names and stands where the
- user left it.
- @param crossings every place this line runs across a wire
- @param core the core being worked out
- @param at where its label stands, in scene coordinates
- @return the wire to give the core, or nullptr
-*/
-Conductor *CablePart::wireFor(const QList &crossings,
- const CableCore &core, const QPointF &at) const
-{
- const CableCrossing *nearest = nullptr;
- const qreal tolerance = CableManager::bind_tolerance;
- qreal nearest_distance = tolerance;
-
- for (const CableCrossing &crossing : crossings)
- {
- if (!crossing.conductor) continue;
-
- const qreal distance = QLineF(crossing.position, at).length();
- if (distance > tolerance) continue;
- if (nearest && distance >= nearest_distance) {
- //Where it already stands wins a draw, so that a label
- //between two crossings never slides from one to the other
- if (distance > nearest_distance
- || crossing.conductor->uuid() != core.conductor) {
- continue;
- }
- }
-
- nearest = &crossing;
- nearest_distance = distance;
- }
- return nearest ? nearest->conductor : nullptr;
-}
-
-/**
- @brief CablePart::askClaim
- The question every takeover is asked with: three answers, in
- French, of which the third one is the way out of the whole gesture
- -- he has let go in the wrong place, and would rather have nothing
- happen at all than take a wire he did not mean to take.
- @param parent the window the question is asked in
- @param title the title of the question
- @param question what is being asked
- @return what he answered
-*/
-CablePart::ClaimAnswer CablePart::askClaim(QWidget *parent,
- const QString &title,
- const QString &question) const
-{
- QMessageBox box(QMessageBox::Question, title, question, QMessageBox::NoButton, parent);
- QPushButton *take = box.addButton(
- tr("Yes", "Prendre le conducteur à l'autre câble."),
- QMessageBox::YesRole);
- QPushButton *leave = box.addButton(
- tr("No", "Laisser le conducteur à l'autre câble, on ne change rien."),
- QMessageBox::NoRole);
- QPushButton *cancel = box.addButton(
- tr("Cancel", "Annuler le geste entier : rien n'est écrit, la ligne et ses couleurs reviennent où elles étaient."),
- QMessageBox::RejectRole);
- //The safe answer is the one under his finger, and the escape
- //key throws the whole gesture away rather than taking a wire.
- box.setDefaultButton(leave);
- box.setEscapeButton(cancel);
- box.exec();
-
- const QAbstractButton *clicked = box.clickedButton();
- if (clicked == take) return ClaimAnswer::Take;
- if (clicked == leave) return ClaimAnswer::Leave;
- return ClaimAnswer::Cancel;
-}
-
-/**
- @brief CablePart::mayClaim
- Whether a wire may be given to this cable. A wire another cable
- already describes is asked about first: taking it empties the cable
- field of that other cable, and that is not something a drop or a
- movement does behind the back of the user.
- @param conductor the wire about to be taken
- @return what he answered
-*/
-CablePart::ClaimAnswer CablePart::mayClaim(Conductor *conductor) const
-{
- if (!conductor || !m_cable || !diagram() || !diagram()->project()) {
- return ClaimAnswer::Leave;
- }
-
- const QUuid holder = conductor->properties().m_cable_uuid;
- if (holder.isNull() || holder == m_cable->uuid()) return ClaimAnswer::Take;
-
- const Cable *other = CableManager::cableByUuid(diagram()->project(), holder);
- const QString other_name = other ? other->designation() : QString();
-
- const QString text = other_name.isEmpty()
- ? tr("This conductor is already described by another cable. Do you want to assign it to cable %1?",
- "Un conducteur ne peut appartenir qu'à un câble : on demande avant de le prendre à l'autre.")
- : tr("This conductor is already described by cable %1. Do you want to assign it to cable %2?",
- "Un conducteur ne peut appartenir qu'à un câble : on demande avant de le prendre à l'autre.");
-
- QWidget *parent = nullptr;
- if (!diagram()->views().isEmpty()) {
- parent = diagram()->views().constFirst();
- }
-
- return askClaim(parent,
- tr("Conductor already assigned", "Un conducteur ne peut appartenir qu'à un câble."),
- other_name.isEmpty() ? text.arg(m_cable->designation())
- : text.arg(other_name, m_cable->designation()));
-}
-
-/**
- @brief CablePart::mayClaimSeveral
- The same question as mayClaim(), but asked once for a whole handful
- of conductors: pulling a line across several wires which another
- cable already describes must ask about them too, rather than either
- quietly taking them or quietly leaving them -- one question, one
- answer, and the other cable releases every wire he says yes to.
- @param conductors the wires which other cables hold
- @return what he answered
-*/
-CablePart::ClaimAnswer CablePart::mayClaimSeveral(const QList &conductors) const
-{
- if (conductors.isEmpty() || !m_cable || !diagram() || !diagram()->project()) {
- return ClaimAnswer::Leave;
- }
-
- //Which cables hold those wires: he is told the name of one of
- //them, and that is the one he is most likely to be looking for.
- QString other_name;
- QSet others;
- for (const Conductor *conductor : conductors)
- {
- const QUuid holder = conductor->properties().m_cable_uuid;
- if (holder.isNull() || holder == m_cable->uuid()) continue;
- others.insert(holder);
- }
- if (others.isEmpty()) return ClaimAnswer::Take;
-
- if (others.size() == 1) {
- const Cable *other = CableManager::cableByUuid(diagram()->project(), *others.constBegin());
- if (other) other_name = other->designation();
- }
- //Several other cables at once: no one name would say it all.
- const bool unnamed = (others.size() != 1) || other_name.isEmpty();
-
- const int n = conductors.size();
- const QString title = n <= 1
- ? tr("Conductor already assigned", "Un conducteur ne peut appartenir qu'à un câble.")
- : tr("Conductors already assigned", "Plusieurs conducteurs sont pris en une seule fois.", n);
-
- const QString text = n <= 1
- ? (unnamed
- ? tr("This conductor is already described by another cable. Do you want to assign it to cable %1?",
- "Un conducteur ne peut appartenir qu'à un câble : on demande avant de le prendre à l'autre.")
- : tr("This conductor is already described by cable %1. Do you want to assign it to cable %2?",
- "Un conducteur ne peut appartenir qu'à un câble : on demande avant de le prendre à l'autre."))
- : (unnamed
- ? tr("%n conductors are already described by other cables. Do you want to assign them to cable %1?",
- "On prend plusieurs conducteurs à la fois : on demande avant de les prendre aux autres câbles.", n)
- : tr("%n conductors are already described by cable %1. Do you want to assign them to cable %2?",
- "On prend plusieurs conducteurs à la fois : on demande avant de les prendre à l'autre câble.", n));
-
- const QString question = unnamed
- ? text.arg(m_cable->designation())
- : text.arg(other_name, m_cable->designation());
-
- QWidget *parent = nullptr;
- if (!diagram()->views().isEmpty()) {
- parent = diagram()->views().constFirst();
- }
-
- return askClaim(parent, title, question);
-}
-
-/**
- @brief CablePart::settleCores
- Which wire each core names once the line has come to rest: read
- off where the colour labels have stood themselves, so that moving a
- line as a whole updates the entries of the wires instead of leaving
- them behind on the wires it has gone away from.
-
- While he drags, every label is merely carried along, so nothing
- jumps, nothing lands on a neighbour's wire and nothing disappears
- because the line came a little short -- the drawing stays readable
- over the whole gesture, and pulling the line back puts every label
- exactly back where it was. Which wire each core names is worked out
- once, at the very end, and always from the place of the label: a
- label standing on a wire gives that wire to its core, and the wire
- it came from keeps no binding, so the refresh which follows empties
- its cable field. Not one label is moved here. A label which came
- to rest beside a wire rather than on it -- a whole grid step away,
- which is as far as one pull of the line can carry it -- lets go of
- that wire, because it no longer stands on it: an entry is always
- where its colour label stands, and the grid is what puts a label
- down on a crossing in the first place, so standing on that
- crossing is what counts. When the line has been drawn off into an
- empty part of the sheet, where it crosses no wire at all, every one
- of its cores lets go, so that no entry is left standing on a wire
- the label has gone away from. Pulling the line back puts every
- label on a crossing again, and with it the entries they had.
-
- The whole set is worked out at once, and not core after core in the
- order they happen to be stored. Otherwise a label coming to rest on
- a wire another label of this very line is standing on at this moment
- would be turned away for a core which is moving off that very wire
- in the same gesture -- and its entry would go on standing on a wire
- the line has gone away from, which is exactly the untidy entry that
- has to disappear. A label which does not move keeps its wire all the
- same, and where two labels want one wire, the first of them has it
- and the other keeps what it had.
-
- A core which has no wire at all yet -- a copy put down on a folio
- it was not wired on, because a copy is carried whole even where
- there is nothing to wire -- takes the wire its own label stands on,
- if there is one: pulling a copied line into place is what wires it.
-
- Only wires nobody else holds are taken -- and when the line comes
- to rest on wires another cable already describes, he is asked about
- them once, before anything is taken, naming the other cable and
- answering no unless he says yes. The answer covers the whole move:
- either those wires change hands and the other cable releases them,
- or none of them does.
-
- Whichever way he answers, a core whose label has come to rest on
- such a wire and names none of its own any more stops being a core
- of its cable: it goes back to being a free slot, so that neither its
- slash nor its colour is drawn on a wire which is not its own, and
- its entry leaves the wire the label went away from -- an entry
- following its label just as it does everywhere else, so that no
- entry is left standing where no colour label stands. Yes makes the
- other cable's core the free one instead; No makes it this cable's
- own, and the other cable keeps its wire either way. That is why
- both answers look the same on the sheet: one label standing there,
- and a free slot in the list of existing cables.
-
- And he may call the whole gesture off: nothing is then written at
- all, the line goes back to where he picked it up, and the drawing
- is once more what it was before he started.
-
- @param cores the whole set of assignments, changed in place
- @return false when he cancelled the gesture
-*/
-bool CablePart::settleCores(QList &cores) const
-{
- if (!m_cable || !diagram()) return true;
-
- const QList crossings = CableManager::crossedConductors(
- diagram(), QLineF(m_p1, m_p2));
- //A line which runs across nothing at all is not a reason to call
- //the move off: it is the moment every core of this section lets
- //go of the wire it came from, since an entry belongs to a wire
- //the cable still touches. So the walk below happens either way,
- //and then simply comes out with nothing to keep.
-
- //Which wire every core ends up with is worked out for the whole
- //set before a single one of them is written down -- a dry run
- //first, which merely collects the wires of other cables on the
- //way, so that he is asked about all of them before any of them
- //changes hands.
- //
- //Working it out for the set as a whole, and not core by core in
- //the order they happen to be stored, is what makes the entries
- //come out right: a label coming to rest on a wire another label
- //of this very line is standing on at this moment must have that
- //wire, because the other label is moving off it in the same
- //gesture -- whereas a label which does not move, and keeps the
- //wire it names, must not be robbed of it.
- const auto settle_once = [this, &crossings](
- QList &list,
- const bool dry_run,
- QList *foreign_wires,
- const bool take_foreign)
- {
- //A wire another cable already describes only changes hands
- //when he has said yes to taking it away from that one.
- const auto is_foreign = [this](Conductor *wire) {
- return wire && !wire->properties().m_cable_uuid.isNull()
- && wire->properties().m_cable_uuid != m_cable->uuid();
- };
-
- //The wire each colour label of this line is standing on
- //right now -- worked out for every one of them first, so
- //that which wire a core gets does not depend on which core
- //is looked at first. None at all when a label stands on no
- //wire, and then that core lets go of whatever it named.
- QHash wanted;
- for (const CableCore &core : list) {
- if (core.part != m_part_uuid) continue;
- wanted.insert(core.core, wireFor(crossings, core, core.position));
- }
-
- //Who lets go of nothing: a label standing on its own wire,
- //and every label of another part of this cable, which this
- //line never takes anything from. A label standing on no
- //wire at all is not among them: it lets go of what it
- //named, a whole grid step away from a crossing being
- //already beside that wire rather than on it.
- QSet keepers;
- //Who ends up naming no wire at all: a label which has come
- //to rest on a wire another cable holds, and he has said no
- //to taking it. Such a core goes back to being a free slot of
- //this cable -- exactly what the other cable's core becomes
- //when he says yes -- so neither its slash nor its colour is
- //drawn on a wire which is not its own, and its entry leaves
- //the wire the label came from with it. The other cable keeps
- //its wire either way.
- QSet refuses;
- for (const CableCore &core : list)
- {
- if (core.part != m_part_uuid) continue;
- Conductor *wire = wanted.value(core.core, nullptr);
- //Stands on no wire of this line: not a keeper, so it
- //lets go of whatever it named further down.
- if (!wire) continue;
-
- if (wire->uuid() == core.conductor) {
- keepers.insert(core.core);
- continue;
- }
- if (!take_foreign && is_foreign(wire)) {
- refuses.insert(core.core);
- }
- }
-
- //Which core ends up with which wire, over and over until it
- //stops moving: a core sent away from its own wire sends the
- //one waiting for that wire away too, and that one may in
- //turn free the wire the first one was after. The keepers
- //only ever grow, so this cannot go round for ever.
- QSet given_wires;
- bool grew = true;
- while (grew)
- {
- grew = false;
- given_wires.clear();
-
- //The wires nobody is letting go of
- QSet staying;
- for (const CableCore &core : list)
- {
- if (core.conductor.isNull()) continue;
- if (core.part != m_part_uuid || keepers.contains(core.core)) {
- staying.insert(core.conductor);
- }
- }
-
- for (const CableCore &core : list)
- {
- if (core.part != m_part_uuid
- || keepers.contains(core.core)
- || refuses.contains(core.core)) continue;
-
- Conductor *wanted_wire = wanted.value(core.core, nullptr);
- if (!wanted_wire) continue; //stands on no wire: a keeper already
-
- const QUuid uuid = wanted_wire->uuid();
- if (staying.contains(uuid) || given_wires.contains(uuid))
- {
- //Another label of this line goes on standing on it,
- //or has just been given it: this one keeps the wire
- //it names now rather than taking it away from under
- //a label which has come to rest on it.
- keepers.insert(core.core);
- grew = true;
- continue;
- }
- given_wires.insert(uuid);
- }
- }
-
- //The whole set has come to rest; now it can be written down
- //without anything shifting under the feet of the next core.
- //Cores which name no wire at all any more are collected on
- //the way and taken out of the set only once the walk is over,
- //since a list cannot be shortened while it is being walked.
- QList freed;
- for (CableCore &core : list)
- {
- if (core.part != m_part_uuid) continue;
-
- Conductor *wire = wanted.value(core.core, nullptr);
-
- if (refuses.contains(core.core))
- {
- //He said no, so this core names no wire at all any
- //more: it goes back to being a free slot of this
- //cable, exactly what the other cable's core becomes
- //when he says yes to taking its wire. Neither its
- //slash nor its colour stays standing on a wire which
- //is not its own, and its entry leaves the wire the
- //label came from with it -- while the other cable
- //keeps its wire either way.
- if (!dry_run) {
- freed << core.core;
- }
- continue;
- }
-
- if (!wire)
- {
- //The label has come to rest where this line crosses
- //nothing: the entry of the wire it came from goes
- //with it, while the label itself stays exactly
- //where he put it and merely names no wire at all
- //any more -- an entry follows its label, and stops
- //where its label stopped. A whole grid step away
- //from a crossing is already beside that wire
- //rather than on it, which is the case he means.
- if (!dry_run && !core.conductor.isNull()) {
- core.conductor = QUuid();
- }
- continue;
- }
-
- if (keepers.contains(core.core))
- {
- continue;
- }
-
- const QUuid uuid = wire->uuid();
- //Another cable holds this wire: it only changes hands
- //when he has said yes to taking it away from that one.
- const bool foreign = is_foreign(wire);
- if (foreign && foreign_wires) {
- foreign_wires->append(wire);
- }
- if (!dry_run) {
-
- wire->setUuid(uuid);
- //A wire is one piece of wire: whichever cable held it
- //before has to let it go, or its entry would stand on
- //the wire all the same.
- if (foreign) {
- CableManager::claimConductor(diagram()->project(), wire, m_cable.data());
- wire->setUuid(uuid);
- }
- core.conductor = uuid;
- //The wire this core has just left keeps no binding, so
- //the refresh which follows empties its cable field: an
- //entry follows its label, and stops where its label
- //stopped
- }
- }
-
- //A core which names no wire is not a core of this cable any
- //more: it goes back to being a free slot, and takes its slash
- //and its colour off the sheet with it -- the very thing the
- //other cable's core becomes when he says yes to taking its
- //wire, so that both answers behave the same way.
- for (int i = list.size() - 1; i >= 0; --i) {
- if (list.at(i).part == m_part_uuid
- && freed.contains(list.at(i).core)) {
- list.removeAt(i);
- }
- }
- };
-
- QList trial = cores;
- QList foreign_wires;
- settle_once(trial, true, &foreign_wires, true);
-
- //The whole move is asked about at once, and the wires of other
- //cables are taken either all together or not at all.
- ClaimAnswer answer = ClaimAnswer::Leave;
- if (!foreign_wires.isEmpty()) {
- answer = mayClaimSeveral(foreign_wires);
- if (answer == ClaimAnswer::Cancel) return false;
- }
-
- settle_once(cores, false, nullptr, answer == ClaimAnswer::Take);
- return true;
-}
-
-/**
- @brief CablePart::itemChange
- Fold any translation of the item into its own geometry: this item
- works in scene coordinates and keeps itself at the origin, so that
- one number -- the trunk line -- is all the cable ever has to store.
-
- The colour labels come along with it. A line moved as a whole --
- by a paste, by a duplicate, or together with the rest of a selection
- -- is one thing, and its labels belong to it, so they travel rather
- than staying behind at the place the line was copied from. The trunk
- line dragged by its own gesture does not come through here: it is
- given its new geometry directly, which is what lets that gesture
- carry the labels step by step and settle them only at the end.
- @param change
- @param value
- @return
-*/
-QVariant CablePart::itemChange(GraphicsItemChange change, const QVariant &value)
-{
- if (change == ItemSelectedHasChanged)
- {
- //The two blue points at the ends come and go with the
- //selection, so the moment a line is picked it has to be
- //drawn again -- otherwise the points only appear at the
- //next redraw of any other kind.
- update();
- }
- if (change == ItemSceneHasChanged)
- {
- //The folio this line stands in is what its references to the
- //other folios of the cable are worked out from, and it is
- //also what has to be listened to: only now, once there is
- //one, can they be kept up to date -- and once this line
- //leaves that folio, they have to stop.
- setUpXrefHooks();
- }
- if (change == ItemPositionHasChanged)
- {
- const QPointF new_pos = value.toPointF();
- if (!new_pos.isNull())
- {
- prepareGeometryChange();
- m_p1 += new_pos;
- m_p2 += new_pos;
- if (m_cable) {
- m_cable->movePartCores(m_part_uuid, new_pos);
- }
- syncGeometry();
- update();
- QGraphicsItem::setPos(QPointF());
- }
- }
- return QetGraphicsItem::itemChange(change, value);
-}
-
-/**
- @brief CablePart::editProperty
- Open the dialog which edits the properties of the cable this line
- belongs to -- the number, the type, the installation, the place. They
- belong to the cable, so editing them here changes them for every line
- of that cable, on every folio.
-*/
-void CablePart::editProperty()
-{
- if (!m_cable || !diagram() || diagram()->isReadOnly()) return;
-
- //Asking for the whole cable is another thing than putting one
- //of its cores down: anything which waited for a click stops
- //waiting -- on this line or on any other, since only one of
- //them can wait at a time -- and he can start over from this
- //dialog.
- cancelPlacing();
-
- CablePropertiesDialog dialog(m_cable, diagram(), this,
- diagram()->views().isEmpty()
- ? nullptr : diagram()->views().constFirst());
- dialog.exec();
-}
-
-/**
- @brief CablePart::startPlacingCore
- Give this line the mouse and wait for the click which puts that core
- of its cable down: the core follows the line wherever the mouse comes
- near it, drawn where the click would leave it -- on the line, at a
- grid step, and never past either end of it.
-
- It is always this very section which takes the core, never the first
- section of the cable: a cable running over two folios stands on two
- lines, and the one whose dialog was asked for is the one he is
- working on.
- @param core which core of the cable is being put down
-*/
-void CablePart::startPlacingCore(int core)
-{
- if (!m_cable || !diagram() || diagram()->isReadOnly()) return;
- if (core < 0 || core >= m_cable->coreCount() || m_cable->hasCore(core)) return;
- if (m_dragged_core >= 0 || m_stretch_end > 0 || m_line_moved) return;
- if (m_pending_core == core)
- {
- //He asks again for the very core which is waiting already:
- //there is nothing to arm a second time, but the mouse still
- //goes to the line -- asking twice has to behave exactly like
- //asking once, and the mark is where he is being sent to.
- sendMouseToLine();
- return;
- }
-
- cancelPlacement();
-
- //Only one line at a time may wait for its click, and it is the
- //one whose dialog was asked for: a line standing on another
- //folio lets go as well, so Escape and the right button always
- //find the very line he is working on.
- cancelPlacing();
-
- m_pending_core = core;
- s_placing = this;
- m_drag_scene = m_p1;
- m_pending_valid = false;
-
- //Where the mouse stands at this very moment, so that a dialog
- //which happened to cover the line does not have to be closed by
- //a click the core would take for itself -- and then straight to
- //the line, which is where the next click belongs.
- sendMouseToLine();
-
- if (!isSelected()) {
- setSelected(true);
- }
- grabMouse();
- setCursor(Qt::CrossCursor);
- update();
-}
-
-/**
- @brief CablePart::sendMouseToLine
- Send the mouse to this line, onto the grid step the mark of the
- waiting core stands on, so that the very next click is the click
- which puts that core down: he picks the core out of a list, and the
- line may well be across the screen from where that list stands.
-
- The mouse goes wherever the line stands, as long as that line is
- standing in some window he can see -- there is nothing else to ask.
- It is never the mark which is held back: he asked for this core, the
- very next click is the one which puts it down, and the mouse is
- meant to be there when he clicks.
-*/
-void CablePart::sendMouseToLine()
-{
- if (!scene() || scene()->views().isEmpty()) return;
-
- QGraphicsView *view = scene()->views().constFirst();
- const QPointF scene_pos = view->mapToScene(view->mapFromGlobal(QCursor::pos()));
- m_drag_scene = placePosition(scene_pos);
- m_pending_valid = QLineF(scene_pos, m_drag_scene).length() <= place_grab;
- view->setFocus();
-
- //The window he is actually looking at: a folio may stand in
- //more than one window, and only the one which is on screen can
- //be sent the mouse. The mark itself is drawn on the line
- //whenever he asks for the core, seen or not -- whether a click
- //keeps it or sends it away is worked out from the click itself
- //(see mousePressEvent), so nothing here may stop the mouse
- //from going where he just told it to go.
- QGraphicsView *shown = nullptr;
- for (QGraphicsView *candidate : scene()->views())
- {
- if (candidate && candidate->isVisible()) {
- shown = candidate;
- break;
- }
- }
-
- if (shown)
- {
- QWidget *viewport = shown->viewport();
- const QPoint there = viewport->mapFrom(shown, shown->mapFromScene(m_drag_scene));
- const QPoint target = viewport->mapToGlobal(there);
- const QPointF mark = m_drag_scene;
- QPointer self(this);
-
- QCursor::setPos(target);
-
- //And again a little later: a pop-up which has just closed
- //keeps the mouse in hand for a moment longer, and the system
- //only lets go once the menu is really gone from the screen.
- //A jump asked for earlier than that is dropped on the floor,
- //while one asked for afterwards always goes through -- so the
- //very same jump is asked for three times, as long as this
- //line is still the one waiting and its mark still stands on
- //that very spot.
- const int waits[] = {0, 70, 250};
- for (const int wait : waits)
- {
- QTimer::singleShot(wait, viewport, [self, target, mark]() {
- if (!self || self->m_pending_core < 0 || self->m_drag_scene != mark) return;
-
- //The same pop-up may have kept the mouse out of
- //this line's hands while it was closing: the line
- //waits for the click, so it has to be the one
- //holding the mouse.
- if (self->scene() && self->scene()->mouseGrabberItem() != self.data()) {
- self->grabMouse();
- }
-
- QCursor::setPos(target);
- });
- }
- }
- update();
-
-}
-
-/**
- @brief CablePart::cancelPlacement
- Stop waiting for the click which would put a core down, without
- putting anything down anywhere: the core goes back to being a free
- one in the dialog it came from.
-*/
-void CablePart::cancelPlacement()
-{
- if (s_placing == this) s_placing = nullptr;
- if (m_pending_core < 0) return;
-
- m_pending_core = -1;
- m_pending_valid = false;
- ungrabMouse();
- unsetCursor();
- update();
-}
-
-/**
- @brief CablePart::placingPart
- @return the line which waits for the click putting a core down right
- now, or nullptr when no line does
-*/
-CablePart *CablePart::placingPart()
-{
- return s_placing.data();
-}
-
-/**
- @brief CablePart::cancelPlacing
- End the wait of whichever line stands in it, without putting anything
- down anywhere: the core goes back to being a free one.
- @return true when a line was waiting for its click
-*/
-bool CablePart::cancelPlacing()
-{
- if (!s_placing) return false;
-
- s_placing->cancelPlacement();
- return true;
-}
-
-/**
- @brief CablePart::placeCore
- Put a core which is not on the sheet yet onto this line, standing
- where the mouse says: on a wire this line runs across it takes that
- wire like any other label would -- including asking before taking
- one another cable holds -- and where the line runs across nothing it
- merely stands and names no wire at all.
-
- The core belongs to this very section, so a cable standing on two
- folios gets it on the line he asked for rather than on the first one.
- @param core which core of the cable is being put down
- @param at where its label is to stand, in scene coordinates
- @return false when nothing was put down
-*/
-bool CablePart::placeCore(int core, const QPointF &at)
-{
- if (!m_cable || !diagram() || diagram()->isReadOnly()) return false;
- if (core < 0 || core >= m_cable->coreCount()) return false;
- if (m_cable->hasCore(core)) return false;
-
- //Every core the cable holds at this moment: the addition is
- //kept as one step of undo together with whatever the click
- //makes of it, so that taking it back leaves no trace.
- const QList before = m_cable->usedCores();
-
- CableCore fresh;
- fresh.core = core;
- fresh.part = m_part_uuid;
- fresh.conductor = QUuid();
- fresh.position = at;
- m_cable->setCore(fresh);
-
- if (!rebindCore(core, at, &before))
- {
- //He called the question off, so nothing is put down at
- //all: the core goes back to being a free one, and no step
- //of undo was written for what never happened.
- m_cable->setCores(before);
- return false;
- }
- return true;
-}
diff --git a/sources/cable/cablepart.h b/sources/cable/cablepart.h
deleted file mode 100644
index a3b8744fa..000000000
--- a/sources/cable/cablepart.h
+++ /dev/null
@@ -1,513 +0,0 @@
-/*
- 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 .
-*/
-#ifndef CABLEPART_H
-#define CABLEPART_H
-
-#include "cable.h"
-#include "cablemanager.h"
-#include "../qetgraphicsitem/qetgraphicsitem.h"
-
-#include
-#include
-#include
-#include
-#include
-
-class Cable;
-class CablePartData;
-class Conductor;
-class QUndoCommand;
-class QWidget;
-
-/**
- @brief The CablePart class draws one section of a cable: the trunk
- line of one folio, the number and the type of the cable at its left,
- and a slash with the colour of a core wherever that core crosses a
- conductor.
-
- All of it is a view of the Cable: the geometry lives in the cable
- itself, so lengthening the trunk line is purely optical. What a core
- wires is read off where its colour label stands: dropping a label
- gives that core the wire it lands on, and letting go of a moved line
- works the entries out the same way, from where the labels have come
- to rest. A label is never sent off to look for a wire and never
- moved to reach one -- it goes on standing where the user put it --
- and no gesture at all takes a core away from its cable.
-*/
-class CablePart : public QetGraphicsItem
-{
- Q_OBJECT
-
- public:
- explicit CablePart(const CablePartData &data, QGraphicsItem *parent = nullptr);
- explicit CablePart(QGraphicsItem *parent = nullptr);
- ~CablePart() override;
-
- enum {Type = UserType + 1012};
- int type() const override {return Type;}
-
- void setCable(Cable *cable);
- Cable *cable() const;
- QUuid partUuid() const;
- ///Bind the item to its cable once the project is loaded
- void refreshPending();
-
- void setLine(const QPointF &p1, const QPointF &p2);
- QLineF line() const;
- QPointF firstPoint() const {return m_p1;}
- QPointF secondPoint() const {return m_p2;}
-
- QRectF boundingRect() const override;
- QPainterPath shape() const override;
- void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
- QString name() const override;
-
- void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
- void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
- void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
- void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override;
- ///Shows the hand over a line which points at another folio
- void hoverMoveEvent(QGraphicsSceneHoverEvent *event) override;
- ///Opens the dialog editing the properties of the whole cable
- void editProperty() override;
-
- /**
- Give this line the mouse and wait for the next click to put
- down a core of its cable which is not on the sheet yet: the
- core follows this line wherever the mouse comes near it and
- the click leaves it standing there, snapped to the grid like
- anything else drawn.
-
- It is always this very section which takes it, never the
- first section of the cable: a cable running over two folios
- stands on two lines, and the one whose dialog was asked for
- is the one being worked on.
- @param core which core of the cable is being put down
- */
- void startPlacingCore(int core);
- ///Stop waiting for that click, putting nothing down at all
- void cancelPlacement();
-
- /**
- The line which waits for the click putting one of its cores
- down at this very moment, anywhere in the application, or
- nullptr when no line does.
-
- Only one line at a time may wait -- taking the core always
- belongs to the line whose dialog was asked for -- so Escape
- and the right button can simply ask here and always act on
- the right one, however many folios are open.
- */
- static CablePart *placingPart();
- /**
- End that wait wherever it stands, without putting anything
- down anywhere: the core goes back to being a free one.
-
- Used by the sheet itself, which gets the right button and
- Escape before any item does (see DiagramView).
- @return true when a line was waiting for its click
- */
- static bool cancelPlacing();
-
- /**
- Which core of this cable has its colour label standing at
- that very place on the sheet -- asked by the sheet itself
- when the right button comes down on a line, so that the
- menu which opens belongs to that one core rather than to
- the whole folio.
- @param scene_pos a point of the sheet, in scene coordinates
- @return the core standing there, -1 when none does
- */
- int coreAt(const QPointF &scene_pos) const;
- /**
- Take one core off this line: it names no wire any more and
- goes back to being a free slot of its cable, listed among
- the ones which stand nowhere -- the very opposite of
- putting one down from that list.
-
- Kept as one undo step, so a single undo puts the label
- back exactly where it stood.
- @param core which core standing on this line is taken off
- @return true when a core was taken off
- */
- bool takeCoreOffLine(int core);
-
- /**
- Which wire this line runs across at the very place a colour
- label stands -- so that the entry of a wire follows the label
- instead of the label being sent off to find a wire. Also asked
- by a copy being put down (CableCopy::wire), which must not
- move any label to reach a wire either.
- @param crossings every place this line runs across a wire
- @param core the core being worked out
- @param at where its label stands, in scene coordinates
- @return the wire to give the core, or nullptr
- */
- Conductor *wireFor(const QList &crossings,
- const CableCore &core, const QPointF &at) const;
-
- /**
- Move this line from outside: the sheet moving a whole
- selection of elements and lines at once asks every line
- of the selection to come along, one step after the other,
- exactly as if the finger had been on the line itself, so
- that one gesture moves everything which is marked instead
- of two halves of the selection going different ways.
-
- The calls come in order -- begin once, continue as often
- as the gesture needs, settle once for the whole
- selection, then either done or abort -- and a line which
- never moves simply has no undo step to hand over.
- */
- void beginLineGesture();
- ///Take one more step of such a move, by that much
- void continueLineGesture(const QPointF &delta);
- /**
- Where this line comes to rest, worked out the same way as
- after a drag of its own: which wire every colour label now
- stands on, asked before anything changes hands. The step
- of this line is handed over rather than pushed, so that
- the movement as a whole ends up as one single undo step.
- @param steps receives the undo step of this line, if any
- @return false when he called the gesture off
- */
- bool settleLineGesture(QList &steps);
- ///Put this line back where the gesture found it
- void abortLineGesture();
- ///The gesture is over for this line: forget where it began
- void doneLineGesture();
-
- /**
- One cross reference of this section as the PDF export
- needs it: where the text of the reference stands on this
- folio, and on the folio it points at where the section it
- names stands -- so that exporting makes it a clickable
- link the way a cross reference of an element already is.
-
- Both rectangles are in scene coordinates, each of its own
- folio.
- */
- struct PdfRef
- {
- ///The folio the reference points at
- Diagram *diagram = nullptr;
- ///Where the text of the reference stands on this folio
- QRectF here;
- ///Where the section it names stands on that folio
- QRectF there;
- };
- QList pdfRefs() const;
-
- protected:
- QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
-
- private:
- /**
- One line of the label at the left of the trunk line:
- what is written, which field of the cable it comes from
- and -- worked out from what that field asks for -- the
- font it is written with, the way it lines up and the
- rectangle it sits in.
-
- Every line carries its own font, because every field
- may have been given one of its own: Anlage, Ort and
- Désignation stand in one block, and their lines may
- very well no longer all be the same size once he has
- set them apart.
- */
- struct LabelLine
- {
- QString text;
- QString key;
- QFont font;
- Qt::Alignment align = Qt::AlignRight;
- qreal width = 0.0;
- qreal height = 0.0;
- QRectF rect;
- };
-
- /**
- One line of the label which points at a folio this cable
- runs on besides this one: that folio, the section of the
- cable standing there, and the text this project asks for
- such a reference -- the format chosen for the type
- "Cable" under Cross-references, written out once per folio,
- in the order the folios turn.
- */
- struct CrossRef
- {
- ///The other folio, which always exists while listed
- Diagram *diagram = nullptr;
- ///Which section of the cable stands on it
- QUuid part;
- ///The text as it is written on the drawing
- QString text;
- };
-
- QFont textFont() const;
- ///The font the colours of the cores are written with
- QFont coreFont() const;
- ///The font one named text of this cable is written with
- QFont fontOf(const QString &key) const;
- ///How one named text of this cable lines up at the line
- Qt::Alignment alignOf(const QString &key) const;
- /**
- How far the whole label at the left of the line is
- nudged on the drawing: over the line, under it, left
- or right of the end of it. A nudge only, which moves
- the texts and never the line.
- @return the distance in scene units
- */
- QPointF textOffset() const;
- /**
- How far the row of colours is nudged on the drawing:
- again a distance on the texts only, the slashes and
- the entries behind them staying where they stand.
- @return the distance in scene units
- */
- QPointF coreOffset() const;
- /**
- Where a text of this width starts, at the left end of
- the trunk line: before the line when it is set to end
- there (the way it always did), on the line when it is
- centred, after it when it starts there.
- @param key the field the text comes from
- @param width how wide the text is, in scene units
- @return the x of the left of the text
- */
- qreal textLeft(const QString &key, qreal width) const;
- bool trunkIsHorizontal() const;
- QList labelLines() const;
- /**
- The references to the other folios this cable runs on,
- one line each, stacked under the length of the cable:
- at the very bottom of everything the line says.
- @return those lines, in the order crossRefs() lists them
- */
- QList refLines() const;
- /**
- Every other folio this cable runs on, one entry each, in
- the order the folios turn -- empty when the cable runs on
- this folio alone, which is the usual case.
- */
- QList crossRefs() const;
- /**
- Which of those references stands at that very place, so
- that both the hand of the mouse and a double click know
- which line they are about.
- @param scene_pos a point of the sheet, in scene coordinates
- @return the index of that reference, -1 when none stands there
- */
- int crossRefAt(const QPointF &scene_pos) const;
- /**
- Send the mouse to the section of the cable which stands
- on the folio the reference at that place points at -- the
- very leap an element's cross reference takes, straight to
- the place to look at.
- @param scene_pos a point of the sheet, in scene coordinates
- @return true when the click stood on a reference
- */
- bool goToCrossRef(const QPointF &scene_pos);
- /**
- Keep the reference lines up to date with the project: a
- folio which is added, removed or moved changes what they
- say, and so does the format chosen under Cross-references.
- */
- void setUpXrefHooks();
- QString typeText() const;
- QString lengthText() const;
- QRectF labelRect() const;
- qreal underTop() const;
- QRectF typeRect() const;
- QRectF lengthRect() const;
- QRectF coreLabelRect(const QPointF &at, const QString &color) const;
- QPointF coreLabelPivot(const QPointF &at, const QString &color) const;
- QPointF coreDrawPosition(const CableCore &core) const;
- bool coreDrawn(const CableCore &core) const;
- QPointF dragPosition(const QPointF &scene_pos) const;
- /**
- Where the mouse would put down a core which waits to be
- put down: the mouse projected onto the line itself, at a
- grid step, and never past either end of the line -- the
- same coordinates a dragged label would be drawn at, only
- worked out from the mouse rather than from a gesture.
- @param scene_pos where the mouse stands, in scene coordinates
- @return where the slash would be drawn
- */
- QPointF placePosition(const QPointF &scene_pos) const;
- /**
- Snap a distance measured along the line to a grid step, in
- the coordinates of the sheet -- so a horizontal line steps
- along x and a vertical one along y. Holding Ctrl lets go of
- the grid, as everywhere else on the sheet.
- @param along how far from the first end of the line
- @return that distance at a grid step
- */
- qreal stepAlong(qreal along) const;
- void drawLabel(QPainter *painter, const QColor &ink) const;
- void drawCores(QPainter *painter, const QColor &ink) const;
- ///Draw the slash of one core and the colour written beside it
- void drawCoreMark(QPainter *painter, const QColor &ink,
- const QPointF &at, const QString &color) const;
- void syncGeometry();
- bool labelAt(int core, const QPointF &scene_pos) const;
- ///Answer to the question of taking a wire another cable holds
- enum class ClaimAnswer {
- ///Take it: the other cable lets it go
- Take,
- ///Leave it: nothing changes hands, the rest still happens
- Leave,
- ///Cancel: this whole gesture is called off, as if undone
- Cancel
- };
- /**
- @param core which core is being let go
- @param dropped_at where its label comes to rest
- @param before every core the cable held before this one was
- added to it, so that the one step of undo takes the addition
- back as well; nullptr while a core which is already on the
- sheet is merely moved
- @return false when the user called the whole thing off
- */
- bool rebindCore(int core, const QPointF &dropped_at,
- const QList *before = nullptr);
- /**
- Put a core which is not on the sheet yet onto this line: it
- stands where the mouse says, and if a wire runs across that
- very place it names that wire the same way a dragged label
- would. Called off means back to being a free core, with one
- step of undo to take it away again.
- @param core which core of the cable is being put down
- @param at where its label is to stand, in scene coordinates
- @return false when nothing was put down
- */
- bool placeCore(int core, const QPointF &at);
- /**
- Send the mouse to this line, to the grid step the mark of
- the waiting core stands on, so that the very next click is
- the one which puts the core down -- wherever he asked for
- the core from, and whether he asks for this very one a
- second time or not. The mouse goes there only where the
- line is really in sight: a folio hidden behind another
- one, or off the edge of the view, is left alone rather
- than jumped to.
- */
- void sendMouseToLine();
- void reconcileCores(QList &cores, const QPointF &delta) const;
- bool settleCores(QList &cores) const;
- ClaimAnswer mayClaim(Conductor *conductor) const;
- ClaimAnswer mayClaimSeveral(const QList &conductors) const;
- ClaimAnswer askClaim(QWidget *parent,
- const QString &title,
- const QString &question) const;
-
- private:
- ///The one line which waits for the click putting a core down,
- ///null once there is none: taking the core belongs to the line
- ///whose dialog was asked for, never to the first line of the
- ///cable, and the sheet has to be able to cancel that wait
- ///without knowing which scene it stands in
- static QPointer s_placing;
- QPointer m_cable;
- ///Identity of the cable this part belongs to, kept until the
- ///project has loaded it and setCable() can be called
- QUuid m_pending_cable;
- QUuid m_part_uuid;
- QPointF m_p1;
- QPointF m_p2;
-
- ///Core being dragged by its slash, -1 when none
- int m_dragged_core = -1;
- ///Core waiting to be put down by the next click, -1 when none
- int m_pending_core = -1;
- ///True while the mouse stands close enough to this line for
- ///the waiting core to be put down where it shows
- bool m_pending_valid = false;
- ///True between the click which put a core down and the second
- ///click Qt counts as a double click, so that a click which was
- ///there to place something does not open the dialog as well
- bool m_just_placed = false;
- ///Where the dragged slash follows the mouse
- QPointF m_drag_scene;
- QPointF m_press_scene;
- QPointF m_move_p1;
- QPointF m_move_p2;
- ///True while the trunk line is being dragged, so undo knows
- ///there is a move to take back
- bool m_line_moved = false;
- ///Which end of the trunk line is being pulled by one of its
- ///blue points: 0 none, 1 the first end, 2 the second
- int m_stretch_end = 0;
- ///Every core as it stands while the line is being dragged:
- ///every label is carried along by however far the line has
- ///come, so the line, its description and the colours of its
- ///cores move as one thing and no label ever jumps, lands on
- ///a neighbour's wire or disappears under his hand
- QList m_line_drag_cores;
- ///Every core as it was when the line was picked up, so undo
- ///can put the labels back with the line
- QList m_cores_before;
- ///Another line he picked up at the same time: a drag takes
- ///the whole selection with it, so each of them is carried
- ///along and each of them goes back to where it stood if he
- ///calls the gesture off
- struct CarriedLine
- {
- CablePart *part = nullptr;
- ///Where that line ran when the gesture began
- QPointF p1;
- QPointF p2;
- ///What its cable held when the gesture began
- QList cores;
- };
- QList m_carried;
-
- ///True when the sheet has been told that the rest of the
- ///selection comes along with this line (see carrySelection),
- ///so that it is told once per gesture rather than once per
- ///step -- and never again once it had nothing to move
- bool m_mover_started = false;
- ///How far of this gesture the sheet has already been told
- QPointF m_told_mover;
-
- ///The lines which keep the references to the other folios of
- ///this cable up to date, taken down and taken again whenever
- ///this section comes to stand in another folio
- QList m_xref_connections;
-
- void carryCompanions(const QPointF &delta);
- /**
- Everything else he marked at the same time comes along
- too -- the elements, the texts and the images of the same
- selection -- by the very step this line has taken, so
- that one gesture moves all of it instead of the lines
- going one way and the rest another.
-
- What this line measures is how far it has come all in
- altogether, from where the press found it, while the
- sheet takes a step at a time: so only the part it has not
- been told yet is handed over.
- @param total_delta how far this line has come since the press
- */
- void carrySelection(const QPointF &total_delta);
- bool settleCompanions(QList &steps);
- void putCompanionsBack();
- void readyToSettle(QList &cores) const;
-};
-
-#endif // CABLEPART_H
diff --git a/sources/cable/cablepropertiesdialog.cpp b/sources/cable/cablepropertiesdialog.cpp
deleted file mode 100644
index f489c4a44..000000000
--- a/sources/cable/cablepropertiesdialog.cpp
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- 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 .
-*/
-#include "cablepropertiesdialog.h"
-
-#include "cablecreatedialog.h"
-#include "cablepart.h"
-#include "cablepropertieswidget.h"
-#include "editcablecommand.h"
-#include "../diagram.h"
-#include "../qeticons.h"
-
-#include
-#include
-#include
-#include
-#include
-#include