From 2bb3343d2d2d4c9517b757b96fc1c6a90c34b458 Mon Sep 17 00:00:00 2001 From: Laurent Trinques Date: Sat, 10 Oct 2026 16:49:39 +0200 Subject: [PATCH] Revert "Add cable management: drawing, cores, numbering, cross-references and cable list" --- CMakeLists.txt | 1 - cmake/qet_compilation_vars.cmake | 42 - ico/icon-themes.qrc | 36 - ico/qet/traced/16/cable.svg | 6 - ico/qet/traced/22/cable.svg | 6 - ico/qet/traced/large16/cable.svg | 6 - ico/qet/traced/large22/cable.svg | 6 - ico/themes/generated/traced-24/cable.svg | 8 - .../generated/traced-large-24/cable.svg | 8 - ico/themes/qet-dark/traced-24/cable.svg | 8 - ico/themes/qet-dark/traced-large-24/cable.svg | 8 - ico/themes/qet-dark/traced/16/cable.svg | 6 - ico/themes/qet-dark/traced/22/cable.svg | 6 - ico/themes/qet-dark/traced/large16/cable.svg | 6 - ico/themes/qet-dark/traced/large22/cable.svg | 6 - sources/autoNum/assignvariables.cpp | 68 - sources/autoNum/assignvariables.h | 25 - sources/autoNum/ui/numparteditorw.cpp | 23 +- sources/autoNum/ui/numparteditorw.h | 8 - sources/autoNum/ui/pastenumberingimport.cpp | 28 +- sources/autoNum/ui/pastenumberingimport.h | 9 +- sources/autoNum/ui/selectautonumw.cpp | 122 +- sources/autoNum/ui/selectautonumw.h | 59 +- sources/bomexport.cpp | 5 +- sources/bomexport.h | 5 - sources/cable/addcablecommand.cpp | 221 -- sources/cable/addcablecommand.h | 95 - sources/cable/cable.cpp | 921 ----- sources/cable/cable.h | 376 -- sources/cable/cablecopy.cpp | 454 --- sources/cable/cablecopy.h | 126 - sources/cable/cablecreatedialog.cpp | 1407 ------- sources/cable/cablecreatedialog.h | 236 -- sources/cable/cablemanager.cpp | 635 ---- sources/cable/cablemanager.h | 113 - sources/cable/cablepart.cpp | 3377 ----------------- sources/cable/cablepart.h | 513 --- sources/cable/cablepropertiesdialog.cpp | 279 -- sources/cable/cablepropertiesdialog.h | 99 - sources/cable/cablepropertieseditorwidget.cpp | 389 -- sources/cable/cablepropertieseditorwidget.h | 115 - sources/cable/cablepropertieswidget.cpp | 409 -- sources/cable/cablepropertieswidget.h | 158 - sources/cable/cablerenumber.cpp | 292 -- sources/cable/cablerenumber.h | 102 - sources/cable/cabletextformatdialog.cpp | 278 -- sources/cable/cabletextformatdialog.h | 83 - sources/cable/editcablecommand.cpp | 494 --- sources/cable/editcablecommand.h | 285 -- sources/cablelist/cableexportdialog.cpp | 155 - sources/cablelist/cableexportdialog.h | 58 - sources/cablelist/cablelistmodel.cpp | 421 -- sources/cablelist/cablelistmodel.h | 107 - .../cablelistmodelpropertieswidget.cpp | 100 - .../cablelistmodelpropertieswidget.h | 55 - sources/cablelist/cablelistrows.cpp | 468 --- sources/cablelist/cablelistrows.h | 101 - sources/cablelist/cablequerywidget.cpp | 319 -- sources/cablelist/cablequerywidget.h | 71 - sources/cablelist/cabletypelist.cpp | 824 ---- sources/cablelist/cabletypelist.h | 134 - sources/conductorproperties.cpp | 13 - sources/conductorproperties.h | 9 - sources/diagram.cpp | 40 +- sources/diagram.h | 13 +- sources/diagramevent/diagrameventaddcable.cpp | 463 --- sources/diagramevent/diagrameventaddcable.h | 78 - sources/diagramevent/diagrameventaddpaste.cpp | 83 +- sources/diagramevent/diagrameventaddpaste.h | 21 - sources/diagramtoolbarsettings.cpp | 5 - sources/diagramview.cpp | 258 +- sources/diagramview.h | 14 - sources/elementsmover.cpp | 200 +- sources/elementsmover.h | 35 - sources/factory/propertieseditorfactory.cpp | 38 +- sources/factory/qetgraphicstablefactory.cpp | 53 +- sources/factory/qetgraphicstablefactory.h | 1 - sources/pdf_links.cpp | 58 +- sources/properties/xrefproperties.cpp | 32 +- sources/properties/xrefproperties.h | 9 - sources/qetapp.cpp | 91 - sources/qetapp.h | 18 - sources/qetdiagrameditor.cpp | 242 +- sources/qetdiagrameditor.h | 17 - .../ViewItem/qetgraphicstableitem.cpp | 51 +- sources/qetgraphicsitem/conductor.cpp | 26 - sources/qetgraphicsitem/conductor.h | 9 - sources/qeticons.cpp | 2 - sources/qeticons.h | 1 - sources/qetproject.cpp | 418 +- sources/qetproject.h | 68 - sources/scripting/qetscriptapi.cpp | 64 +- sources/scripting/qetscriptapi.h | 10 +- sources/ui/cablenumberingdialog.cpp | 383 -- sources/ui/cablenumberingdialog.h | 86 - sources/ui/configpage/configpages.cpp | 97 +- sources/ui/configpage/configpages.h | 6 - .../configpage/generalconfigurationpage.cpp | 180 - .../ui/configpage/generalconfigurationpage.h | 4 - .../ui/configpage/generalconfigurationpage.ui | 148 - sources/ui/configpage/projectconfigpages.cpp | 124 +- sources/ui/configpage/projectconfigpages.h | 6 - sources/ui/projectpropertiesdialog.cpp | 14 - sources/ui/projectpropertiesdialog.h | 2 - sources/ui/xrefpropertieswidget.cpp | 110 +- sources/ui/xrefpropertieswidget.h | 6 - sources/ui/xrefpropertieswidget.ui | 178 +- .../deleteqgraphicsitemcommand.cpp | 21 - sources/undocommand/linkelementcommand.cpp | 20 - tests/qttest/CMakeLists.txt | 26 - tests/qttest/cable_undo_probe.cpp | 105 - tests/qttest/run_cable_undo_probe.py | 60 - tests/qttest/tst_cabletypelist.cpp | 205 - 113 files changed, 209 insertions(+), 18793 deletions(-) delete mode 100644 ico/qet/traced/16/cable.svg delete mode 100644 ico/qet/traced/22/cable.svg delete mode 100644 ico/qet/traced/large16/cable.svg delete mode 100644 ico/qet/traced/large22/cable.svg delete mode 100644 ico/themes/generated/traced-24/cable.svg delete mode 100644 ico/themes/generated/traced-large-24/cable.svg delete mode 100644 ico/themes/qet-dark/traced-24/cable.svg delete mode 100644 ico/themes/qet-dark/traced-large-24/cable.svg delete mode 100644 ico/themes/qet-dark/traced/16/cable.svg delete mode 100644 ico/themes/qet-dark/traced/22/cable.svg delete mode 100644 ico/themes/qet-dark/traced/large16/cable.svg delete mode 100644 ico/themes/qet-dark/traced/large22/cable.svg delete mode 100644 sources/cable/addcablecommand.cpp delete mode 100644 sources/cable/addcablecommand.h delete mode 100644 sources/cable/cable.cpp delete mode 100644 sources/cable/cable.h delete mode 100644 sources/cable/cablecopy.cpp delete mode 100644 sources/cable/cablecopy.h delete mode 100644 sources/cable/cablecreatedialog.cpp delete mode 100644 sources/cable/cablecreatedialog.h delete mode 100644 sources/cable/cablemanager.cpp delete mode 100644 sources/cable/cablemanager.h delete mode 100644 sources/cable/cablepart.cpp delete mode 100644 sources/cable/cablepart.h delete mode 100644 sources/cable/cablepropertiesdialog.cpp delete mode 100644 sources/cable/cablepropertiesdialog.h delete mode 100644 sources/cable/cablepropertieseditorwidget.cpp delete mode 100644 sources/cable/cablepropertieseditorwidget.h delete mode 100644 sources/cable/cablepropertieswidget.cpp delete mode 100644 sources/cable/cablepropertieswidget.h delete mode 100644 sources/cable/cablerenumber.cpp delete mode 100644 sources/cable/cablerenumber.h delete mode 100644 sources/cable/cabletextformatdialog.cpp delete mode 100644 sources/cable/cabletextformatdialog.h delete mode 100644 sources/cable/editcablecommand.cpp delete mode 100644 sources/cable/editcablecommand.h delete mode 100644 sources/cablelist/cableexportdialog.cpp delete mode 100644 sources/cablelist/cableexportdialog.h delete mode 100644 sources/cablelist/cablelistmodel.cpp delete mode 100644 sources/cablelist/cablelistmodel.h delete mode 100644 sources/cablelist/cablelistmodelpropertieswidget.cpp delete mode 100644 sources/cablelist/cablelistmodelpropertieswidget.h delete mode 100644 sources/cablelist/cablelistrows.cpp delete mode 100644 sources/cablelist/cablelistrows.h delete mode 100644 sources/cablelist/cablequerywidget.cpp delete mode 100644 sources/cablelist/cablequerywidget.h delete mode 100644 sources/cablelist/cabletypelist.cpp delete mode 100644 sources/cablelist/cabletypelist.h delete mode 100644 sources/diagramevent/diagrameventaddcable.cpp delete mode 100644 sources/diagramevent/diagrameventaddcable.h delete mode 100644 sources/ui/cablenumberingdialog.cpp delete mode 100644 sources/ui/cablenumberingdialog.h delete mode 100644 tests/qttest/cable_undo_probe.cpp delete mode 100755 tests/qttest/run_cable_undo_probe.py delete mode 100644 tests/qttest/tst_cabletypelist.cpp 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.svg themes/generated/traced-large-24/bring_forward.svg qet/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.svg qet/traced/16/circle.svg qet/traced/large16/circle.svg qet/traced/22/circle.svg @@ -2714,12 +2699,6 @@ themes/qet-dark/traced/large22/bring_forward.svg themes/qet-dark/traced-large-24/bring_forward.svg themes/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.svg themes/qet-dark/traced/16/circle.svg themes/qet-dark/traced/large16/circle.svg themes/qet-dark/traced/22/circle.svg @@ -3537,21 +3516,6 @@ themes/qet-dark/traced-24/bring_forward.svg themes/qet-dark/traced-large-24/bring_forward.svg themes/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.svg themes/qet-dark/traced/16/circle.svg themes/qet-dark/traced/large16/circle.svg themes/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 -#include - -/** - @brief CablePropertiesDialog::CablePropertiesDialog - @param cable the cable being edited - @param diagram the folio the line was double-clicked on, whose undo - stack takes the edit back - @param part the very line which was double-clicked: a core put down - from this dialog lands on that line, never on the first line of a - cable which stands on two folios - @param parent -*/ -CablePropertiesDialog::CablePropertiesDialog(Cable *cable, - Diagram *diagram, - CablePart *part, - QWidget *parent) : - QDialog(parent), - m_cable(cable), - m_diagram(diagram), - m_part(part) -{ - setWindowTitle(tr("Cable")); - - auto *layout = new QVBoxLayout(this); - m_fields = new CablePropertiesWidget(this); - layout->addWidget(m_fields); - - //The button beside the type field: it opens the window which - //opens when a cable line has just been drawn, in the mode where - //it changes the type of this very cable. - connect(m_fields, &CablePropertiesWidget::typeChangeRequested, - this, &CablePropertiesDialog::changeTypeRequested); - - m_cores = new QLabel(this); - m_cores->setWordWrap(true); - layout->addWidget(m_cores); - - //The cores the type offers which are not on the sheet yet. - //Double-clicking one brings it in: the line it was asked from - //takes the mouse and the next click puts the core down there. - m_free_label = new QLabel(tr("Unplaced cores:"), this); - m_free_cores = new QListWidget(this); - m_free_cores->setSelectionMode(QAbstractItemView::SingleSelection); - m_free_cores->setAlternatingRowColors(true); - m_free_cores->setToolTip(tr("Double-click a core to place it on the " - "line: the line follows the mouse and a " - "click leaves it where you aimed.")); - m_free_cores->setContextMenuPolicy(Qt::CustomContextMenu); - connect(m_free_cores, &QWidget::customContextMenuRequested, - this, &CablePropertiesDialog::freeCoreMenu); - connect(m_free_cores, &QListWidget::itemDoubleClicked, - this, &CablePropertiesDialog::placeRequested); - connect(m_free_cores, &QListWidget::itemActivated, - this, &CablePropertiesDialog::placeRequested); - layout->addWidget(m_free_label); - layout->addWidget(m_free_cores); - - auto *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); - connect(buttons, &QDialogButtonBox::accepted, this, &CablePropertiesDialog::accept); - connect(buttons, &QDialogButtonBox::rejected, this, &CablePropertiesDialog::reject); - layout->addWidget(buttons); - - if (m_cable) { - m_fields->setProperties(m_cable->properties()); - } - updateCoreCount(); - - fillFreeCores(); - - setMinimumWidth(400); -} - -/** - @brief CablePropertiesDialog::updateCoreCount - Say how many cores this cable has now and how many of them are wired - to a conductor. It is said again after the type was changed, since - the new type may hold more cores -- or fewer. -*/ -void CablePropertiesDialog::updateCoreCount() -{ - if (!m_cores) return; - - if (!m_cable) - { - m_cores->setText(tr("No cable.")); - return; - } - - m_cores->setText(tr("%1 of %2 cores wired to a conductor", - "how many cores of this cable are wired", - m_cable->usedCoreCount()) - .arg(m_cable->usedCoreCount()) - .arg(m_cable->coreCount())); -} - -/** - @brief CablePropertiesDialog::changeTypeRequested - Open the window which opens when a cable line has just been drawn, - in the mode where it changes the type of this very cable: he picks - the new type there out of the cable type file, and the cable takes - it -- cores reworked, marks redrawn -- as one single undo step. - - Whatever he typed into the form is written first, so the change and - the typing cannot overwrite each other; and the form is filled in - again afterwards, since pressing OK right after would otherwise - write the old type back. -*/ -void CablePropertiesDialog::changeTypeRequested() -{ - if (!m_cable) return; - - apply(); - - CableCreateDialog dialog(m_diagram, m_cable, this); - if (dialog.exec() != QDialog::Accepted) return; - - m_fields->setProperties(m_cable->properties()); - updateCoreCount(); - fillFreeCores(); -} - -/** - @brief CablePropertiesDialog::fillFreeCores - List every core the type of this cable offers and which stands - nowhere on the sheet: those are the ones he can still put down. The - list is of no use when there is none, so it takes no room then. -*/ -void CablePropertiesDialog::fillFreeCores() -{ - if (!m_free_cores) return; - - m_free_cores->clear(); - for (int i = 0; m_cable && i < m_cable->coreCount(); ++i) - { - if (m_cable->hasCore(i)) continue; - - const QString color = m_cable->colorOfCore(i); - auto *item = new QListWidgetItem( - color.isEmpty() ? tr("Core %1").arg(i + 1) - : tr("Core %1 — %2").arg(i + 1).arg(color), - m_free_cores); - item->setData(Qt::UserRole, i); - } - - const bool any = m_part && m_free_cores->count() > 0; - m_free_label->setVisible(any); - m_free_cores->setVisible(any); - if (!any) return; - - //As high as the entries it really shows, six of them at most: - //a handful of free cores must not turn the dialog into a wall. - const int row = qMax(m_free_cores->sizeHintForRow(0), 18); - m_free_cores->setFixedHeight(row * qMin(6, m_free_cores->count()) - + 2 * m_free_cores->frameWidth() + 4); -} - -/** - @brief CablePropertiesDialog::placeRequested - He picked one of the cores which stand nowhere: whatever is typed in - the form is written first, the dialog closes, and the line he asked - it from takes the mouse -- so the core goes onto that very line, and - the next click leaves it standing there. - @param item the entry he double-clicked -*/ -void CablePropertiesDialog::placeRequested(QListWidgetItem *item) -{ - //A double click reports itself twice on some platforms (once as - //activated, once as double-clicked): the first one takes the - //mouse away, the second must not ask for it again. - if (!item || !m_part || m_placing) return; - - m_placing = true; - const int core = item->data(Qt::UserRole).toInt(); - QPointer part = m_part; - - apply(); - QDialog::accept(); - - part->startPlacingCore(core); -} - -/** - @brief CablePropertiesDialog::freeCoreMenu - The small menu a right click on a core which stands nowhere opens: - one entry, doing what a double click does -- this dialog writes - whatever he typed, closes itself, the line he asked it from takes - the mouse, the mark shows on that line at once and the mouse is put - down there, so that the very next click leaves the core standing on - that line. - - @param pos where he right-clicked, in list coordinates -*/ -void CablePropertiesDialog::freeCoreMenu(const QPoint &pos) -{ - QListWidgetItem *item = m_free_cores ? m_free_cores->itemAt(pos) : nullptr; - if (!item || !m_part || m_placing) return; - - m_free_cores->setCurrentItem(item); - - QMenu menu(m_free_cores); - QAction *place = menu.addAction(tr("Place this core on the line")); - place->setIcon(QET::Icons::Cable); - - if (menu.exec(m_free_cores->viewport()->mapToGlobal(pos)) == place) { - //Gone before he is sent off: this dialog is closing, the - //mouse goes to the line and nothing may be left standing - //over the place it is sent to. - menu.hide(); - placeRequested(item); - } -} - -/** - @brief CablePropertiesDialog::accept - Whatever was typed here is written into the cable as one undo step, - and the cable works out the cable field of every conductor it feeds - again on its own: rename the cable and every drawing which shows it - follows. -*/ -void CablePropertiesDialog::accept() -{ - apply(); - QDialog::accept(); -} - -/** - @brief CablePropertiesDialog::apply - Everything the form holds goes into the cable as one single undo - step -- and only when something really changed, so that closing the - dialog by any means leaves no step of undo to walk back through. -*/ -void CablePropertiesDialog::apply() -{ - if (!m_cable) return; - - const CableProperties before = m_cable->properties(); - const CableProperties after = m_fields->properties(); - if (after != before) - { - if (m_diagram) { - m_diagram->undoStack().push( - new ChangeCablePropertiesCommand(m_cable, before, after)); - } else { - m_cable->setProperties(after); - } - } -} diff --git a/sources/cable/cablepropertiesdialog.h b/sources/cable/cablepropertiesdialog.h deleted file mode 100644 index b0eddcf1e..000000000 --- a/sources/cable/cablepropertiesdialog.h +++ /dev/null @@ -1,99 +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 CABLEPROPERTIESDIALOG_H -#define CABLEPROPERTIESDIALOG_H - -#include "cable.h" - -#include -#include - -class CablePropertiesWidget; -class CablePart; -class Diagram; -class QLabel; -class QListWidget; -class QListWidgetItem; - -/** - @brief The dialog which edits a cable once it is drawn. - - Everything it holds belongs to the cable itself rather than to the - line which was drawn: whatever is typed here applies to every section - of that cable, on every folio, and the conductors it feeds get their - cable field worked out again as soon as it says it changed. - - Under the count of cores it lists the cores of the type which stand - nowhere on the sheet yet: double-clicking one sends it to the very - line this dialog was asked from, which takes the mouse until he - clicks and leaves the core standing there. -*/ -class CablePropertiesDialog : public QDialog -{ - Q_OBJECT - - public: - /** - @param cable the cable being edited - @param diagram the folio whose undo stack takes the edit - @param part the very line which was double-clicked, where a - core chosen in this dialog is to be put down - @param parent - */ - explicit CablePropertiesDialog(Cable *cable, - Diagram *diagram = nullptr, - CablePart *part = nullptr, - QWidget *parent = nullptr); - - void accept() override; - - private: - ///Everything the form holds goes into the cable, as one step - void apply(); - ///What the count of cores under the form says right now - void updateCoreCount(); - ///He asked for the window choosing another type for this cable - void changeTypeRequested(); - ///Lists the cores of the type which stand nowhere yet - void fillFreeCores(); - ///He double-clicked one of them: put it down on the line - void placeRequested(QListWidgetItem *item); - /** - The small menu a right click on a core which stands - nowhere opens: one entry doing what a double click does - -- the dialog closes, the line takes the mouse, the mark - shows on it at once and the mouse is put down there. - @param pos where he right-clicked, in list coordinates - */ - void freeCoreMenu(const QPoint &pos); - - private: - QPointer m_cable; - Diagram *m_diagram = nullptr; - ///The line whose dialog this is, and the one a core chosen - ///here is put down on - QPointer m_part; - CablePropertiesWidget *m_fields = nullptr; - QLabel *m_cores = nullptr; - QLabel *m_free_label = nullptr; - QListWidget *m_free_cores = nullptr; - ///True from the moment he picks a core to put down - bool m_placing = false; -}; - -#endif // CABLEPROPERTIESDIALOG_H diff --git a/sources/cable/cablepropertieseditorwidget.cpp b/sources/cable/cablepropertieseditorwidget.cpp deleted file mode 100644 index a5516aaeb..000000000 --- a/sources/cable/cablepropertieseditorwidget.cpp +++ /dev/null @@ -1,389 +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 "cablepropertieseditorwidget.h" - -#include "cablecreatedialog.h" -#include "cablepart.h" -#include "cablepropertieswidget.h" -#include "editcablecommand.h" -#include "../qeticons.h" -#include "../qetproject.h" - -#include -#include -#include -#include -#include -#include -#include - -/** - @brief CablePropertiesEditorWidget::CablePropertiesEditorWidget - @param cable the cable being edited - @param part the line of that cable this panel was opened by, which - is the line a core picked here is put down on - @param parent parent widget -*/ -CablePropertiesEditorWidget::CablePropertiesEditorWidget(Cable *cable, - CablePart *part, - QWidget *parent) : - PropertiesEditorWidget(parent), - m_fields(new CablePropertiesWidget(this)), - m_info(new QLabel(this)), - m_free_label(new QLabel(this)), - m_free_cores(new QListWidget(this)) -{ - m_info->setWordWrap(true); - - //The cores which stand nowhere yet, listed under the count of - //wired ones: double-clicking one puts it down on this very line - //without any dialog having to be opened for it. The list takes - //no keyboard focus, so clicking it never takes the drawing away - //from the user -- startPlacingCore() hands the focus to the - //sheet itself, which is where Escape has to be heard. - m_free_label->setText(tr("Unplaced cores:")); - m_free_label->setVisible(false); - m_free_cores->setSelectionMode(QAbstractItemView::SingleSelection); - m_free_cores->setAlternatingRowColors(true); - m_free_cores->setFocusPolicy(Qt::NoFocus); - m_free_cores->setToolTip(tr("Double-click a core to place it on the " - "line: the line follows the mouse and a " - "click leaves it where you aimed.")); - m_free_cores->setVisible(false); - m_free_cores->setContextMenuPolicy(Qt::CustomContextMenu); - connect(m_free_cores, &QWidget::customContextMenuRequested, - this, &CablePropertiesEditorWidget::freeCoreMenu); - connect(m_free_cores, &QListWidget::itemDoubleClicked, - this, &CablePropertiesEditorWidget::placeRequested); - connect(m_free_cores, &QListWidget::itemActivated, - this, &CablePropertiesEditorWidget::placeRequested); - - auto *layout = new QVBoxLayout(this); - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(4); - layout->addWidget(m_fields); - - //The button beside the type field: it opens the window which - //opens when a cable line has just been drawn, in the mode where - //it changes the type of this very cable. - connect(m_fields, &CablePropertiesWidget::typeChangeRequested, - this, &CablePropertiesEditorWidget::changeTypeRequested); - - layout->addWidget(m_info); - layout->addWidget(m_free_label); - layout->addWidget(m_free_cores); - layout->addStretch(1); - - //Wide enough for the labels, the ticks, the fields, the buttons - //setting how a text is written and the two distances nudging - //the label and the row of colours -- they sit side by side. - setMinimumWidth(250); - setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); - - setDisabled(true); - setCable(cable, part); -} - -/** - @brief CablePropertiesEditorWidget::setCable - Set (or change) the cable whose properties are edited. - @param cable - @param part the line of that cable this panel was opened by: the one - a core picked in the list is put down on, and null while no single - line is selected -- then nothing can be put down and the list stays - out of the way -*/ -void CablePropertiesEditorWidget::setCable(Cable *cable, CablePart *part) -{ - if (!cable) return; - m_part = part; - if (m_cable && m_cable != cable) - disconnect(m_cable, nullptr, this, nullptr); - - m_cable = cable; - //Keep the panel in sync when the cable is edited elsewhere (the - //dialog reached by double-clicking its line, for one): otherwise - //the panel would write back what it held before that edit. - connect(m_cable, &Cable::changed, - this, &CablePropertiesEditorWidget::updateUi, Qt::UniqueConnection); - setEnabled(true); - updateUi(); -} - -/** - @brief CablePropertiesEditorWidget::setLiveEdit - In live-edit mode (how the panel uses every editor) an edit is - applied as soon as a field is finished, rather than waiting for a - call to apply(). On top of that the text itself is written onto the - cable with every character, so what he types shows up on the drawing - while he is still typing it -- the whole editing of one field stays - one single undo step, which is worked out when the field is left. - @param live_edit true to enable live edit - @return always true -*/ -bool CablePropertiesEditorWidget::setLiveEdit(bool live_edit) -{ - if (m_live_edit == live_edit) return true; - m_live_edit = live_edit; - - if (m_live_edit) { - m_live_connection = connect(m_fields, &CablePropertiesWidget::propertiesEdited, - this, &CablePropertiesEditorWidget::apply); - connect(m_fields, &CablePropertiesWidget::propertiesTyped, - this, &CablePropertiesEditorWidget::applyLive); - } else { - disconnect(m_live_connection); - disconnect(m_fields, &CablePropertiesWidget::propertiesTyped, - this, &CablePropertiesEditorWidget::applyLive); - } - return true; -} - -/** - @brief CablePropertiesEditorWidget::applyLive - Put what is being typed straight onto the cable, without pushing an - undo step: the whole editing of one field is one step, which - apply() works out from what the panel showed when the field is left. -*/ -void CablePropertiesEditorWidget::applyLive() -{ - //Ignore the signals sent while the form is being filled in: at - //that moment it holds a half loaded state. - if (m_updating) return; - if (!m_cable) return; - - m_live_cable = m_cable; - m_cable->setProperties(m_fields->properties()); -} - -/** - @brief CablePropertiesEditorWidget::changeTypeRequested - Open the window which opens when a cable line has just been drawn, - in the mode where it changes the type of this very cable: he picks - the new type there out of the cable type file, and the cable takes - it -- cores reworked, marks redrawn -- as one single undo step. - - The form is filled in again afterwards rather than through the usual - path: the type has just changed under it, and it may not go on - offering the one which was left. -*/ -void CablePropertiesEditorWidget::changeTypeRequested() -{ - if (!m_cable) return; - - CableCreateDialog dialog(m_part ? m_part->diagram() : nullptr, - m_cable, - this); - if (dialog.exec() != QDialog::Accepted) return; - - m_updating = true; - m_fields->setProperties(m_cable->properties()); - m_shown = m_cable->properties(); - m_updating = false; - updateInfo(); -} - -/** - @brief CablePropertiesEditorWidget::apply - Put what was typed onto the cable as one undo step. -*/ -void CablePropertiesEditorWidget::apply() -{ - //Ignore the signals sent while the form is being filled in: at - //that moment it holds a half loaded state. - if (m_updating) return; - if (!m_cable) return; - - if (QUndoCommand *undo = associatedUndo()) - { - auto *project = qobject_cast(m_cable->parent()); - if (project && project->undoStack()) { - project->undoStack()->push(undo); - } else { - m_cable->setProperties(m_fields->properties()); - delete undo; - } - } - m_shown = m_fields->properties(); -} - -/** - @brief CablePropertiesEditorWidget::associatedUndo - @return the edit as one undo step, or nullptr when nothing changed -*/ -QUndoCommand *CablePropertiesEditorWidget::associatedUndo() const -{ - if (!m_cable) return nullptr; - - const CableProperties after = m_fields->properties(); - if (after == m_shown) return nullptr; - return new ChangeCablePropertiesCommand(m_cable, m_shown, after); -} - -/** - @brief CablePropertiesEditorWidget::reset - Discard the edit in progress, putting back what the panel showed. -*/ -void CablePropertiesEditorWidget::reset() -{ - if (!m_cable) return; - m_updating = true; - m_fields->setProperties(m_shown); - m_updating = false; -} - -/** - @brief CablePropertiesEditorWidget::updateInfo - Say how many cores of this cable are wired to a conductor. -*/ -void CablePropertiesEditorWidget::updateInfo() -{ - if (!m_cable) return; - - m_info->setText(tr("%1 of %2 cores wired to a conductor", - "how many cores of this cable are wired", - m_cable->usedCoreCount()) - .arg(m_cable->usedCoreCount()) - .arg(m_cable->coreCount())); - - updateFreeCores(); -} - -/** - @brief CablePropertiesEditorWidget::updateFreeCores - List the cores the type of this cable offers which stand nowhere on - the sheet yet: those are the ones he can still put down, and they - come down straight from here, on the line this panel was opened by. - - The list is of no use when there is none -- and it cannot work at - all when no single line of the cable is the selected one, since a - core then has no line to be put down on -- so it takes no room then. -*/ -void CablePropertiesEditorWidget::updateFreeCores() -{ - if (!m_free_cores) return; - - const bool usable = m_cable && m_part && m_part->cable() == m_cable; - - m_free_cores->clear(); - if (usable) - { - for (int i = 0; i < m_cable->coreCount(); ++i) - { - if (m_cable->hasCore(i)) continue; - - const QString color = m_cable->colorOfCore(i); - auto *item = new QListWidgetItem( - color.isEmpty() ? tr("Core %1").arg(i + 1) - : tr("Core %1 — %2").arg(i + 1).arg(color), - m_free_cores); - item->setData(Qt::UserRole, i); - } - } - - const bool any = usable && m_free_cores->count() > 0; - m_free_label->setVisible(any); - m_free_cores->setVisible(any); - if (!any) return; - - //As high as the entries it really shows, four of them at most: - //the dock is small, and a handful of free cores must not turn - //it into a wall. - const int row = qMax(m_free_cores->sizeHintForRow(0), 18); - m_free_cores->setFixedHeight(row * qMin(4, m_free_cores->count()) - + 2 * m_free_cores->frameWidth() + 4); -} - -/** - @brief CablePropertiesEditorWidget::placeRequested - He double-clicked one of the cores which stand nowhere: this very - line takes the mouse and waits for the click which puts the core - down. There is no dialog to close here -- the panel stays where it - is, and it reads the cable back as soon as the core lands. - - @param item the entry he double-clicked -*/ -void CablePropertiesEditorWidget::placeRequested(QListWidgetItem *item) -{ - if (!item || !m_part) return; - - m_part->startPlacingCore(item->data(Qt::UserRole).toInt()); -} - -/** - @brief CablePropertiesEditorWidget::freeCoreMenu - The small menu a right click on a core which stands nowhere opens: - one entry, doing what a double click does -- the line takes the - mouse, the mark shows on it at once and the mouse is put down there, - so the very next click leaves the core standing on that line. - - @param pos where he right-clicked, in list coordinates -*/ -void CablePropertiesEditorWidget::freeCoreMenu(const QPoint &pos) -{ - QListWidgetItem *item = m_free_cores ? m_free_cores->itemAt(pos) : nullptr; - if (!item || !m_part) return; - - m_free_cores->setCurrentItem(item); - - QMenu menu(m_free_cores); - QAction *place = menu.addAction(tr("Place this core on the line")); - place->setIcon(QET::Icons::Cable); - - if (menu.exec(m_free_cores->viewport()->mapToGlobal(pos)) == place) { - //Gone before he is sent off: the mouse goes to the line - //and nothing may be left standing over the place it is - //sent to. - menu.hide(); - placeRequested(item); - } -} - -/** - @brief CablePropertiesEditorWidget::updateUi - Read the cable back into the panel. - - Not while a field is being typed in: writing what was just typed - back into that field would take the cursor away from the user with - every key, and it would take back the state the whole editing of - that field is measured against when the undo step is worked out. -*/ -void CablePropertiesEditorWidget::updateUi() -{ - if (!m_cable) return; - - if (m_fields->editing() && m_live_cable == m_cable) { - updateInfo(); - return; - } - - m_updating = true; - m_fields->setProperties(m_cable->properties()); - m_shown = m_cable->properties(); - updateInfo(); - m_updating = false; -} - -/** - @brief CablePropertiesEditorWidget::title - @return the panel title -*/ -QString CablePropertiesEditorWidget::title() const -{ - return tr("Cable"); -} diff --git a/sources/cable/cablepropertieseditorwidget.h b/sources/cable/cablepropertieseditorwidget.h deleted file mode 100644 index 52268a0ce..000000000 --- a/sources/cable/cablepropertieseditorwidget.h +++ /dev/null @@ -1,115 +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 CABLEPROPERTIESEDITORWIDGET_H -#define CABLEPROPERTIESEDITORWIDGET_H - -#include "cable.h" - -#include "../PropertiesEditor/propertieseditorwidget.h" - -#include -#include -#include - -class CablePropertiesWidget; -class CablePart; -class QLabel; -class QListWidget; -class QListWidgetItem; - -/** - @brief The cable shown in the "Propriétés de la sélection" panel. - - Selecting a line of a cable used to bring up nothing at all there: - the panel only knew the item types it already had an editor for. - Now it holds the very fields the cable is filled in with -- each of - them with its own tick saying whether the drawing shows it -- and - what stands there is written back live, one undo step per edit. -*/ -class CablePropertiesEditorWidget : public PropertiesEditorWidget -{ - Q_OBJECT - - public: - explicit CablePropertiesEditorWidget(Cable *cable = nullptr, - CablePart *part = nullptr, - QWidget *parent = nullptr); - - void setCable(Cable *cable, CablePart *part = nullptr); - - void apply() override; - void reset() override; - void updateUi() override; - QUndoCommand *associatedUndo() const override; - QString title() const override; - bool setLiveEdit(bool live_edit) override; - - private: - ///Write what is being typed onto the cable right away - void applyLive(); - ///He asked for the window choosing another type for this cable - void changeTypeRequested(); - ///Say how many cores of this cable are wired - void updateInfo(); - /** - List the cores which stand nowhere on the sheet yet, under - the count of wired ones: those are the ones he can still - put down, and double-clicking one puts it down straight - from here, on the line this panel was opened for. - */ - void updateFreeCores(); - ///He double-clicked one of those cores: give it this line - void placeRequested(QListWidgetItem *item); - /** - He right-clicked one of them: a small menu with the one - thing which can be done to a core which stands nowhere - -- putting it down -- for whoever does not know that a - double click does the same. - @param pos where he clicked, in list coordinates - */ - void freeCoreMenu(const QPoint &pos); - - private: - CablePropertiesWidget *m_fields = nullptr; - QLabel *m_info = nullptr; - ///The cores which stand nowhere yet, listed under the count - QLabel *m_free_label = nullptr; - QListWidget *m_free_cores = nullptr; - QPointer m_cable; - /** - The line this panel was opened by, which is the one a core - picked here is put down on: taking a core always belongs to - the line whose dialog was asked for, never to the first - line of the cable. Null while no single line is selected, - and then there is nothing to put a core down on and the - list takes no room. - */ - QPointer m_part; - ///What the panel showed before the edit: the fields which - ///differ from it are the ones the user changed - CableProperties m_shown; - ///The cable the text is being typed straight into, so that - ///reading the cable back only skips the very field which is - ///under the fingers of the user -- and never for another - ///cable which has just become the selected one - QPointer m_live_cable; - QMetaObject::Connection m_live_connection; - bool m_updating = false; -}; - -#endif // CABLEPROPERTIESEDITORWIDGET_H diff --git a/sources/cable/cablepropertieswidget.cpp b/sources/cable/cablepropertieswidget.cpp deleted file mode 100644 index 5728c777f..000000000 --- a/sources/cable/cablepropertieswidget.cpp +++ /dev/null @@ -1,409 +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 "cablepropertieswidget.h" - -#include "cabletextformatdialog.h" -#include "../qetapp.h" -#include "../qeticons.h" -#include "../utils/qetutils.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** - @brief CablePropertiesWidget::CablePropertiesWidget - One row per field: its name, the tick saying whether the drawing - shows it, the field itself, then the button setting how that one - text is written -- one last row for the colours of the cores, which - are written all together along the line, and one row per distance - he may nudge the whole label and that row of colours with. - @param parent -*/ -CablePropertiesWidget::CablePropertiesWidget(QWidget *parent) : - QWidget(parent) -{ - auto *grid = new QGridLayout(this); - grid->setContentsMargins(0, 0, 0, 0); - grid->setHorizontalSpacing(6); - grid->setVerticalSpacing(3); - - auto *plan = new QLabel(tr("Drawing", - "column header: the drawing shows this field"), this); - plan->setAlignment(Qt::AlignCenter); - plan->setToolTip(tr("Tick to write this field on the drawing.", - "tooltip of the tick before each field")); - grid->addWidget(plan, 0, 1, Qt::AlignCenter); - - int row = 1; - addField(grid, row++, QStringLiteral("designation"), - tr("Designation"), &m_designation, &m_show_designation); - const int type_row = row; - addField(grid, row++, QStringLiteral("type"), - tr("Type"), &m_type, &m_show_type); - - //A button beside the type field, opening the very window which - //opens when a cable line has just been drawn: the type is - //picked from the cable type file there -- with its search, its - //filter and its own button for a type the file does not hold - //yet -- rather than typed in as plain text. - auto *pick_type = new QPushButton(this); - pick_type->setFocusPolicy(Qt::NoFocus); - pick_type->setIcon(QET::Icons::Cable); - pick_type->setToolTip(tr("Choose the type of this cable from the list of " - "cable types…")); - connect(pick_type, &QPushButton::clicked, - this, &CablePropertiesWidget::typeChangeRequested); - grid->addWidget(pick_type, type_row, 4, Qt::AlignRight | Qt::AlignVCenter); - - addField(grid, row++, QStringLiteral("installation"), - tr("Plant (=)"), &m_installation, &m_show_installation); - addField(grid, row++, QStringLiteral("location"), - tr("Location (+)"), &m_location, &m_show_location); - addField(grid, row++, QStringLiteral("length"), - tr("Length"), &m_length, &m_show_length); - - //The colours of the cores: one setting for every one of them at - //once, since they are written in one row along the line and are - //read as one row. - auto *cores_label = new QLabel(tr("Core colors"), this); - cores_label->setAlignment(Qt::AlignRight | Qt::AlignVCenter); - cores_label->setToolTip(tr("The font of the colors of all the cores " - "of this cable, written along the line.")); - grid->addWidget(cores_label, row, 0, Qt::AlignRight | Qt::AlignVCenter); - addFormatButton(grid, row, QStringLiteral("cores")); - - //Where that whole label stands: one distance along the line and - //one across it, in pixels, moving the label as one thing -- - //installation, place and designation over the line, type and - //length under it -- and the line not at all. - addOffsetRow(grid, row + 1, tr("Text position"), - tr("Shifts all the cable text: plant, location " - "and designation on one side, type and length " - "on the other. The line, the slashes and the " - "wiring stay in place. 10 px make one grid cell."), - &m_text_offset_x, &m_text_offset_y); - - //And where the row of colours stands from the slashes it names. - addOffsetRow(grid, row + 2, tr("Core colors position"), - tr("Shifts the core colors relative to their slashes. " - "The slashes, the entries and the wiring stay in place. " - "10 px make one grid cell."), - &m_core_offset_x, &m_core_offset_y); - - grid->setColumnStretch(2, 1); -} - -/** - @brief CablePropertiesWidget::addField - Build one row of the form. - @param grid the form being built - @param row where to put it - @param key the name this text goes by in the file - @param name what the field holds - @param edit receives the line edit - @param tick receives the tick before it -*/ -void CablePropertiesWidget::addField(QGridLayout *grid, - int row, - const QString &key, - const QString &name, - QLineEdit **edit, - QCheckBox **tick) -{ - auto *label = new QLabel(name, this); - auto *check = new QCheckBox(this); - check->setToolTip(tr("Tick to write this field on the drawing.", - "tooltip of the tick before each field")); - auto *field = new QLineEdit(this); - field->setClearButtonEnabled(true); - - connect(field, &QLineEdit::editingFinished, - this, &CablePropertiesWidget::propertiesEdited); - //Every character too: what is typed is written onto the cable - //as it stands, so the drawing follows while he is still typing - //rather than only once he leaves the field. - connect(field, &QLineEdit::textChanged, - this, &CablePropertiesWidget::propertiesTyped); - connect(check, &QCheckBox::toggled, - this, &CablePropertiesWidget::propertiesEdited); - - grid->addWidget(label, row, 0, Qt::AlignRight | Qt::AlignVCenter); - grid->addWidget(check, row, 1, Qt::AlignCenter); - grid->addWidget(field, row, 2); - addFormatButton(grid, row, key); - - *edit = field; - *tick = check; -} - -/** - @brief CablePropertiesWidget::addFormatButton - The small button standing at the right of a text: it says which - size that text is written with, and opens the window where that - size -- and the rest of how the text is written -- is set. - @param grid the form being built - @param row where to put it - @param key the name of the text - @return the button, so the caller may reach it again -*/ -QPushButton *CablePropertiesWidget::addFormatButton(QGridLayout *grid, - int row, - const QString &key) -{ - auto *button = new QPushButton(this); - button->setFocusPolicy(Qt::NoFocus); - connect(button, &QPushButton::clicked, this, - [this, key]() {editFormat(key);}); - - grid->addWidget(button, row, 3, Qt::AlignRight | Qt::AlignVCenter); - m_format_buttons.insert(key, button); - refreshFormatButton(key); - return button; -} - -/** - @brief CablePropertiesWidget::addOffsetRow - Build one of the two rows nudging what is drawn: a name at the left - of the form, then two distances in pixels -- one measured along the - line, one across it -- the way the cross references of a slave offer - theirs. - - Both boxes speak of the drawing and nothing else: what he types here - moves texts, and the line, the slashes and the wires behind them keep - standing exactly where they were. - @param grid the form being built - @param row where to put it - @param name what that distance moves - @param tip what it does and does not touch - @param dx receives the spin box for the distance along the line - @param dy receives the spin box for the distance across it -*/ -void CablePropertiesWidget::addOffsetRow(QGridLayout *grid, - int row, - const QString &name, - const QString &tip, - QSpinBox **dx, - QSpinBox **dy) -{ - auto *label = new QLabel(name, this); - label->setAlignment(Qt::AlignRight | Qt::AlignVCenter); - label->setToolTip(tip); - - const auto spin = [this, tip]() { - auto *box = new QSpinBox(this); - box->setRange(-200, 200); - box->setSingleStep(1); - box->setSuffix(QStringLiteral(" px")); - box->setAlignment(Qt::AlignRight); - box->setToolTip(tip); - connect(box, &QSpinBox::valueChanged, - this, &CablePropertiesWidget::propertiesEdited); - return box; - }; - QSpinBox *x_box = spin(); - QSpinBox *y_box = spin(); - - auto *along = new QLabel(tr("X :"), this); - along->setAlignment(Qt::AlignRight | Qt::AlignVCenter); - auto *across = new QLabel(tr("Y :"), this); - across->setAlignment(Qt::AlignRight | Qt::AlignVCenter); - - auto *values = new QHBoxLayout; - values->setContentsMargins(0, 0, 0, 0); - values->setSpacing(4); - values->addWidget(along); - values->addWidget(x_box); - values->addWidget(across); - values->addWidget(y_box); - values->addStretch(1); - - grid->addWidget(label, row, 0, Qt::AlignRight | Qt::AlignVCenter); - grid->addLayout(values, row, 2); - - *dx = x_box; - *dy = y_box; -} - -/** - @brief CablePropertiesWidget::editFormat - Open the window where one text of the cable is set, and -- when he - came out of it having really changed something -- write the answer - back into the form. One window is one change, so one step of undo. - @param key the name of the text -*/ -void CablePropertiesWidget::editFormat(const QString &key) -{ - CableTextFormatDialog dialog(m_formats.value(key), coresKey(key), this); - if (dialog.exec() != QDialog::Accepted) return; - - const CableTextFormat answer = dialog.format(); - if (m_formats.value(key) == answer) return; - - if (answer == CableTextFormat()) m_formats.remove(key); - else m_formats.insert(key, answer); - - refreshFormatButton(key); - emit propertiesEdited(); -} - -/** - @brief CablePropertiesWidget::refreshFormatButton - Let the button of one text say which size that text is written with, - so that the size is read off the form rather than only in the - drawing. - @param key the name of the text -*/ -void CablePropertiesWidget::refreshFormatButton(const QString &key) -{ - QPushButton *button = m_format_buttons.value(key); - if (!button) return; - - const QFont font = effectiveFont(key); - button->setText(tr("%1 pt").arg(QString::number(font.pointSizeF(), 'g', 3))); - - const QString tip = coresKey(key) - ? tr("Font, size and style of the core colors.") - : tr("Font, size, style and alignment of this text."); - const CableTextFormat format = m_formats.value(key); - const bool its_own = !format.font.isEmpty() || format.alignment != 0; - button->setToolTip(tip + QLatin1Char(' ') + (its_own - ? tr("Set for this cable.") - : tr("Value defined in the preferences."))); -} - -/** - @brief CablePropertiesWidget::effectiveFont - @param key the name of a text of the form - @return the font that text really is written with on the drawing: - the one this cable asks for, or -- when it asks for none -- the one - the preferences offer -*/ -QFont CablePropertiesWidget::effectiveFont(const QString &key) const -{ - const CableTextFormat format = m_formats.value(key); - if (!format.font.isEmpty()) - { - QFont font; - if (QETUtils::fontFromString(font, format.font)) { - return font; - } - } - return coresKey(key) ? QETApp::cableCoreFont() : QETApp::cableTextsFont(); -} - -/** - @brief CablePropertiesWidget::properties - @return everything the form holds right now -*/ -CableProperties CablePropertiesWidget::properties() const -{ - CableProperties properties; - properties.designation = m_designation->text().trimmed(); - //A name which is not the one read from the cable can only have - //come from him typing it in: from that moment on the cable - //carries a name of his own rather than one the numbering rule - //handed out. Putting the very same name back by hand keeps the - //cable as it stood, which is what the field says. - properties.designation_by_hand = properties.designation == m_designation_text - ? m_designation_by_hand : true; - properties.type = m_type->text().trimmed(); - properties.installation = m_installation->text().trimmed(); - properties.location = m_location->text().trimmed(); - properties.length = m_length->text().trimmed(); - properties.show_designation = m_show_designation->isChecked(); - properties.show_type = m_show_type->isChecked(); - properties.show_installation = m_show_installation->isChecked(); - properties.show_location = m_show_location->isChecked(); - properties.show_length = m_show_length->isChecked(); - properties.texts = m_formats; - properties.text_offset = QPointF(m_text_offset_x->value(), - m_text_offset_y->value()); - properties.core_offset = QPointF(m_core_offset_x->value(), - m_core_offset_y->value()); - return properties; -} - -/** - @brief CablePropertiesWidget::setProperties - Fill the form in. The ticks say what is drawn, so a field which is - switched off still keeps the text standing in it -- hiding it on the - drawing never throws away what the user typed. - @param properties -*/ -void CablePropertiesWidget::setProperties(const CableProperties &properties) -{ - m_designation->setText(properties.designation); - //What the field holds now is what the cable said: anything he - //types from here on is a name of his own - m_designation_text = properties.designation; - m_designation_by_hand = properties.designation_by_hand; - m_type->setText(properties.type); - m_installation->setText(properties.installation); - m_location->setText(properties.location); - m_length->setText(properties.length); - m_show_designation->setChecked(properties.show_designation); - m_show_type->setChecked(properties.show_type); - m_show_installation->setChecked(properties.show_installation); - m_show_location->setChecked(properties.show_location); - m_show_length->setChecked(properties.show_length); - - //Filling the form in is not the user nudging anything: those - //two rows are muted while they are set, so that loading a cable - //can never look like an edit which then has to be undone. - const QSignalBlocker block_text_x(m_text_offset_x); - const QSignalBlocker block_text_y(m_text_offset_y); - const QSignalBlocker block_core_x(m_core_offset_x); - const QSignalBlocker block_core_y(m_core_offset_y); - m_text_offset_x->setValue(qRound(properties.text_offset.x())); - m_text_offset_y->setValue(qRound(properties.text_offset.y())); - m_core_offset_x->setValue(qRound(properties.core_offset.x())); - m_core_offset_y->setValue(qRound(properties.core_offset.y())); - - //The texts this cable writes its own way. Only what really has - //an answer of its own is kept, so that a stray empty entry can - //never make the form claim a change it never made. - m_formats.clear(); - for (auto it = properties.texts.cbegin(); it != properties.texts.cend(); ++it) - { - if (it.value() == CableTextFormat()) continue; - m_formats.insert(it.key(), it.value()); - } - for (auto it = m_format_buttons.cbegin(); it != m_format_buttons.cend(); ++it) { - refreshFormatButton(it.key()); - } -} - -/** - @brief CablePropertiesWidget::editing - @return true when the keyboard stands in one of the fields of this - form. The panel uses it to keep its hands off what is being typed: - writing the cable back into the form while he is typing would take - the cursor away from him with every key. -*/ -bool CablePropertiesWidget::editing() const -{ - QWidget *focus = QApplication::focusWidget(); - return focus && (focus == this || isAncestorOf(focus)); -} diff --git a/sources/cable/cablepropertieswidget.h b/sources/cable/cablepropertieswidget.h deleted file mode 100644 index ea4b83fef..000000000 --- a/sources/cable/cablepropertieswidget.h +++ /dev/null @@ -1,158 +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 CABLEPROPERTIESWIDGET_H -#define CABLEPROPERTIESWIDGET_H - -#include "cable.h" - -#include - -class QCheckBox; -class QGridLayout; -class QLineEdit; -class QPushButton; -class QSpinBox; - -/** - @brief The fields a cable is filled in with, and the tick before each - of them saying whether what stands there is written on the drawing. - - It holds no cable of its own: it is a plain form, filled in with - setProperties() and read back with properties(). That way the dialog - editing one cable and the selection panel of QElectroTech offer - exactly the same fields in the same order, and neither of them can - drift away from the other. - - Every field is followed by a checkbox: what the checkbox says is - written next to the trunk line of the drawing, what it does not say - stays in the cable alone and only shows up in lists. - - Behind every field stands a button opening the window where that one - text is written: its font, its size, its style and -- for the texts - drawn next to the line -- the way it lines up there. The colours of - the cores get a button of their own at the foot of the form, all of - them at once, because they are written in one row along the line and - are never set apart. - - The type field carries one more button, on the right of that row. It - does not say how the word is written: it asks for the window which - opens when a cable line has just been drawn, where a type is picked - out of the cable type file -- with its search, its filter and its own - button for a type the file does not hold yet -- and taken onto the - cable. Whoever holds this form answers typeChangeRequested(). - - At the foot of the form stand the two distances he may nudge: one for - the whole label, one for the row of colours. Both are on the drawing - only -- the line, the slashes and the wiring stay where they are. -*/ -class CablePropertiesWidget : public QWidget -{ - Q_OBJECT - - public: - explicit CablePropertiesWidget(QWidget *parent = nullptr); - - ///What the form holds right now - CableProperties properties() const; - ///Fill the form in - void setProperties(const CableProperties &properties); - ///True while one of the fields is being typed in - bool editing() const; - - signals: - ///Emitted when a field or a tick was really changed by hand - void propertiesEdited(); - ///Emitted with every character typed, so the drawing can - ///follow the text as it is written rather than only once the - ///field is left - void propertiesTyped(); - ///Emitted when he asks for the window choosing the type of - ///this cable out of the cable type file - void typeChangeRequested(); - - private: - void addField(QGridLayout *grid, - int row, - const QString &key, - const QString &name, - QLineEdit **edit, - QCheckBox **tick); - ///The button opening the window which sets one text - QPushButton *addFormatButton(QGridLayout *grid, int row, const QString &key); - /** - Build one row of the two spin boxes nudging what is - drawn: one for the label of the cable, one for the row - of its colours. - @param grid the form being built - @param row where to put it - @param name what the row moves - @param tip what that distance does and does not touch - @param dx receives the spin box for the distance along - the line - @param dy receives the spin box for the distance across - it - */ - void addOffsetRow(QGridLayout *grid, - int row, - const QString &name, - const QString &tip, - QSpinBox **dx, - QSpinBox **dy); - ///Open the window setting one text, then say so if it changed - void editFormat(const QString &key); - ///Let the button say which size that text is written with - void refreshFormatButton(const QString &key); - ///The font this text is really written with right now - QFont effectiveFont(const QString &key) const; - ///True when this key names the colours of the cores - static bool coresKey(const QString &key) {return key == QLatin1String("cores");} - - private: - QLineEdit *m_designation = nullptr; - /** - The name exactly as it was read from the cable, and - whether that name was typed in: anything else he puts - into the field from then on is his own work, and the - cable is marked as carrying a name he chose (see - properties()) - */ - QString m_designation_text; - bool m_designation_by_hand = false; - QLineEdit *m_type = nullptr; - QLineEdit *m_installation = nullptr; - QLineEdit *m_location = nullptr; - QLineEdit *m_length = nullptr; - QCheckBox *m_show_designation = nullptr; - QCheckBox *m_show_type = nullptr; - QCheckBox *m_show_installation = nullptr; - QCheckBox *m_show_location = nullptr; - QCheckBox *m_show_length = nullptr; - ///What each text of this form is written with; a text which - ///is not in here follows what the preferences say - QHash m_formats; - ///The button opening the window of each text, by name - QHash m_format_buttons; - ///How far the label of the cable is nudged on the drawing - QSpinBox *m_text_offset_x = nullptr; - QSpinBox *m_text_offset_y = nullptr; - ///How far the row of colours is nudged on the drawing - QSpinBox *m_core_offset_x = nullptr; - QSpinBox *m_core_offset_y = nullptr; -}; - -#endif // CABLEPROPERTIESWIDGET_H diff --git a/sources/cable/cablerenumber.cpp b/sources/cable/cablerenumber.cpp deleted file mode 100644 index 4d9b23159..000000000 --- a/sources/cable/cablerenumber.cpp +++ /dev/null @@ -1,292 +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 "cablerenumber.h" - -#include "cable.h" -#include "../autoNum/assignvariables.h" -#include "../autoNum/numerotationcontextcommands.h" -#include "../diagram.h" -#include "../qetproject.h" - -#include -#include - -#include -#include - -namespace { - -/** - @brief One cable of the project, and where it stands so that the - cables can be laid out in an order everybody can see again. -*/ -struct OrderedCable -{ - Cable *cable = nullptr; - ///The lowest folio the cable is drawn on - int folio = 0; - ///How far it reaches to the left and how far to the top on - ///that folio - qreal left = 0.0; - qreal top = 0.0; - ///The folio the rule is read on for this cable - Diagram *diagram = nullptr; -}; - -/** - @brief Read a rule once, the way the numbering of a cable reads it. - - The rule is read on a copy, since reading it fills in what its - folio-bound parts are worth on that one folio, and that must not - creep into the rule itself. - @param context the rule as it stands at this point of the numbering - @param diagram the folio the number is worked out on - @param key the name the rule is kept under in the project - @return the number, empty when the rule says nothing here -*/ -QString readRule(NumerotationContext context, Diagram *diagram, const QString &key) -{ - const QString formula = autonum::numerotationContextToFormula(context); - autonum::sequentialNumbers seq; - autonum::setSequential(formula, seq, context, diagram, key); - return autonum::AssignVariables::formulaToLabel(formula, seq, diagram); -} - -} // namespace - -/** - @brief CableRenumber::byHandCount - @param project - @return how many cables of that project carry a name typed in by hand -*/ -int CableRenumber::byHandCount(QETProject *project) -{ - if (!project) return 0; - - int count = 0; - for (Cable *cable : project->cables()) - { - if (cable && cable->designationByHand()) { - ++count; - } - } - return count; -} - -/** - @brief CableRenumber::plan - Work the number of every cable of the project out, without writing - anywhere. The cables are put in the order they are numbered in -- - folio by folio, and on each folio along whichever axis leads -- and - the rule is then read from its beginning once for each of them, so - that the numbering runs on without a hole. - @param project the project whose cables are numbered - @param rule the numbering rule to read - @param x_axis_first true when the leftmost cable of a folio comes first - @param include_by_hand false to leave the names typed in by hand alone - @return the numbers, or what stopped the numbering -*/ -CableRenumberPlan CableRenumber::plan(QETProject *project, - const NumerotationContext &rule, - bool x_axis_first, - bool include_by_hand) -{ - CableRenumberPlan result; - if (!project) - { - result.error = QCoreApplication::translate( - "CableRenumber", "No project is open."); - return result; - } - if (project->isReadOnly()) - { - result.error = QCoreApplication::translate( - "CableRenumber", - "This project is read-only: its cables cannot be " - "renumbered."); - return result; - } - //The rule may be one the numbering window is drawing right now - //and has not written into the project yet: the numbering writes - //it together with the numbers themselves, so the project does - //not have to hold it beforehand. What the counters are kept - //under is the name the rule is stored with, which for cables is - //fixed either way -- so numbering starts from a project which - //has no rule saved yet just as well. - QString key = project->cableCurrentAutoNum(); - if (key.isEmpty()) { - key = QETProject::cableAutoNumRuleName(); - } - if (rule.isEmpty()) - { - result.error = QCoreApplication::translate( - "CableRenumber", - "No cable numbering rule is defined for this " - "project. Define one in this window before " - "renumbering its cables."); - return result; - } - - //Where every cable stands, and which ones are left alone: their - //names stay as they are, so the numbering must not hand the - //same number out again - QList ordered; - QSet taken; - for (Cable *cable : project->cables()) - { - if (!cable) continue; - if (!include_by_hand && cable->designationByHand()) - { - if (!cable->designation().isEmpty()) { - taken.insert(cable->designation()); - } - continue; - } - - OrderedCable entry; - entry.cable = cable; - - for (const CablePartData &part : cable->parts()) - { - Diagram *diagram = project->diagramByUuid(part.diagram); - if (!diagram) continue; - - const int index = project->folioIndex(diagram); - const qreal left = qMin(part.p1.x(), part.p2.x()); - const qreal top = qMin(part.p1.y(), part.p2.y()); - - if (!entry.diagram || index < entry.folio) - { - entry.folio = index; - entry.diagram = diagram; - entry.left = left; - entry.top = top; - } else if (index == entry.folio) { - entry.left = qMin(entry.left, left); - entry.top = qMin(entry.top, top); - } - } - //A cable which stands on no folio the project still knows - //is numbered last rather than first by accident - if (!entry.diagram) { - entry.folio = std::numeric_limits::max(); - } - ordered.append(entry); - } - - //Folio by folio, and on each folio along the axis which leads: - //the other axis is only asked when the leading one says two - //cables are level, so that the numbering never depends on the - //order the cables happen to be stored in - std::sort(ordered.begin(), ordered.end(), - [x_axis_first](const OrderedCable &a, const OrderedCable &b) - { - if (a.folio != b.folio) return a.folio < b.folio; - - const qreal a_first = x_axis_first ? a.left : a.top; - const qreal b_first = x_axis_first ? b.left : b.top; - if (a_first != b_first) return a_first < b_first; - - const qreal a_second = x_axis_first ? a.top : a.left; - const qreal b_second = x_axis_first ? b.top : b.left; - if (a_second != b_second) return a_second < b_second; - - return a.cable->uuid().toString() < b.cable->uuid().toString(); - }); - - //Read the rule from the beginning, once for each cable: its - //counters are put back first, since numbering a project over - //starts at the beginning whatever the last number was - NumerotationContext context = autonum::resetContextCounters(rule); - int current_folio = -1; - - for (const OrderedCable &entry : std::as_const(ordered)) - { - if (!entry.diagram) - { - result.error = QCoreApplication::translate( - "CableRenumber", - "A cable is not on any sheet of the project: " - "nothing was changed."); - return result; - } - - //A counter bound to the folio counts from the beginning of - //that folio: the first cable of a folio gets the first - //number of it, and the folios after it start again rather - //than carrying the number of the folio before on - if (entry.folio != current_folio) - { - context = autonum::resetFolioCounters(context); - current_folio = entry.folio; - } - - const QString candidate = readRule(context, entry.diagram, key); - if (candidate.isEmpty()) - { - result.error = QCoreApplication::translate( - "CableRenumber", - "The current rule gives no number here: " - "nothing was changed."); - return result; - } - //A rule which does not count hands out the same number - //twice, and two cables of one project may not be called - //the same thing - if (result.designations.contains(candidate)) - { - result.error = QCoreApplication::translate( - "CableRenumber", - "The current rule gives the same number (%1) twice: " - "it must count to renumber a whole project. " - "Nothing was changed.") - .arg(candidate); - return result; - } - //A name which stays because it was typed in keeps its - //number, and the numbering may not hand that number out - if (taken.contains(candidate)) - { - result.error = QCoreApplication::translate( - "CableRenumber", - "Number %1 is already used by a cable whose name " - "is left as it is: nothing was changed.") - .arg(candidate); - return result; - } - - taken.insert(candidate); - result.cables.append(entry.cable); - result.designations.append(candidate); - - //Step the rule on for the next cable; a rule which cannot - //step stays where it is and is caught by the check above - NumerotationContextCommands ncc(context, entry.diagram); - const NumerotationContext following = ncc.next(); - if (following.size() == context.size()) { - context = following; - } - } - - //Where the rule stands after the last cable has been numbered: - //the caller writes that into the project, so numbering goes on - //from there instead of from wherever the rule happened to stand - //before this numbering ran - result.after = context; - return result; -} diff --git a/sources/cable/cablerenumber.h b/sources/cable/cablerenumber.h deleted file mode 100644 index c97513e8d..000000000 --- a/sources/cable/cablerenumber.h +++ /dev/null @@ -1,102 +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 CABLERENUMBER_H -#define CABLERENUMBER_H - -#include -#include -#include - -#include "../autoNum/numerotationcontext.h" - -class Cable; -class QETProject; - -/** - @brief What numbering a whole project would do, worked out before a - single cable is written to. - - Working it out first means the answer can be refused as a whole: a - rule which hands out the same number twice, or a number some cable - which is left alone already carries, stops the whole numbering - rather than numbering half the project and then getting stuck. -*/ -struct CableRenumberPlan -{ - ///The cables to number, in the order they are numbered in - QList cables; - ///The number each of them gets, one for each cable - QStringList designations; - ///The rule as it stands once the last cable has been numbered: - ///its counters are where the next number would be taken from - NumerotationContext after; - ///What stopped the numbering, empty while it may be carried out - QString error; - - bool ok() const {return error.isEmpty();} -}; - -/** - @brief Working out the numbers a whole project should carry. - - Everything here is pure: nothing is written to the project and - nothing is put on the undo stack, so a caller may look at the plan - and decide against it without anything having happened. -*/ -namespace CableRenumber -{ - /** - How many cables of this project carry a name which was typed in - rather than handed out by the numbering rule. Those are the ones - renumbering asks about before it takes their name away. - @param project - @return the number of such cables - */ - int byHandCount(QETProject *project); - - /** - Work the number of every cable of the project out of @p rule. - - The cables are laid out first -- folio by folio, and on each - folio either from the left to the right or from the top to the - bottom, whichever axis leads -- and are then numbered one after - the other from the beginning of the rule, so that a hole left by - a cable which has been deleted closes behind it. - - @param project the project whose cables are numbered - @param rule the numbering rule to read; its counters are put - back to 1 first, since numbering starts from the beginning - @param x_axis_first true to number the cables of a folio from - the left to the right, false to number them from the top to the - bottom - @param include_by_hand false to leave the cables whose name was - typed in exactly as they are - @return the numbers, or what stopped the numbering. The plan - also carries the rule as it stands once every cable has been - given its number, so that the caller may put that into the - project: the counters have to stay where the last number was - taken from, or the next cable drawn would skip over every - number in between - */ - CableRenumberPlan plan(QETProject *project, - const NumerotationContext &rule, - bool x_axis_first, - bool include_by_hand); -} - -#endif // CABLERENUMBER_H diff --git a/sources/cable/cabletextformatdialog.cpp b/sources/cable/cabletextformatdialog.cpp deleted file mode 100644 index 550b46516..000000000 --- a/sources/cable/cabletextformatdialog.cpp +++ /dev/null @@ -1,278 +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 "cabletextformatdialog.h" - -#include "../qetapp.h" -#include "../utils/qetutils.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace { - /** - @brief The way a text lines up, as the dialog lists it: right - first, which is where every text stands unless he says otherwise. - @param index the place in the list of the dialog - @return the flags the drawing understands - */ - Qt::Alignment alignmentFromIndex(int index) - { - switch (index) - { - case 1: return Qt::AlignHCenter; - case 2: return Qt::AlignLeft; - default: return Qt::AlignRight; - } - } - - /** - @brief The place in the list of the dialog of an alignment. - @param alignment Qt::AlignLeft, Qt::AlignHCenter or Qt::AlignRight - @return the index, 0 (right) when nothing is named - */ - int alignmentIndex(int alignment) - { - switch (alignment) - { - case int(Qt::AlignHCenter): return 1; - case int(Qt::AlignLeft): return 2; - default: return 0; - } - } - - /** - Whether two fonts would be read the very same way on the sheet: - the family he picks, the size he gives it, and the style he - chose among the four offered. - @param a the font he has chosen - @param b the font the preferences would write it with - @return true when nothing of them differs - */ - bool sameFont(const QFont &a, const QFont &b) - { - return a.family() == b.family() - && qFuzzyCompare(a.pointSizeF(), b.pointSizeF()) - && a.bold() == b.bold() - && a.italic() == b.italic(); - } -} - -/** - @brief CableTextFormatDialog::CableTextFormatDialog - @param format what this cable writes that text with right now - @param cores true for the colours of the cores - @param parent -*/ -CableTextFormatDialog::CableTextFormatDialog(const CableTextFormat &format, - bool cores, - QWidget *parent) : - QDialog(parent), - m_cores(cores) -{ - setWindowTitle(cores ? tr("Core colors") - : tr("Text format")); - buildUi(); - - //The font this text is written with right now: the one this - //cable asks for, or the one the preferences offer when it asks - //for none -- which is what he sees on the sheet as he opens this. - QFont font; - bool restored = false; - if (!format.font.isEmpty()) { - restored = QETUtils::fontFromString(font, format.font); - } - if (!restored) { - font = cores ? QETApp::cableCoreFont() : QETApp::cableTextsFont(); - } - - //Where this text stands right now, in the same way: the answer - //of the cable itself, or -- when it has none -- the one the - //preferences offer, so that he opens this onto what the sheet - //really shows rather than onto whatever the default used to be. - m_initial_alignment = cores ? 0 - : (format.alignment ? format.alignment - : int(QETApp::cableTextAlignment())); - - m_family->setCurrentFont(font); - m_size->setValue(font.pointSizeF()); - if (font.bold() && font.italic()) m_style->setCurrentIndex(3); - else if (font.italic()) m_style->setCurrentIndex(2); - else if (font.bold()) m_style->setCurrentIndex(1); - else m_style->setCurrentIndex(0); - if (m_alignment) { - m_alignment->setCurrentIndex(alignmentIndex(m_initial_alignment)); - } - - //Going back to the preferences means something only while this - //cable has an answer of its own: when it already follows them, - //there is nothing here to give back. - m_reset->setEnabled(!format.font.isEmpty() || format.alignment != 0); - updatePreview(); -} - -/** - @brief CableTextFormatDialog::buildUi - One row per thing he may set, then what it looks like -- because a - size means nothing as a number alone. -*/ -void CableTextFormatDialog::buildUi() -{ - auto *form = new QFormLayout; - form->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); - form->setHorizontalSpacing(12); - - m_family = new QFontComboBox(this); - m_family->setFontFilters(QFontComboBox::AllFonts); - form->addRow(tr("Font:"), m_family); - - m_size = new QDoubleSpinBox(this); - m_size->setRange(1.0, 300.0); - m_size->setDecimals(1); - m_size->setSingleStep(0.5); - m_size->setSuffix(tr(" pt")); - form->addRow(tr("Size:"), m_size); - - m_style = new QComboBox(this); - m_style->addItem(tr("Normal")); - m_style->addItem(tr("Bold")); - m_style->addItem(tr("Italic")); - m_style->addItem(tr("Bold italic")); - form->addRow(tr("Style:"), m_style); - - //The colours of the cores stand turned by a quarter along their - //own line: there is nothing for them to line up with. - if (!m_cores) - { - m_alignment = new QComboBox(this); - m_alignment->addItem(tr("Right aligned")); - m_alignment->addItem(tr("Centred")); - m_alignment->addItem(tr("Left aligned")); - form->addRow(tr("Alignment:"), m_alignment); - } - - m_preview = new QLabel(tr("15W1 · H07V-K 3G1.5"), this); - m_preview->setFrameStyle(QFrame::Box | QFrame::Plain); - m_preview->setMinimumHeight(30); - m_preview->setIndent(6); - form->addRow(tr("Preview:"), m_preview); - - auto *buttons = new QDialogButtonBox( - QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); - m_reset = buttons->addButton(tr("Reset"), QDialogButtonBox::ResetRole); - connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); - connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); - connect(m_reset, &QPushButton::clicked, this, [this]() { - m_reset_chosen = true; - accept(); - }); - - auto *layout = new QVBoxLayout(this); - layout->addLayout(form); - layout->addWidget(buttons); - setLayout(layout); - - connect(m_family, &QFontComboBox::currentFontChanged, - this, &CableTextFormatDialog::updatePreview); - connect(m_size, &QDoubleSpinBox::valueChanged, - this, &CableTextFormatDialog::updatePreview); - connect(m_style, &QComboBox::currentIndexChanged, - this, &CableTextFormatDialog::updatePreview); - if (m_alignment) { - connect(m_alignment, &QComboBox::currentIndexChanged, - this, &CableTextFormatDialog::updatePreview); - } -} - -/** - @brief CableTextFormatDialog::chosenFont - What the dialog offers right now: the family he picked, the size he - gave it and the style he chose among the four offered -- rather than - whatever that family happened to be called in the list. - @return the font -*/ -QFont CableTextFormatDialog::chosenFont() const -{ - QFont font = m_family->currentFont(); - font.setPointSizeF(m_size->value()); - const bool italic = m_style->currentIndex() == 2 || m_style->currentIndex() == 3; - const bool bold = m_style->currentIndex() == 1 || m_style->currentIndex() == 3; - font.setBold(bold); - font.setItalic(italic); - return font; -} - -/** - @brief CableTextFormatDialog::updatePreview - Write the line offered as an example the way it would be written - after what he has just set -- the only place where a size becomes - something one can judge. -*/ -void CableTextFormatDialog::updatePreview() -{ - m_preview->setFont(chosenFont()); - if (m_alignment) { - m_preview->setAlignment( - alignmentFromIndex(m_alignment->currentIndex()) | Qt::AlignVCenter); - } else { - m_preview->setAlignment(Qt::AlignCenter); - } -} - -/** - @brief CableTextFormatDialog::format - @return what the cable is to write that text with from now on. - - An empty answer means "the way the preferences say", and the two - halves of it are worked out apart from each other: he may set the - way one text lines up without taking away its freedom to follow a - new default font later, and the other way round. What already - stands the way the preferences say is left out, so that a cable - which said nothing keeps saying nothing -- and a later change of - what those preferences are still reaches it. -*/ -CableTextFormat CableTextFormatDialog::format() const -{ - if (m_reset_chosen) return CableTextFormat(); - - const QFont font = chosenFont(); - const QFont default_font = m_cores ? QETApp::cableCoreFont() - : QETApp::cableTextsFont(); - - CableTextFormat answer; - if (!sameFont(font, default_font)) { - answer.font = QETUtils::fontToString(font); - } - //The colours of the cores stand turned by a quarter: they have - //no line to line up with, and saying so would only be noise in - //the file. - if (!m_cores) - { - const int alignment = int(alignmentFromIndex(m_alignment->currentIndex())); - if (alignment != int(QETApp::cableTextAlignment())) { - answer.alignment = alignment; - } - } - return answer; -} diff --git a/sources/cable/cabletextformatdialog.h b/sources/cable/cabletextformatdialog.h deleted file mode 100644 index 49db070f2..000000000 --- a/sources/cable/cabletextformatdialog.h +++ /dev/null @@ -1,83 +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 CABLETEXTFORMATDIALOG_H -#define CABLETEXTFORMATDIALOG_H - -#include "cable.h" - -#include - -class QComboBox; -class QFontComboBox; -class QDoubleSpinBox; -class QLabel; -class QPushButton; - -/** - @brief The small window where one text of one cable is set: the font - it is written with -- family, size and style -- and, for the texts - written next to the trunk line, the way it lines up at the line. - - The colours of the cores come here as well, all of them together - because they are drawn together: they have no alignment, being - turned by a quarter along their own line. - - Saying nothing is the normal case: the cable then follows what the - preferences offer on the "Textes" page, and OK keeps an answer of - its own only when he really changed something -- so that picking - another default later on still reaches this cable. -*/ -class CableTextFormatDialog : public QDialog -{ - Q_OBJECT - - public: - /** - @param format what this cable writes that text with right - now, an empty format when it merely follows the preferences - @param cores true for the colours of the cores: they have - no alignment and follow QETApp::cableCoreFont() - @param parent - */ - CableTextFormatDialog(const CableTextFormat &format, - bool cores, - QWidget *parent = nullptr); - - ///What the cable is to write that text with from now on - CableTextFormat format() const; - - private: - void buildUi(); - void updatePreview(); - QFont chosenFont() const; - - bool m_cores = false; - ///True when he asked to go back to following the preferences - bool m_reset_chosen = false; - ///The alignment this text has right now, 0 for the cores - int m_initial_alignment = int(Qt::AlignRight); - - QFontComboBox *m_family = nullptr; - QDoubleSpinBox *m_size = nullptr; - QComboBox *m_style = nullptr; - QComboBox *m_alignment = nullptr; - QLabel *m_preview = nullptr; - QPushButton *m_reset = nullptr; -}; - -#endif // CABLETEXTFORMATDIALOG_H diff --git a/sources/cable/editcablecommand.cpp b/sources/cable/editcablecommand.cpp deleted file mode 100644 index eb27596d8..000000000 --- a/sources/cable/editcablecommand.cpp +++ /dev/null @@ -1,494 +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 "editcablecommand.h" - -#include "cablemanager.h" -#include "cablepart.h" -#include "../diagram.h" -#include "../qetproject.h" - -#include - -#include - -/** - @brief BatchCommand::BatchCommand - @param first step to run first, kept by this command - @param second step to run right after it, kept by this command -*/ -BatchCommand::BatchCommand(QUndoCommand *first, QUndoCommand *second) : - m_steps({first, second}) -{ - m_steps.removeAll(nullptr); - setText(QCoreApplication::translate("BatchCommand", "Delete")); -} - -/** - @brief BatchCommand::BatchCommand - @param steps every step of one single gesture, in the order the - user did them, kept by this command - @param text what the gesture was, as the undo history shows it -*/ -BatchCommand::BatchCommand(const QList &steps, const QString &text) : - m_steps(steps) -{ - m_steps.removeAll(nullptr); - setText(text); -} - -/** - @brief BatchCommand::~BatchCommand - The steps it holds go with it: they were never given to anything else. -*/ -BatchCommand::~BatchCommand() -{ - for (QUndoCommand *step : std::as_const(m_steps)) { - delete step; - } -} - -/** - @brief BatchCommand::redo - Every step, in the order the user did them. -*/ -void BatchCommand::redo() -{ - for (QUndoCommand *step : std::as_const(m_steps)) { - if (step) step->redo(); - } -} - -/** - @brief BatchCommand::undo - Every step taken back in the reverse order, so what one step changed - is still there when the next one is undone. -*/ -void BatchCommand::undo() -{ - for (auto it = m_steps.crbegin(); it != m_steps.crend(); ++it) { - if (*it) (*it)->undo(); - } -} - -/** - @brief MoveCablePartCommand::MoveCablePartCommand - @param cable the cable the section belongs to - @param part the line being dragged - @param before_p1 - @param before_p2 - @param after_p1 - @param after_p2 - @param before_cores where every colour label stood before the drag - @param after_cores where it stands after it -*/ -MoveCablePartCommand::MoveCablePartCommand(Cable *cable, - CablePart *part, - const QPointF &before_p1, - const QPointF &before_p2, - const QPointF &after_p1, - const QPointF &after_p2, - const QList &before_cores, - const QList &after_cores) : - m_cable(cable), - m_part(part), - m_before_p1(before_p1), - m_before_p2(before_p2), - m_after_p1(after_p1), - m_after_p2(after_p2), - m_before_cores(before_cores), - m_after_cores(after_cores) -{ - setText(QCoreApplication::translate("MoveCablePartCommand", "Move a cable")); -} - -void MoveCablePartCommand::undo() -{ - apply(m_before_p1, m_before_p2, m_before_cores); -} - -void MoveCablePartCommand::redo() -{ - apply(m_after_p1, m_after_p2, m_after_cores); -} - -/** - @brief MoveCablePartCommand::apply - Put the line where it belongs and the colour labels with it. The - positions are written last: they are stored in scene coordinates, so - they only make sense once the line itself is back in place. - @param p1 - @param p2 - @param cores -*/ -void MoveCablePartCommand::apply(const QPointF &p1, - const QPointF &p2, - const QList &cores) -{ - if (m_part) { - m_part->setLine(p1, p2); - } - if (m_cable) { - m_cable->setCores(cores); - } -} - -/** - @brief ChangeCablePropertiesCommand::ChangeCablePropertiesCommand - @param cable - @param before every field as it was before the edit - @param after every field as the user left it -*/ -ChangeCablePropertiesCommand::ChangeCablePropertiesCommand(Cable *cable, - const CableProperties &before, - const CableProperties &after) : - m_cable(cable), - m_before(before), - m_after(after) -{ - setText(QCoreApplication::translate("ChangeCablePropertiesCommand", - "Edit the properties of a cable")); -} - -void ChangeCablePropertiesCommand::undo() -{ - if (m_cable) { - m_cable->setProperties(m_before); - } -} - -void ChangeCablePropertiesCommand::redo() -{ - if (m_cable) { - m_cable->setProperties(m_after); - } -} - -/** - @brief RenumberCablesCommand::RenumberCablesCommand - @param cables the cables to number, in the order they are numbered in - @param designations the number each of them gets - @param project the project whose numbering rule moves along with - the numbering - @param num_key the name that rule is kept under in the project - @param num_before where its counters stood before the numbering ran - @param num_after where the numbering leaves them -*/ -RenumberCablesCommand::RenumberCablesCommand(const QList &cables, - const QStringList &designations, - QETProject *project, - const QString &num_key, - const NumerotationContext &num_before, - const NumerotationContext &num_after) : - m_project(project), - m_num_key(num_key), - m_num_before(num_before), - m_num_after(num_after) -{ - for (int i = 0; i < cables.size() && i < designations.size(); ++i) - { - Cable *cable = cables.at(i); - if (!cable) continue; - - m_cables.append(cable); - m_before.append(cable->designation()); - m_by_hand_before.append(cable->designationByHand()); - m_after.append(designations.at(i)); - //A number the rule has just handed out is the rule's, so - //nothing here stays marked as having been typed in - m_by_hand_after.append(false); - } - - setText(QCoreApplication::translate("RenumberCablesCommand", - "Renumber %n cable(s)", - "", m_cables.size())); -} - -void RenumberCablesCommand::undo() -{ - apply(false); -} - -void RenumberCablesCommand::redo() -{ - apply(true); -} - -/** - @brief RenumberCablesCommand::apply - @param use_after true to write the numbers the rule handed out, - false to put the ones back which were there before -*/ -void RenumberCablesCommand::apply(bool use_after) -{ - for (int i = 0; i < m_cables.size(); ++i) - { - Cable *cable = m_cables.at(i).data(); - if (!cable) continue; - - //Where the name comes from is written first: a number the - //rule has just handed out is the rule's whatever the cable - //was called before, and undo has to say so again - cable->setDesignationByHand(use_after ? m_by_hand_after.at(i) - : m_by_hand_before.at(i)); - cable->setDesignation(use_after ? m_after.at(i) : m_before.at(i)); - } - - //Where the numbering rule itself stands moves along with the - //numbering: once it has run, its counters are where the last - //number was taken from, so the next cable drawn goes on from - //there rather than skipping over every number in between. Undo - //puts them back like the names, since numbering a project over - //is one single act. - if (m_project && !m_num_key.isEmpty()) - { - const NumerotationContext context = use_after ? m_num_after - : m_num_before; - //An empty rule is never written: losing the rule altogether - //is not something numbering may do, not even on the way back - if (!context.isEmpty()) - { - m_project->addCableAutoNum(m_num_key, context); - m_project->setCurrentCableAutoNum(m_num_key); - } - } -} - -/** - @brief ChangeCableTypeCommand::ChangeCableTypeCommand - @param cable - @param before the type, the count, the colours and the cores as - they were before the change - @param after the very same four as the chosen type says -*/ -ChangeCableTypeCommand::ChangeCableTypeCommand(Cable *cable, - const CableTypeState &before, - const CableTypeState &after) : - m_cable(cable), - m_before(before), - m_after(after) -{ - setText(QCoreApplication::translate("ChangeCableTypeCommand", - "Change the cable type")); -} - -void ChangeCableTypeCommand::undo() -{ - apply(m_before); -} - -void ChangeCableTypeCommand::redo() -{ - apply(m_after); -} - -/** - @brief ChangeCableTypeCommand::apply - One block in one pass: name, colours, count, then the cores. The - count has to stand before the cores, since a cable with fewer cores - cannot hold the others -- and the other way round, undoing a type - with fewer cores has to open the room before the cores come back - into it. - @param state the block to write -*/ -void ChangeCableTypeCommand::apply(const CableTypeState &state) -{ - if (m_cable) { - m_cable->applyTypeState(state); - } -} - -/** - @brief ChangeCableCoresCommand::ChangeCableCoresCommand - @param cable - @param before every core the cable wired before the change - @param after every core it wires after it - @param text what undo says about this step, empty for a core which - is merely moved -*/ -ChangeCableCoresCommand::ChangeCableCoresCommand(Cable *cable, - const QList &before, - const QList &after, - const QString &text) : - m_cable(cable), - m_before(before), - m_after(after) -{ - setText(text.isEmpty() - ? QCoreApplication::translate("ChangeCableCoresCommand", - "Move a cable core") - : text); -} - -void ChangeCableCoresCommand::undo() -{ - apply(m_before); -} - -void ChangeCableCoresCommand::redo() -{ - apply(m_after); -} - -/** - @brief ChangeCableCoresCommand::apply - Write one whole set of assignments in place of another. The cable - says it changed, which is what makes the project work out the cable - field of every conductor it feeds again, so the drawing follows. - @param cores -*/ -void ChangeCableCoresCommand::apply(const QList &cores) -{ - if (m_cable) { - m_cable->setCores(cores); - } -} - -/** - @brief RemoveCableCommand::RemoveCableCommand - @param diagram the folios the lines are drawn on - @param parts the lines the user asked to delete -*/ -RemoveCableCommand::RemoveCableCommand(Diagram *diagram, const QList &parts) : - m_diagram(diagram) -{ - setText(QCoreApplication::translate("RemoveCableCommand", "Delete a cable")); - - for (CablePart *part : parts) - { - if (!part) continue; - - Entry entry; - entry.part = part; - entry.cable = part->cable(); - entry.data.uuid = part->partUuid(); - entry.data.diagram = m_diagram ? m_diagram->uuid() : QUuid(); - entry.data.p1 = part->firstPoint(); - entry.data.p2 = part->secondPoint(); - m_entries.append(entry); - - if (entry.cable && !m_cables.contains(entry.cable)) { - m_cables.append(entry.cable); - m_cores.insert(entry.cable.data(), entry.cable->usedCores()); - } - } -} - -/** - @brief RemoveCableCommand::~RemoveCableCommand - Whatever the stack leaves behind has to be cleaned up here: a cable - which is not held by the project any more, and a line which never - made it back onto a folio, belong to this command. -*/ -RemoveCableCommand::~RemoveCableCommand() -{ - QETProject *proj = project(); - for (const QPointer &cable : std::as_const(m_cables)) - { - if (cable && (!proj || !proj->cables().contains(cable))) { - delete cable; - } - } - for (const Entry &entry : std::as_const(m_entries)) - { - if (entry.part && !entry.part->scene()) { - delete entry.part; - } - } -} - -/** - @brief RemoveCableCommand::project - @return the project this command works in, or nullptr -*/ -QETProject *RemoveCableCommand::project() const -{ - return m_diagram ? m_diagram->project() : nullptr; -} - -/** - @brief RemoveCableCommand::redo - Take the lines off their folios. Each cable loses the sections which - are gone together with the cores drawn on them, and a cable which has - no section left anywhere leaves the project: a cable nothing draws is - no cable of this project. -*/ -void RemoveCableCommand::redo() -{ - QETProject *proj = project(); - - for (const Entry &entry : std::as_const(m_entries)) - { - if (entry.cable) { - entry.cable->removePart(entry.data.uuid); - } - if (entry.part && entry.part->scene() && m_diagram) { - m_diagram->removeItem(entry.part); - } - } - - if (proj) - { - for (const QPointer &cable : std::as_const(m_cables)) - { - if (cable && cable->parts().isEmpty() && proj->cables().contains(cable)) { - proj->removeCable(cable); - } - } - } - - CableManager::refreshLabels(proj); -} - -/** - @brief RemoveCableCommand::undo - Put the lines back on their folios, the sections back in their cable - and the cores back on those sections: exactly the state the delete - action took away. -*/ -void RemoveCableCommand::undo() -{ - QETProject *proj = project(); - - if (proj) - { - for (const QPointer &cable : std::as_const(m_cables)) - { - if (cable && !proj->cables().contains(cable)) { - proj->addCable(cable); - } - } - } - - for (const Entry &entry : std::as_const(m_entries)) - { - if (entry.cable) - { - if (!entry.cable->hasPart(entry.data.uuid)) { - entry.cable->addPart(entry.data); - } - for (const CableCore &core : m_cores.value(entry.cable.data())) { - entry.cable->setCore(core); - } - } - if (entry.part && !entry.part->scene() && m_diagram) { - m_diagram->addItem(entry.part); - } - } - - CableManager::refreshLabels(proj); -} diff --git a/sources/cable/editcablecommand.h b/sources/cable/editcablecommand.h deleted file mode 100644 index 1308b6b66..000000000 --- a/sources/cable/editcablecommand.h +++ /dev/null @@ -1,285 +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 EDITCABLECOMMAND_H -#define EDITCABLECOMMAND_H - -#include "cable.h" - -#include "../autoNum/numerotationcontext.h" - -#include -#include -#include -#include - -class CablePart; -class Diagram; -class QETProject; - -/** - @brief Puts a dragged cable line back where it was, or where the user - just dragged it to. - - The colour labels of its cores follow the line -- they are drawn on - it -- and come back with it when the move is undone. Which core lands - on which conductor never depends on where the trunk line stands, so - the assignments are only carried along by this step, never changed. -*/ -class MoveCablePartCommand : public QUndoCommand -{ - public: - MoveCablePartCommand(Cable *cable, - CablePart *part, - const QPointF &before_p1, - const QPointF &before_p2, - const QPointF &after_p1, - const QPointF &after_p2, - const QList &before_cores, - const QList &after_cores); - - void undo() override; - void redo() override; - - private: - void apply(const QPointF &p1, - const QPointF &p2, - const QList &cores); - - private: - QPointer m_cable; - QPointer m_part; - QPointF m_before_p1; - QPointF m_before_p2; - QPointF m_after_p1; - QPointF m_after_p2; - QList m_before_cores; - QList m_after_cores; -}; - -/** - @brief Rewrites which cores a cable wires, the way dragging the slash - of a core does: the whole set of assignments is taken before and - after the gesture, so undoing puts every one of them back -- the core - which was moved, but also the one it swapped colours with. -*/ -class ChangeCableCoresCommand : public QUndoCommand -{ - public: - /** - @param cable the cable whose assignments are rewritten - @param before every core it held before - @param after every core it holds after - @param text what undo says about this step: an empty string - for the usual step of moving a core around, since putting a - core down for the first time is not a move at all - */ - ChangeCableCoresCommand(Cable *cable, - const QList &before, - const QList &after, - const QString &text = QString()); - - void undo() override; - void redo() override; - - private: - void apply(const QList &cores); - - private: - QPointer m_cable; - QList m_before; - QList m_after; -}; - -/** - @brief Writes what the user typed into the fields of a cable -- and - what he ticks off again -- as one single undo step. - - All the fields belong to the cable rather than to one line of it, so - one change puts every line of that cable, on every folio, right. -*/ -class ChangeCablePropertiesCommand : public QUndoCommand -{ - public: - ChangeCablePropertiesCommand(Cable *cable, - const CableProperties &before, - const CableProperties &after); - - void undo() override; - void redo() override; - - private: - QPointer m_cable; - CableProperties m_before; - CableProperties m_after; -}; - -/** - @brief Gives every cable of a project the number the numbering rule - says it should have, laid out in the order the cables are met along - the sheets. - - That is how a project is numbered again after a cable has been taken - away: the hole its number leaves closes because the cables behind it - move up one place, and a rule which has just been changed reaches - every cable of the project at once. - - One command covers them all -- numbering a whole project is one - single act, so undo takes all of it back together. What the - numbering rule itself holds is part of that act: once the cables - are numbered, the counters of the rule stand where the last number - was taken from, and undo puts them back where they stood. -*/ -class RenumberCablesCommand : public QUndoCommand -{ - public: - /** - @param cables the cables to number, in the order they are - numbered in - @param designations the number each of them gets, one for - each cable - @param project the project whose numbering rule moves along - with the numbering - @param num_key the name that rule is kept under in the - project - @param num_before where its counters stood before the - numbering ran - @param num_after where the numbering leaves them - */ - RenumberCablesCommand(const QList &cables, - const QStringList &designations, - QETProject *project, - const QString &num_key, - const NumerotationContext &num_before, - const NumerotationContext &num_after); - - void undo() override; - void redo() override; - - private: - void apply(bool use_after); - - private: - QList> m_cables; - QStringList m_before; - QStringList m_after; - QList m_by_hand_before; - QList m_by_hand_after; - QETProject *m_project = nullptr; - QString m_num_key; - NumerotationContext m_num_before; - NumerotationContext m_num_after; -}; - -/** - @brief Changes which type a cable is -- its name, how many cores it - has and the colour of each of them -- together with the cores it - may lose on the way, as one single step. - - The cores belong to this step rather than to a step of their own: - a type with fewer cores takes marks off the drawing, and undoing has - to bring them back along with the name and the colours, never half - of it. -*/ -class ChangeCableTypeCommand : public QUndoCommand -{ - public: - /** - @param cable the cable whose type is changed - @param before the state the cable was in - @param after the state it is put into - */ - ChangeCableTypeCommand(Cable *cable, - const CableTypeState &before, - const CableTypeState &after); - - void undo() override; - void redo() override; - - private: - void apply(const CableTypeState &state); - - private: - QPointer m_cable; - CableTypeState m_before; - CableTypeState m_after; -}; - -/** - @brief Holds the steps of one single gesture together, so a single - undo takes all of them back -- an ordinary deletion with the cable - deletions which go with it, or a drag which moved several cable - lines at once. - - It keeps its steps rather than handing them to QUndoCommand as - children: Qt6.11 has no addChild() any more. -*/ -class BatchCommand : public QUndoCommand -{ - public: - BatchCommand(QUndoCommand *first, QUndoCommand *second); - BatchCommand(const QList &steps, const QString &text); - ~BatchCommand() override; - - void undo() override; - void redo() override; - - private: - QList m_steps; -}; - -/** - @brief Takes one or more drawn cable lines off their folios -- and the - cable itself with them when no line of it is left anywhere. - - Undo puts everything back: the lines on their folios, the sections in - the cable, the cores on those sections, and the cable in the project - when it had to leave it. -*/ -class RemoveCableCommand : public QUndoCommand -{ - public: - RemoveCableCommand(Diagram *diagram, const QList &parts); - ~RemoveCableCommand() override; - - void undo() override; - void redo() override; - - private: - QETProject *project() const; - - private: - struct Entry - { - ///The line as it is drawn on the folio - QPointer part; - ///Where the cable holds that section - CablePartData data; - ///The cable the section belongs to - QPointer cable; - }; - - QPointer m_diagram; - QList m_entries; - ///Every cable which loses a section here, with all the cores - ///it wires at this moment: removing a section drops the cores - ///drawn on it, and undo has to bring them back - QList> m_cables; - QHash> m_cores; -}; - -#endif // EDITCABLECOMMAND_H diff --git a/sources/cablelist/cableexportdialog.cpp b/sources/cablelist/cableexportdialog.cpp deleted file mode 100644 index f929f476d..000000000 --- a/sources/cablelist/cableexportdialog.cpp +++ /dev/null @@ -1,155 +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 3 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 "cableexportdialog.h" - -#include "cablelistmodel.h" -#include "cablelistrows.h" -#include "cablequerywidget.h" - -#include "../bomexport.h" -#include "../qet.h" -#include "../qetapp.h" -#include "../qetproject.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** - @brief CableExportDialog::CableExportDialog - @param project the project whose cables are exported - @param parent -*/ -CableExportDialog::CableExportDialog(QETProject *project, QWidget *parent) : - QDialog(parent), - m_project(project) -{ - setWindowTitle(tr("Export the cable list as CSV")); - QET::trackDialogGeometry(this); - - auto *main_layout = new QVBoxLayout(this); - - //The same column picker the cable list is built with, so the - //export asks for its columns the same way the list does - m_query_widget = new CableQueryWidget(this); - main_layout->addWidget(m_query_widget); - - auto *page_group = new QGroupBox(tr("Layout"), this); - auto *page_layout = new QVBoxLayout(page_group); - m_include_headers = new QCheckBox(tr("include headers"), page_group); - m_include_headers->setChecked(true); - page_layout->addWidget(m_include_headers); - page_layout->addStretch(); - main_layout->addWidget(page_group); - - auto *preview_layout = new QHBoxLayout; - auto *preview_pb = new QPushButton(tr("Preview"), this); - preview_layout->addWidget(preview_pb); - preview_layout->addStretch(); - main_layout->addLayout(preview_layout); - - m_preview_table = new QTableView(this); - m_preview_table->setEditTriggers(QAbstractItemView::NoEditTriggers); - main_layout->addWidget(m_preview_table); - - auto *buttons = new QDialogButtonBox( - QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); - main_layout->addWidget(buttons); - connect(buttons, &QDialogButtonBox::accepted, - this, &CableExportDialog::accept); - connect(buttons, &QDialogButtonBox::rejected, - this, &CableExportDialog::reject); - connect(preview_pb, &QPushButton::clicked, - this, &CableExportDialog::preview); - - m_model = new CableListModel(m_project, this); - m_preview_table->setModel(m_model); - preview(); - - setMinimumSize(QSize(760, 560)); -} - -/** - @brief CableExportDialog::exec - Ask for the file once the user accepted, and write the chosen - columns to it -- the same flow as the material list export - @return -*/ -int CableExportDialog::exec() -{ - const auto r = QDialog::exec(); - if (r == QDialog::Accepted) - { - QString dir = m_project->currentDir(); - if (dir.isEmpty()) dir = QETApp::documentDir(); - const QString file_name = dir + QLatin1Char('/') - + tr("cable_list_") + m_project->title() - + QStringLiteral(".csv"); - const QString file_path = QFileDialog::getSaveFileName( - this, tr("Save As... "), file_name, - tr("CSV files (*.csv)")); - if (!file_path.isEmpty()) - { - QString error; - const auto csv = CableList::toCsv( - m_project, selectedColumns(), - m_include_headers->isChecked()); - if (!BomExport::writeCsv(file_path, csv, &error)) { - QMessageBox::critical( - this, tr("Error"), - tr("Cannot save the cable list to %1.\n%2") - .arg(file_path, error)); - } - } - } - return r; -} - -/** - @brief CableExportDialog::selectedColumns - @return the picked columns, and the default ones when the user - picked nothing -- an export of everything is what a dialog - confirmed without choosing asks for, the same fallback the drawn - table uses -*/ -QStringList CableExportDialog::selectedColumns() const -{ - auto keys = m_query_widget->selectedKeys(); - if (keys.isEmpty()) { - keys = CableList::defaultKeys(); - } - return keys; -} - -/** - @brief CableExportDialog::preview - Show what will be written: the chosen columns, worked out from the - cables of the project as they are right now -*/ -void CableExportDialog::preview() -{ - m_model->setFields(selectedColumns()); - m_preview_table->resizeColumnsToContents(); -} diff --git a/sources/cablelist/cableexportdialog.h b/sources/cablelist/cableexportdialog.h deleted file mode 100644 index e14d63ec7..000000000 --- a/sources/cablelist/cableexportdialog.h +++ /dev/null @@ -1,58 +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 3 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 CABLEEXPORTDIALOG_H -#define CABLEEXPORTDIALOG_H - -#include -#include - -class QCheckBox; -class QTableView; - -class CableListModel; -class CableQueryWidget; -class QETProject; - -/** - @brief The CableExportDialog asks which columns of the cable list - are exported and writes them to a CSV file, built the same way the - BOMExportDialog does it for the material list: the column picker - the cable list is built with on top, the page settings under it, a - preview of what will be written, and the file asked for when the - dialog is accepted. -*/ -class CableExportDialog : public QDialog -{ - Q_DECLARE_TR_FUNCTIONS(CableExportDialog) - - public: - explicit CableExportDialog(QETProject *project, QWidget *parent = nullptr); - int exec() override; - - private: - QStringList selectedColumns() const; - void preview(); - - QETProject *m_project; - CableQueryWidget *m_query_widget; - CableListModel *m_model; - QCheckBox *m_include_headers; - QTableView *m_preview_table; -}; - -#endif // CABLEEXPORTDIALOG_H diff --git a/sources/cablelist/cablelistmodel.cpp b/sources/cablelist/cablelistmodel.cpp deleted file mode 100644 index 8991a9522..000000000 --- a/sources/cablelist/cablelistmodel.cpp +++ /dev/null @@ -1,421 +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 3 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 "cablelistmodel.h" - -#include "cablelistrows.h" - -#include "../cable/cable.h" -#include "../diagram.h" -#include "../qetgraphicsitem/element.h" -#include "../qetproject.h" -#include "../qetxml.h" -#include "../utils/qetutils.h" - -#include -#include - -#include -#include - -/** - @brief CableListModel::CableListModel - @param project : project whose cables this model lists - @param parent : parent QObject -*/ -CableListModel::CableListModel(QETProject *project, QObject *parent) : - QAbstractTableModel(parent), - m_project(project) -{ - //Left is what a table starts with and stays with: the factory - //overwrites it with the alignment chosen in the dialog, and a - //table which never got that far (no column selected at - //placement) still draws its cells left rather than from an - //empty role - m_index_0_0_data.insert(Qt::TextAlignmentRole, int(Qt::AlignLeft)); - //The same gap between the text and the cell lines the creation - //dialog offers for the material list, already in place for a - //table which starts out empty and gets its columns later -- - //the factory overwrites it with the margins chosen in the - //dialog as soon as the table has a cell to configure - m_index_0_0_data.insert(Qt::UserRole+1, - QETUtils::marginsToString(QMargins(5,5,10,5))); - connectProject(); - setFields(CableList::defaultKeys()); -} - -/** - @brief CableListModel::CableListModel - @param other_model -*/ -CableListModel::CableListModel(const CableListModel &other_model) : - QAbstractTableModel(other_model.parent()) -{ - setParent(other_model.parent()); - m_project = other_model.m_project; - m_identifier = other_model.m_identifier; - connectProject(); - setFields(other_model.m_fields); - m_header_data = other_model.m_header_data; - m_index_0_0_data = other_model.m_index_0_0_data; -} - -/** - @brief CableListModel::rowCount - Reimplemented for QAbstractTableModel - @param parent - @return -*/ -int CableListModel::rowCount(const QModelIndex &parent) const -{ - if (parent.isValid()) - return 0; - - //A table built without a single column shows no rows either: - //the empty frame a nomenclature table makes when it is - //created without entries, not a list of rows nobody can see - if (m_fields.isEmpty()) { - return 0; - } - - return m_rows.count(); -} - -/** - @brief CableListModel::columnCount - Reimplemented for QAbstractTableModel - @param parent - @return -*/ -int CableListModel::columnCount(const QModelIndex &parent) const -{ - if (parent.isValid()) - return 0; - - return m_fields.count(); -} - -/** - @brief CableListModel::setHeaderData - Reimplemented from QAbstractTableModel. - Only horizontal orientation is accepted. - @param section - @param orientation - @param value - @param role - @return -*/ -bool CableListModel::setHeaderData(int section, Qt::Orientation orientation, - const QVariant &value, int role) -{ - if (orientation == Qt::Vertical) { - return false; - } - auto hash_ = m_header_data.value(section); - hash_.insert(role, value); - m_header_data.insert(section, hash_); - emit headerDataChanged(orientation, section, section); - return true; -} - -/** - @brief CableListModel::headerData - Reimplemented for QAbstractTableModel - @param section - @param orientation - @param role - @return -*/ -QVariant CableListModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (orientation == Qt::Vertical) { - return QVariant(); - } - - if (m_header_data.contains(section)) - { - auto hash_ = m_header_data.value(section); - if (role == Qt::DisplayRole && !hash_.contains(Qt::DisplayRole)) { //special case to have the same behavior as Qt - return hash_.value(Qt::EditRole); - } - return m_header_data.value(section).value(role); - } - return QVariant(); -} - -/** - @brief CableListModel::setData - Only store the data for the index 0.0. An empty table has no cells - yet -- index(0,0) does not exist without a single column -- but - its look is still settable: the factory configuring a freshly made - table must not be thrown away just because the table was placed - without entries and gains its columns afterwards. The data is read - as soon as cells exist. - @param index - @param value - @param role - @return -*/ -bool CableListModel::setData(const QModelIndex &index, const QVariant &value, int role) -{ - if (index.isValid() && (index.row() != 0 || index.column() != 0)) { - return false; - } - m_index_0_0_data.insert(role, value); - if (index.isValid()) { - emit dataChanged(index, index, {role}); - } - return true; -} - -/** - @brief CableListModel::data - Reimplemented for QAbstractTableModel - @param index - @param role - @return -*/ -QVariant CableListModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - if (index.row() == 0 && - index.column() == 0 && - role != Qt::DisplayRole) { - return m_index_0_0_data.value(role); - } - - if (role == Qt::DisplayRole) { - return m_rows.at(index.row()).value(m_fields.at(index.column())); - } - - return QVariant(); -} - -/** - @brief CableListModel::project - @return the project whose cables this model lists -*/ -QETProject *CableListModel::project() const -{ - return m_project.data(); -} - -/** - @brief CableListModel::setFields - Set which columns this table shows, in that order, and work the rows - out along the way. Unknown keys are dropped rather than shown as - empty columns: a file naming a column this program does not know - would draw a blank header nobody can fill. - @param fields the keys of CableList::allColumns() -*/ -void CableListModel::setFields(const QStringList &fields) -{ - QStringList kept; - for (const QString &field : fields) - { - if (kept.contains(field)) { - continue; - } - if (CableList::labelOf(field).isEmpty()) { - continue; - } - kept << field; - } - - beginResetModel(); - m_fields = kept; - //The columns themselves change, their labels are written again - //below -- but HOW a header is drawn (font, alignment, margins) - //belongs to the table, not to the column set, and stays as it - //is across a column edit. Sections which no longer exist are - //dropped, so a stale header of an old column cannot linger - for (auto it = m_header_data.begin(); it != m_header_data.end(); ) { - if (it.key() >= m_fields.count()) { - it = m_header_data.erase(it); - } else { - ++it; - } - } - for (int i = 0; i < m_fields.count(); ++i) { - m_header_data[i].insert(Qt::DisplayRole, CableList::labelOf(m_fields.at(i))); - } - m_rows = m_project ? CableList::rows(m_project) - : QVector>(); - endResetModel(); -} - -/** - @brief CableListModel::queryString - @return the fields joined the way the project file stores them -*/ -QString CableListModel::queryString() const -{ - return m_fields.join(QLatin1Char(',')); -} - -/** - @brief CableListModel::refresh - Work the rows out again, and watch everything they are worked out - from: a cable of the project, a folio, a piece a cable lands on - (its label is the BMK of an end, its Installation and Localisation - are what those two columns of an end read). -*/ -void CableListModel::refresh() -{ - for (const QMetaObject::Connection &connection : std::as_const(m_watchers)) { - QObject::disconnect(connection); - } - m_watchers.clear(); - - if (m_project) - { - const QVector cables = m_project->cables(); - for (Cable *cable : cables) { - m_watchers << connect(cable, &Cable::changed, - this, &CableListModel::refresh); - } - - const QList diagrams = m_project->diagrams(); - for (Diagram *diagram : diagrams) - { - m_watchers << connect(diagram, &Diagram::diagramInformationChanged, - this, &CableListModel::refresh); - const QList elements = diagram->elements(); - for (Element *element : elements) { - m_watchers << connect(element, &Element::elementInfoChange, - this, &CableListModel::refresh); - } - } - } - - beginResetModel(); - m_rows = m_project ? CableList::rows(m_project) - : QVector>(); - endResetModel(); -} - -/** - @brief CableListModel::connectProject - Watch the project itself: cables and folios coming and going reach - this model even though no refresh of ours was running to catch them - (the rows of a project being read from its file are filled cable by - cable, one signal at a time). -*/ -void CableListModel::connectProject() -{ - if (!m_project) { - return; - } - - connect(m_project, &QETProject::cableAdded, this, &CableListModel::refresh); - connect(m_project, &QETProject::cableRemoved, this, &CableListModel::refresh); - connect(m_project, &QETProject::diagramAdded, this, &CableListModel::refresh); - connect(m_project, &QETProject::diagramRemoved, this, &CableListModel::refresh); -} - -/** - @brief CableListModel::toXml - Save the model to xml: which columns are shown, the look of the - cells and of the header -- the rows themselves are not saved, they - are worked out again from the cables whenever the project opens. - @param document - @return -*/ -QDomElement CableListModel::toXml(QDomDocument &document) const -{ - auto dom_element = document.createElement(xmlTagName()); - - //Identifier - auto dom_identifier = document.createElement("identifier"); - auto dom_identifier_text = document.createTextNode(m_identifier); - dom_identifier.appendChild(dom_identifier_text); - dom_element.appendChild(dom_identifier); - - //Fields - auto dom_fields = document.createElement("fields"); - auto dom_fields_text = document.createTextNode(queryString()); - dom_fields.appendChild(dom_fields_text); - dom_element.appendChild(dom_fields); - - //Add index 0,0 data - auto index_00 = document.createElement("index00"); - index_00.setAttribute("font", QETUtils::fontToString(m_index_0_0_data.value(Qt::FontRole).value())); - auto me = QMetaEnum::fromType(); - index_00.setAttribute("alignment", me.valueToKey(m_index_0_0_data.value(Qt::TextAlignmentRole).toInt())); - dom_element.appendChild(index_00); - index_00.setAttribute("margins", m_index_0_0_data.value(Qt::UserRole+1).toString()); - - //header data - QHash> horizontal_; - for (auto key : m_header_data.keys()) - { - //We save all data except the display role, because it is - //generated from the fields above (same reason and same sorted - //order as ProjectDBModel, so the file stays reproducible) - auto list = m_header_data.value(key).keys(); - list.removeAll(Qt::DisplayRole); - std::sort(list.begin(), list.end()); - - horizontal_.insert(key, list); - } - - dom_element.appendChild(QETXML::modelHeaderDataToXml(document, this, horizontal_, QHash>())); - - return dom_element; -} - -/** - @brief CableListModel::fromXml - Restore the model from xml - @param element -*/ -void CableListModel::fromXml(const QDomElement &element) -{ - if (element.tagName() != xmlTagName()) - return; - - setIdentifier(element.firstChildElement("identifier").text()); - setFields(element.firstChildElement("fields").text() - .split(QLatin1Char(','), Qt::SkipEmptyParts)); - - //Index 0,0 - auto index_00 = element.firstChildElement("index00"); - QFont font_; - QETUtils::fontFromString(font_, index_00.attribute("font")); - m_index_0_0_data.insert(Qt::FontRole, font_); - auto me = QMetaEnum::fromType(); - const int alignment = me.keyToValue(index_00.attribute("alignment") - .toStdString().data()); - //A file written without an alignment (an empty or unknown enum - //key) would restore as -1, whose bits draw every cell right- - //aligned -- left is the default instead - m_index_0_0_data.insert(Qt::TextAlignmentRole, - alignment > 0 ? alignment : int(Qt::AlignLeft)); - //A file saved without cell margins (one written before the - //look of an empty table was kept) restores with the same - //default the creation dialog offers, instead of text drawn - //against the cell lines - const QString margins_ = index_00.attribute("margins"); - m_index_0_0_data.insert(Qt::UserRole+1, - margins_.isEmpty() - ? QETUtils::marginsToString(QMargins(5,5,10,5)) - : margins_); - - QETXML::modelHeaderDataFromXml(element.firstChildElement("header_data"), this); -} diff --git a/sources/cablelist/cablelistmodel.h b/sources/cablelist/cablelistmodel.h deleted file mode 100644 index 376c790a4..000000000 --- a/sources/cablelist/cablelistmodel.h +++ /dev/null @@ -1,107 +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 3 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 CABLELISTMODEL_H -#define CABLELISTMODEL_H - -#include -#include -#include -#include -#include -#include -#include -#include - -class Cable; -class Diagram; -class Element; -class QETProject; - -/** - @brief The CableListModel class - - The rows of a cable list, shown by QetGraphicsTableItem the way the - project database model shows a nomenclature -- and shaped after it - on purpose: same roles, same header behaviour, same way of saving - itself to the project file, so that the table on the sheet treats - this model exactly like any other. - - Whereas ProjectDBModel asks the project database, this model asks - CableList::rows(), straight from the cables of the project. It - refreshes itself whenever anything those rows are worked out from - changes: a cable edited, a cable added or removed, a folio added, - removed or re-informed (its title block carries the installation and - the place), a label typed over on a piece a cable lands on. -*/ -class CableListModel : public QAbstractTableModel -{ - Q_OBJECT - - public: - explicit CableListModel(QETProject *project, QObject *parent = nullptr); - CableListModel(const CableListModel &other_model); - - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - int columnCount(const QModelIndex &parent = QModelIndex()) const override; - bool setHeaderData(int section, - Qt::Orientation orientation, - const QVariant &value, - int role = Qt::EditRole) override; - QVariant headerData(int section, - Qt::Orientation orientation, - int role = Qt::DisplayRole) const override; - bool setData(const QModelIndex &index, - const QVariant &value, - int role = Qt::EditRole) override; - QVariant data(const QModelIndex &index, - int role = Qt::DisplayRole) const override; - - /// The columns this table shows, in order: keys of CableList - void setFields(const QStringList &fields); - QStringList fields() const {return m_fields;} - /// The same, joined the way the project file stores it - QString queryString() const; - void setIdentifier(const QString &identifier) {m_identifier = identifier;} - QString identifier() const {return m_identifier;} - QETProject *project() const; - - QDomElement toXml(QDomDocument &document) const; - void fromXml(const QDomElement &element); - static QString xmlTagName() {return QStringLiteral("cable_list_model");} - - public slots: - /// Work the rows out again and tell whoever shows them - void refresh(); - - private: - /// Watch everything the rows are worked out from - void connectProject(); - - QPointer m_project; - QStringList m_fields; - QVector> m_rows; - //First int = section, second int = Qt::role, QVariant = value - QHash> m_header_data; - QHash m_index_0_0_data; - QString m_identifier = QStringLiteral("cable_list"); - /// The current watchers of this model, dropped and taken - /// again on every refresh - QList m_watchers; -}; - -#endif // CABLELISTMODEL_H diff --git a/sources/cablelist/cablelistmodelpropertieswidget.cpp b/sources/cablelist/cablelistmodelpropertieswidget.cpp deleted file mode 100644 index 990e85b08..000000000 --- a/sources/cablelist/cablelistmodelpropertieswidget.cpp +++ /dev/null @@ -1,100 +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 3 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 "cablelistmodelpropertieswidget.h" - -#include "cablelistmodel.h" -#include "cablequerywidget.h" - -#include -#include -#include -#include -#include - -/** - @brief CableListModelPropertiesWidget::CableListModelPropertiesWidget - @param model - @param parent -*/ -CableListModelPropertiesWidget::CableListModelPropertiesWidget( - CableListModel *model, - QWidget *parent) : - PropertiesEditorWidget(parent) -{ - auto *layout = new QHBoxLayout(this); - m_edit_pb = new QPushButton(tr("Edit columns..."), this); - m_refresh_pb = new QPushButton(tr("Refresh"), this); - layout->addWidget(m_edit_pb); - layout->addWidget(m_refresh_pb); - layout->addStretch(); - - connect(m_edit_pb, &QPushButton::clicked, this, &CableListModelPropertiesWidget::editColumns); - connect(m_refresh_pb, &QPushButton::clicked, this, &CableListModelPropertiesWidget::refreshRows); - - setModel(model); -} - -/** - @brief CableListModelPropertiesWidget::setModel - @param model -*/ -void CableListModelPropertiesWidget::setModel(CableListModel *model) -{ - m_model = model; - m_edit_pb->setEnabled(m_model != nullptr); - m_refresh_pb->setEnabled(m_model != nullptr); -} - -/** - @brief CableListModelPropertiesWidget::editColumns - Ask for the columns again and put them on the table right away -- - the same immediate, not-undoable way the database table changes its - query -*/ -void CableListModelPropertiesWidget::editColumns() -{ - if (!m_model) { - return; - } - - QDialog d(this); - d.setWindowTitle(tr("Cable list columns")); - auto *layout = new QVBoxLayout(&d); - auto *widget = new CableQueryWidget(&d); - widget->setFields(m_model->fields()); - layout->addWidget(widget); - - auto *button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - layout->addWidget(button_box); - connect(button_box, &QDialogButtonBox::accepted, &d, &QDialog::accept); - connect(button_box, &QDialogButtonBox::rejected, &d, &QDialog::reject); - - if (d.exec()) { - m_model->setFields(widget->selectedKeys()); - } -} - -/** - @brief CableListModelPropertiesWidget::refreshRows -*/ -void CableListModelPropertiesWidget::refreshRows() -{ - if (m_model) { - m_model->refresh(); - } -} diff --git a/sources/cablelist/cablelistmodelpropertieswidget.h b/sources/cablelist/cablelistmodelpropertieswidget.h deleted file mode 100644 index 908a8b178..000000000 --- a/sources/cablelist/cablelistmodelpropertieswidget.h +++ /dev/null @@ -1,55 +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 3 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 CABLELISTMODELPROPERTIESWIDGET_H -#define CABLELISTMODELPROPERTIESWIDGET_H - -#include "../../PropertiesEditor/propertieseditorwidget.h" - -class CableListModel; -class QPushButton; - -/** - @brief The CableListModelPropertiesWidget class - The editor shown when a cable list table is selected: change which - columns the table shows, or work its rows out again. Shaped after - ProjectDBModelPropertiesWidget, which changes its query the same - immediate, not-undoable way -- the table itself is undoable to move - or delete, its content always follows the cables. -*/ -class CableListModelPropertiesWidget : public PropertiesEditorWidget -{ - Q_OBJECT - - public: - explicit CableListModelPropertiesWidget(CableListModel *model = nullptr, - QWidget *parent = nullptr); - ~CableListModelPropertiesWidget() override = default; - - void setModel(CableListModel *model); - - private slots: - void editColumns(); - void refreshRows(); - - private: - CableListModel *m_model = nullptr; - QPushButton *m_edit_pb = nullptr; - QPushButton *m_refresh_pb = nullptr; -}; - -#endif // CABLELISTMODELPROPERTIESWIDGET_H diff --git a/sources/cablelist/cablelistrows.cpp b/sources/cablelist/cablelistrows.cpp deleted file mode 100644 index 34df4998e..000000000 --- a/sources/cablelist/cablelistrows.cpp +++ /dev/null @@ -1,468 +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 3 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 "cablelistrows.h" - -#include "../bomexport.h" -#include "../cable/cable.h" -#include "../conductorproperties.h" -#include "../diagram.h" -#include "../qetgraphicsitem/conductor.h" -#include "../qetgraphicsitem/element.h" -#include "../qetgraphicsitem/terminal.h" -#include "../qetinformation.h" -#include "../qetproject.h" - -#include -#include - -#include -#include -#include - -namespace { - -/** - @brief One place where the line of a cable ends. - - An end which lands on a piece carries that piece (its uuid for - grouping and its pointer for reading the label, both valid as long - as the plan lives); an end taken from the drawn line carries none, - and its tie names the section instead. -*/ -struct CableEnd -{ - Diagram *diagram = nullptr; - QPointF scene; - Element *element = nullptr; - QUuid element_uuid; - QString tie; - - bool isLanding() const {return !element_uuid.isNull();} -}; - -/** - @brief endBefore - The order the ends of the cables are read in: the earlier sheet - first, then the higher point of that sheet, then the left one, then - a stable name so that a redraw can never swap two ends and shuffle - the rows around. - @param a first end - @param b second end - @param project the project the sheet numbers come from - @return true when a comes before b -*/ -bool endBefore(const CableEnd &a, const CableEnd &b, const QETProject *project) -{ - const int folio_a = a.diagram ? project->folioIndex(a.diagram) : INT_MAX; - const int folio_b = b.diagram ? project->folioIndex(b.diagram) : INT_MAX; - if (folio_a != folio_b) { - return folio_a < folio_b; - } - if (a.scene.y() != b.scene.y()) { - return a.scene.y() < b.scene.y(); - } - if (a.scene.x() != b.scene.x()) { - return a.scene.x() < b.scene.x(); - } - return a.tie < b.tie; -} - -/** - @brief folioOf - @param end - @return the sheet number the end lies on, empty when it lies on no - sheet at all (a cable nothing was ever drawn for) -*/ -QString folioOf(const CableEnd &end, const QETProject *project) -{ - return end.diagram ? QString::number(project->folioIndex(end.diagram) + 1) - : QString(); -} - -/** - @brief plantOf - @return the Installation the piece the end lands on carries -- the - same field every other list reads for a piece. A piece without one - leaves the cell empty: there is no second place to look for it, and - an end of the drawn line, which lands on no piece at all, fills - nothing either. -*/ -QString plantOf(const CableEnd &end) -{ - if (!end.element) { - return QString(); - } - return end.element->elementInformations() - .value(QETInformation::ELMT_PLANT).toString(); -} - -/** - @brief locmachOf - @return the Localisation the piece the end lands on carries, with - the same rule as the Installation: what the piece carries or - nothing at all. -*/ -QString locmachOf(const CableEnd &end) -{ - if (!end.element) { - return QString(); - } - return end.element->elementInformations() - .value(QETInformation::ELMT_LOCATION).toString(); -} - -/** - @brief labelOfEnd - @return the label (the BMK) of the piece the end lands on, empty for - an end of the drawn line -- there is no piece there to carry one -*/ -QString labelOfEnd(const CableEnd &end) -{ - if (!end.element) { - return QString(); - } - return end.element->elementInformations() - .value(QETInformation::ELMT_LABEL).toString(); -} - -} // namespace - -/** - @brief CableList::allColumns - @return every column of the cable list, in the order they are - offered, drawn and exported -*/ -const QList &CableList::allColumns() -{ - static const QList columns = { - {QStringLiteral("nr"), tr("No.")}, - {QStringLiteral("installation_cable"), tr("Plant (cable)")}, - {QStringLiteral("location_cable"), tr("Location (cable)")}, - {QStringLiteral("bmk_cable"), tr("Label (cable)")}, - {QStringLiteral("installation_start"), tr("Plant (start)")}, - {QStringLiteral("location_start"), tr("Location (start)")}, - {QStringLiteral("bmk_start"), tr("Label (start)")}, - {QStringLiteral("folio_start"), tr("Sheet (start)")}, - {QStringLiteral("installation_end"), tr("Plant (end)")}, - {QStringLiteral("location_end"), tr("Location (end)")}, - {QStringLiteral("bmk_end"), tr("Label (end)")}, - {QStringLiteral("folio_end"), tr("Sheet (end)")}, - {QStringLiteral("type"), tr("Cable type")}, - {QStringLiteral("length"), tr("Length")}, - {QStringLiteral("cores_used"), tr("Used cores")}, - }; - return columns; -} - -/** - @brief CableList::defaultKeys - @return the columns a freshly drawn cable list starts with: all of - them -*/ -const QStringList &CableList::defaultKeys() -{ - static const QStringList keys = []() { - QStringList list; - for (const Column &column : allColumns()) { - list << column.key; - } - return list; - }(); - return keys; -} - -/** - @brief CableList::labelOf - @param key - @return the header text of that column, empty when the key names no - column of this list -*/ -QString CableList::labelOf(const QString &key) -{ - for (const Column &column : allColumns()) { - if (column.key == key) { - return column.label; - } - } - return QString(); -} - -/** - @brief CableList::rows - Work out one row per cable, in plan order. - @param project the project whose cables are listed - @return the rows, sorted, the number column already filled in -*/ -QVector> CableList::rows(QETProject *project) -{ - QVector> result; - if (!project) { - return result; - } - - //Every conductor of every cable is walked once, and where one - //lands on a real piece becomes an arrival of that cable. A core - //which ends on a folio report is skipped on that side: the - //cable goes on over there, and the landing on the far sheet is - //what counts as its end. - QHash> arrivals; - for (Diagram *diagram : project->diagrams()) - { - for (Conductor *conductor : diagram->conductors()) - { - const ConductorProperties properties = conductor->properties(); - if (properties.m_cable_uuid.isNull()) { - continue; - } - if (!conductor->terminal1 || !conductor->terminal2) { - continue; - } - - Terminal *terminals[2] = {conductor->terminal1, conductor->terminal2}; - for (Terminal *terminal : terminals) - { - Element *element = terminal->parentElement(); - if (!element) { - //a free end is not an arrival - continue; - } - if (element->linkType() & Element::AllReport) { - //the cable continues on the linked sheet - continue; - } - - CableEnd end; - end.diagram = element->diagram(); - end.scene = terminal->scenePos(); - end.element = element; - end.element_uuid = element->uuid(); - end.tie = conductor->uuid().toString(); - arrivals[properties.m_cable_uuid].append(end); - } - } - } - - struct Entry - { - Cable *cable = nullptr; - CableEnd start; - CableEnd target; - }; - - auto byPlan = [project](const CableEnd &a, const CableEnd &b) { - return endBefore(a, b, project); - }; - - QVector entries; - entries.reserve(project->cables().count()); - - for (Cable *cable : project->cables()) - { - //Several cores landing on one piece are one end: the highest - //of their landings speaks for it. - QHash grouped; - for (const CableEnd &end : arrivals.value(cable->uuid())) - { - auto it = grouped.find(end.element_uuid); - if (it == grouped.end() || byPlan(end, it.value())) { - grouped.insert(end.element_uuid, end); - } - } - QList landings = grouped.values(); - std::sort(landings.begin(), landings.end(), byPlan); - - //The free ends of the drawn line, for the sides no core ever - //landed on - QList line_ends; - for (const CablePartData &part : cable->parts()) - { - Diagram *diagram = project->diagramByUuid(part.diagram); - if (!diagram) { - continue; - } - for (int i = 0; i < 2; ++i) - { - CableEnd end; - end.diagram = diagram; - end.scene = i ? part.p2 : part.p1; - end.tie = part.uuid.toString() + (i ? QStringLiteral("2") - : QStringLiteral("1")); - line_ends.append(end); - } - } - std::sort(line_ends.begin(), line_ends.end(), byPlan); - - Entry entry; - entry.cable = cable; - - if (landings.size() >= 2) - { - //The rule as asked for: the first finding counts for - //both ends. First landing in plan order is the start, - //the very next finding is the target -- not the last - //one. A sheet further along finds the same cable again - //but start and target are already taken by then, so it - //adds nothing: on the first sheet, the upper piece is - //the start and the lower one the target. - entry.start = landings.first(); - entry.target = landings.at(1); - } - else if (landings.size() == 1) - { - //One side is wired: that side carries the label, the - //other side falls back to the drawn line, farthest end - //first so a tie goes to the later point of the plan. - entry.start = landings.first(); - entry.target = landings.first(); - qreal farthest = -1.0; - for (const CableEnd &candidate : std::as_const(line_ends)) - { - const qreal distance = QLineF(entry.start.scene, candidate.scene).length(); - if (distance >= farthest) { - farthest = distance; - entry.target = candidate; - } - } - } - else if (line_ends.size() >= 2) - { - entry.start = line_ends.first(); - entry.target = line_ends.last(); - } - else if (line_ends.size() == 1) - { - entry.start = line_ends.first(); - entry.target = line_ends.first(); - } - //A cable with neither a core nor a line draws no ends at all; - //its own fields still fill the row. - - entries.append(entry); - } - - //Plan order again for the rows themselves: by the sheet the start - //lies on, then from the top of that sheet down. The name and the - //uuid only ever break ties, so the numbering is stable between - //two openings of the same project. - std::sort(entries.begin(), entries.end(), [project](const Entry &a, const Entry &b) { - if (endBefore(a.start, b.start, project)) return true; - if (endBefore(b.start, a.start, project)) return false; - if (endBefore(a.target, b.target, project)) return true; - if (endBefore(b.target, a.target, project)) return false; - if (a.cable->designation() != b.cable->designation()) { - return a.cable->designation() < b.cable->designation(); - } - return a.cable->uuid().toString() < b.cable->uuid().toString(); - }); - - int number = 0; - for (const Entry &entry : std::as_const(entries)) - { - Cable *cable = entry.cable; - QMap row; - row.insert(QStringLiteral("nr"), QString::number(++number)); - row.insert(QStringLiteral("installation_cable"), cable->installation()); - row.insert(QStringLiteral("location_cable"), cable->location()); - row.insert(QStringLiteral("bmk_cable"), cable->designation()); - row.insert(QStringLiteral("installation_start"), plantOf(entry.start)); - row.insert(QStringLiteral("location_start"), locmachOf(entry.start)); - row.insert(QStringLiteral("bmk_start"), labelOfEnd(entry.start)); - row.insert(QStringLiteral("folio_start"), folioOf(entry.start, project)); - row.insert(QStringLiteral("installation_end"), plantOf(entry.target)); - row.insert(QStringLiteral("location_end"), locmachOf(entry.target)); - row.insert(QStringLiteral("bmk_end"), labelOfEnd(entry.target)); - row.insert(QStringLiteral("folio_end"), folioOf(entry.target, project)); - row.insert(QStringLiteral("type"), cable->type()); - row.insert(QStringLiteral("length"), cable->length()); - row.insert(QStringLiteral("cores_used"), - QStringLiteral("%1/%2") - .arg(cable->usedCoreCount()) - .arg(cable->coreCount())); - result.append(row); - } - - return result; -} - -/** - @brief CableList::toCsv - The whole list as CSV bytes: every column of the list, every cable - of the project, in the format every CSV export of this program - writes. - @param project the project whose cables are listed - @return the bytes to write -*/ -/** - @brief CableList::toCsv - @param project the project whose cables are listed - @param keys the columns to write, in that order; unknown keys are - dropped, and a list without a single usable one falls back to the - default columns rather than writing a blank file - @param include_headers whether the line of column names is written - as the first line - @return the CSV bytes in the format every CSV export of this - program writes (UTF-8 BOM, ';', every field quoted) -*/ -QByteArray CableList::toCsv(QETProject *project, const QStringList &keys, - bool include_headers) -{ - QStringList kept; - for (const QString &key : keys) - { - if (kept.contains(key) || labelOf(key).isEmpty()) { - continue; - } - kept << key; - } - if (kept.isEmpty()) { - kept = defaultKeys(); - } - - QByteArray csv("\xEF\xBB\xBF"); - if (include_headers) { - QStringList headers; - for (const QString &key : std::as_const(kept)) { - headers << labelOf(key); - } - csv += BomExport::csvRecord(headers); - } - - const QVector> rows_ = rows(project); - for (const QMap &row : rows_) - { - QStringList values; - for (const QString &key : std::as_const(kept)) { - values << row.value(key); - } - csv += BomExport::csvRecord(values); - } - - return csv; -} - -/** - @brief CableList::toCsv - The whole list as it has always been written: every column, every - cable, the line of column names included - @param project the project whose cables are listed - @return the CSV bytes -*/ -QByteArray CableList::toCsv(QETProject *project) -{ - return toCsv(project, defaultKeys(), true); -} diff --git a/sources/cablelist/cablelistrows.h b/sources/cablelist/cablelistrows.h deleted file mode 100644 index 4f3f80988..000000000 --- a/sources/cablelist/cablelistrows.h +++ /dev/null @@ -1,101 +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 3 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 CABLELISTROWS_H -#define CABLELISTROWS_H - -#include -#include -#include -#include -#include -#include - -class QETProject; - -/** - @brief The CableList class - - The columns and the rows of the cable list (menu "Listes": the entry - which puts such a table on a sheet, and the CSV export which writes - the same table to a file). - - Everything which decides what a cable list row says lives here, so - that the table drawn on the sheet and the exported CSV can never - disagree: one row per cable, worked out once and read twice. - - The two ends of a cable are worked out the way the user asked for - it: - - - wherever the cores of the cable land on a real piece (a terminal - strip, a device) is an end. Several cores landing on the same - piece are one end. A core which ends on a folio report is not an - end at all -- the cable goes on over there, and the landing on the - far sheet is the end instead, which is what makes a cable running - over several sheets show its start on the earlier sheet and its - target on the later one; - - ends are ordered by sheet first (the earlier sheet wins), then by - height in the plan (the higher one wins), then left to right; - the first end is the start, the last one the target; - - a cable whose cores never landed anywhere (not drawn yet, or only - wired in the middle) falls back to the two free ends of its drawn - line; when there is only one landing, the other side falls back to - the line end farthest away from it. -*/ -class CableList -{ - Q_DECLARE_TR_FUNCTIONS(CableList) - - public: - struct Column - { - /// The name the row hash, the saved file and the CSV use - QString key; - /// What the header over the column says - QString label; - }; - - /// Every column the list can offer, in the order they are - /// offered and exported - static const QList &allColumns(); - /// The columns a fresh list starts with: all of them but the - /// ones marked optional above - static const QStringList &defaultKeys(); - /// The header text of one column, empty when the key is not - /// a column of this list - static QString labelOf(const QString &key); - - /// One row per cable, sorted: first by the sheet its start - /// lies on, then from the top of that sheet down, the number - /// column filled in along the way - static QVector> rows(QETProject *project); - - /// The whole list as CSV bytes, in the format every CSV - /// export of this program writes (UTF-8 BOM, ';', every - /// field quoted): every column, every cable, the line of - /// column names included - static QByteArray toCsv(QETProject *project); - /// The same, but only the given columns, in the given - /// order, and only with the line of column names when asked - /// for -- what the export dialog writes. Unknown keys are - /// dropped, and no usable key at all falls back to the - /// default columns - static QByteArray toCsv(QETProject *project, const QStringList &keys, - bool include_headers); -}; - -#endif // CABLELISTROWS_H diff --git a/sources/cablelist/cablequerywidget.cpp b/sources/cablelist/cablequerywidget.cpp deleted file mode 100644 index 4b32dc71f..000000000 --- a/sources/cablelist/cablequerywidget.cpp +++ /dev/null @@ -1,319 +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 3 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 "cablequerywidget.h" - -#include "cablelistrows.h" - -#include "../qetapp.h" -#include "../ui/configsaveloaderwidget.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/** - @brief CableQueryWidget::CableQueryWidget - @param parent -*/ -CableQueryWidget::CableQueryWidget(QWidget *parent) : - QWidget(parent) -{ - auto *layout = new QVBoxLayout(this); - - auto *lists = new QHBoxLayout; - - auto *left = new QVBoxLayout; - left->addWidget(new QLabel(tr("Available columns"), this)); - m_available_list = new QListWidget(this); - left->addWidget(m_available_list); - - auto *buttons = new QVBoxLayout; - buttons->addStretch(); - auto make_button = [this](QStyle::StandardPixmap icon, const QString &tip) { - auto *button = new QPushButton(this); - button->setIcon(style()->standardIcon(icon)); - button->setToolTip(tip); - return button; - }; - auto *add_pb = make_button(QStyle::SP_ArrowRight, tr("Add the selected column")); - auto *remove_pb = make_button(QStyle::SP_ArrowLeft, tr("Remove the selected column")); - auto *up_pb = make_button(QStyle::SP_ArrowUp, tr("Move the selected column up")); - auto *down_pb = make_button(QStyle::SP_ArrowDown, tr("Move the selected column down")); - buttons->addWidget(add_pb); - buttons->addWidget(remove_pb); - buttons->addWidget(up_pb); - buttons->addWidget(down_pb); - buttons->addStretch(); - - auto *right = new QVBoxLayout; - right->addWidget(new QLabel(tr("Table columns"), this)); - m_choosen_list = new QListWidget(this); - right->addWidget(m_choosen_list); - - lists->addLayout(left); - lists->addLayout(buttons); - lists->addLayout(right); - layout->addLayout(lists); - - m_config_gb = new ConfigSaveLoaderWidget(this); - layout->addWidget(m_config_gb); - - setMinimumSize(QSize(520, 420)); - - setUpItems(); - fillSavedQuery(); - - connect(m_config_gb, &ConfigSaveLoaderWidget::saveClicked, this, &CableQueryWidget::saveConfig); - connect(m_config_gb, &ConfigSaveLoaderWidget::loadClicked, this, &CableQueryWidget::loadConfig); - - connect(add_pb, &QPushButton::clicked, this, &CableQueryWidget::addField); - connect(remove_pb, &QPushButton::clicked, this, &CableQueryWidget::removeField); - connect(up_pb, &QPushButton::clicked, this, &CableQueryWidget::moveFieldUp); - connect(down_pb, &QPushButton::clicked, this, &CableQueryWidget::moveFieldDown); - - connect(m_available_list, &QListWidget::itemDoubleClicked, - this, &CableQueryWidget::on_m_available_list_itemDoubleClicked); - connect(m_choosen_list, &QListWidget::itemDoubleClicked, - this, &CableQueryWidget::on_m_choosen_list_itemDoubleClicked); -} - -/** - @brief CableQueryWidget::~CableQueryWidget -*/ -CableQueryWidget::~CableQueryWidget() -{ -} - -/** - @brief CableQueryWidget::selectedKeys - @return the chosen columns, in order -*/ -QStringList CableQueryWidget::selectedKeys() const -{ - QStringList keys; - int row = 0; - while (auto *item = m_choosen_list->item(row)) - { - keys.append(item->data(Qt::UserRole).toString()); - ++row; - } - - return keys; -} - -/** - @brief CableQueryWidget::setFields - Put exactly these columns in the right list, in that order. Keys - this list does not know are ignored, which is what keeps a file - coming from a newer program readable here. - @param fields -*/ -void CableQueryWidget::setFields(const QStringList &fields) -{ - //Ugly hack to force to remove all chosen columns, the same way - //SummaryQueryWidget::reset() does it - while (auto *item = m_choosen_list->takeItem(0)) { - m_available_list->addItem(item); - } - - for (const QString &key : fields) - { - for (int row = 0; row < m_available_list->count(); ++row) - { - QListWidgetItem *item = m_available_list->item(row); - if (item->data(Qt::UserRole).toString() == key) { - m_available_list->takeItem(row); - m_choosen_list->addItem(item); - break; - } - } - } -} - -/** - @brief CableQueryWidget::setUpItems - Fill the available list with every column the list offers and - leave them there: a fresh dialog opens with its entries on the - left, the same way the material list dialog does. What a table or - an export starts with when nothing has been picked is the default - set of columns, applied where the table or the file is made. -*/ -void CableQueryWidget::setUpItems() -{ - for (const CableList::Column &column : CableList::allColumns()) - { - auto *item = new QListWidgetItem(column.label, m_available_list); - item->setData(Qt::UserRole, column.key); - } -} - -/** - @brief CableQueryWidget::fillSavedQuery - Fill the combo box of the saved configurations -*/ -void CableQueryWidget::fillSavedQuery() -{ - QFile file(QETApp::configDir() % QStringLiteral("/cable_list.json")); - if (file.open(QFile::ReadOnly)) - { - QJsonDocument jsd(QJsonDocument::fromJson(file.readAll())); - QJsonObject jso = jsd.object(); - - for (auto it = jso.begin() ; it != jso.end() ; ++it) { - m_config_gb->addItem(it.key()); - } - } -} - -/** - @brief CableQueryWidget::on_m_available_list_itemDoubleClicked - @param item -*/ -void CableQueryWidget::on_m_available_list_itemDoubleClicked(QListWidgetItem *item) -{ - Q_UNUSED(item) - addField(); -} - -/** - @brief CableQueryWidget::on_m_choosen_list_itemDoubleClicked - @param item -*/ -void CableQueryWidget::on_m_choosen_list_itemDoubleClicked(QListWidgetItem *item) -{ - Q_UNUSED(item) - removeField(); -} - -/** - @brief CableQueryWidget::addField -*/ -void CableQueryWidget::addField() -{ - if (auto *item = m_available_list->takeItem(m_available_list->currentRow())) { - m_choosen_list->addItem(item); - } -} - -/** - @brief CableQueryWidget::removeField -*/ -void CableQueryWidget::removeField() -{ - if (auto *item = m_choosen_list->takeItem(m_choosen_list->currentRow())) { - m_available_list->addItem(item); - } -} - -/** - @brief CableQueryWidget::moveFieldUp -*/ -void CableQueryWidget::moveFieldUp() -{ - auto row = m_choosen_list->currentRow(); - if (row <= 0) { - return; - } - - auto *item = m_choosen_list->takeItem(row); - m_choosen_list->insertItem(row - 1, item); - m_choosen_list->setCurrentItem(item); -} - -/** - @brief CableQueryWidget::moveFieldDown -*/ -void CableQueryWidget::moveFieldDown() -{ - auto row = m_choosen_list->currentRow(); - if (row == -1) { - return; - } - - auto *item = m_choosen_list->takeItem(row); - m_choosen_list->insertItem(row + 1, item); - m_choosen_list->setCurrentItem(item); -} - -/** - @brief CableQueryWidget::saveConfig -*/ -void CableQueryWidget::saveConfig() -{ - QFile file_(QETApp::configDir() % QStringLiteral("/cable_list.json")); - - if (file_.open(QFile::ReadWrite)) - { - QJsonDocument doc_(QJsonDocument::fromJson(file_.readAll())); - QJsonObject root_object; - - if (!doc_.isEmpty()) - { - root_object = doc_.object(); - if (root_object.contains(m_config_gb->text())) { - root_object.remove(m_config_gb->text()); - } - } - - QJsonObject object_; - object_.insert(QStringLiteral("fields"), selectedKeys().join(QLatin1Char(','))); - root_object[m_config_gb->text()] = object_; - - doc_.setObject(root_object); - file_.resize(0); - file_.write(doc_.toJson()); - } -} - -/** - @brief CableQueryWidget::loadConfig -*/ -void CableQueryWidget::loadConfig() -{ - auto name = m_config_gb->selectedText(); - if (name.isEmpty()) { - return; - } - - QFile file_(QETApp::configDir() % QStringLiteral("/cable_list.json")); - if (!file_.open(QFile::ReadOnly)) { - return; - } - - QJsonDocument doc_(QJsonDocument::fromJson(file_.readAll())); - QJsonObject object_ = doc_.object(); - - auto value = object_.value(name); - if (!value.isObject()) { - return; - } - - auto fields = value.toObject().value(QStringLiteral("fields")); - if (fields.isString()) { - setFields(fields.toString().split(QLatin1Char(','), Qt::SkipEmptyParts)); - } -} diff --git a/sources/cablelist/cablequerywidget.h b/sources/cablelist/cablequerywidget.h deleted file mode 100644 index d1d4d7738..000000000 --- a/sources/cablelist/cablequerywidget.h +++ /dev/null @@ -1,71 +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 3 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 CABLEQUERYWIDGET_H -#define CABLEQUERYWIDGET_H - -#include - -class ConfigSaveLoaderWidget; -class QListWidget; -class QListWidgetItem; -class QPushButton; - -/** - @brief The CableQueryWidget class - The content tab of the dialog which puts a cable list on a sheet: - which columns the list shows, and in which order. Shaped after - SummaryQueryWidget -- two lists with arrows between them, a saved - configuration per name -- but the columns are chosen out of the - fixed set of CableList instead of out of a SQL query, so there is no - query line to edit. -*/ -class CableQueryWidget : public QWidget -{ - Q_OBJECT - - public: - explicit CableQueryWidget(QWidget *parent = nullptr); - ~CableQueryWidget(); - - static QString modelIdentifier() {return "cable_list";} - /// The chosen columns, in order - QStringList selectedKeys() const; - /// Put exactly these columns in the right list, in that - /// order; keys this list does not know are ignored - void setFields(const QStringList &fields); - - private slots: - void on_m_available_list_itemDoubleClicked(QListWidgetItem *item); - void on_m_choosen_list_itemDoubleClicked(QListWidgetItem *item); - void addField(); - void removeField(); - void moveFieldUp(); - void moveFieldDown(); - void saveConfig(); - void loadConfig(); - - private: - void setUpItems(); - void fillSavedQuery(); - - QListWidget *m_available_list = nullptr; - QListWidget *m_choosen_list = nullptr; - ConfigSaveLoaderWidget *m_config_gb = nullptr; -}; - -#endif // CABLEQUERYWIDGET_H diff --git a/sources/cablelist/cabletypelist.cpp b/sources/cablelist/cabletypelist.cpp deleted file mode 100644 index 4e8250341..000000000 --- a/sources/cablelist/cabletypelist.cpp +++ /dev/null @@ -1,824 +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 "cabletypelist.h" - -#include "../materiallist/materiallist.h" -#include "../qetinformation.h" - -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace { - -/** - @brief The columns of a cable type file -- the whole catalogue, and - the only ones the program reads anything from. - - Three of them: the name of the type, how many cores it has and the - colour of each of them. In that order they are written into a brand - new file, and a label line is matched against them so that a - catalogue read back never depends on the wording of its first line. - - The material file the pattern was copied from holds more columns - (section, sheath colour, manufacturer and so on); they were never - read here and are not listed. A file which has them anyway loses - nothing: a header cell outside this list keeps its own name and its - own data, it carries no meaning, and the machine header line is - recognized by its shape rather than by this list, so an extra - column there never turns that line into a record of cables. -*/ -const QStringList &canonicalColumns() -{ - static const QStringList columns = { - QStringLiteral("designation"), - QStringLiteral("cores"), - QStringLiteral("core_colors") - }; - return columns; -} - -/** - @brief Tell whether a cell of the machine header can be a column key. - - A key is one plain lowercase word -- core_colors and - manufacturer_reference are typical -- written the same whichever - language the file is read in, which is exactly why it can sit under - the label line. A key the program doesn't know is still a key : its - column is kept and shown the way it is written, it carries no - meaning. What the shape really protects is the other side: a cable - ("H07V-K 3G1,5") never looks like this, so a line of cables is - never mistaken for a header. - @param cell - @return true when the cell can be part of the machine header line -*/ -bool isMachineKey(const QString &cell) -{ - if (cell.isEmpty()) { - return false; - } - for (const QChar c : cell) - { - const ushort u = c.unicode(); - const bool key = (u >= 'a' && u <= 'z') - || (u >= '0' && u <= '9') - || u == '_'; - if (!key) { - return false; - } - } - //A key starts with a letter, so that a row of numbers can never - //pass for one. - return cell.at(0).unicode() >= 'a' && cell.at(0).unicode() <= 'z'; -} - -/** - @brief Decode a csv file content. - The cable type file is written in utf-8 with a BOM, the same way the - material file is, so spreadsheets open it with the right encoding. A - file written by hand in the local encoding is still decodable: - fromUtf8() is only used when the bytes are valid utf-8. - @param raw - @return -*/ -QString decode(const QByteArray &raw) -{ - if (raw.startsWith("\xEF\xBB\xBF")) { - return QString::fromUtf8(raw.constData() + 3, raw.size() - 3); - } - - const QByteArray utf8 = QString::fromUtf8(raw).toUtf8(); - if (utf8 == raw) { - return QString::fromUtf8(raw); - } - return QString::fromLocal8Bit(raw); -} - -/** - @brief Tell whether a file exists and holds something. - An existing but empty file is the same as no file at all: it can be - filled with the template without losing anything. - @param path - @return -*/ -bool hasContent(const QString &path) -{ - const QFileInfo info(path); - return info.exists() && info.isFile() && info.size() > 0; -} - -} // namespace - -/** - @brief CableTypeList::settingsKey - @return the QSettings key holding the path of the cable type file -*/ -QString CableTypeList::settingsKey() -{ - return QStringLiteral("cable-management/cable-type-list-path"); -} - -/** - @brief CableTypeList::configuredPath - @return the path of the cable type file as chosen in the preferences, - or a null QString when the user never set one. -*/ -QString CableTypeList::configuredPath() -{ - QSettings settings; - const QString path = settings.value(settingsKey(), QStringLiteral("default")).toString(); - if (path.isEmpty() || path == QLatin1String("default")) { - return QString(); - } - return path; -} - -/** - @brief CableTypeList::setConfiguredPath - Store the path of the cable type file in the preferences. - @param path -*/ -void CableTypeList::setConfiguredPath(const QString &path) -{ - QSettings settings; - settings.setValue(settingsKey(), path.isEmpty() ? QStringLiteral("default") : path); -} - -/** - @brief CableTypeList::defaultFileName - @return the file name used when the cable type file is created without - an explicit location. -*/ -QString CableTypeList::defaultFileName() -{ - return QStringLiteral("qet_cable_types.csv"); -} - -/** - @brief CableTypeList::defaultPath - @return where the cable type file is created when no path is - configured yet : the standard documents folder. -*/ -QString CableTypeList::defaultPath() -{ - QString dir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); - while (dir.endsWith(QLatin1Char('/'))) { - dir.chop(1); - } - if (dir.isEmpty()) { - dir = QDir::currentPath(); - } - return dir + QLatin1Char('/') + defaultFileName(); -} - -/** - @brief CableTypeList::savedHeaderState - @return the saved column layout of the selection window, empty when the - window was never opened or the layout could not be read back. -*/ -QByteArray CableTypeList::savedHeaderState() -{ - QSettings settings; - return settings.value(QStringLiteral("cable-management/cable-type-list-header")).toByteArray(); -} - -/** - @brief CableTypeList::saveHeaderState - Remember the column layout of the selection window. - @param state the header state as saved by QHeaderView::saveState() -*/ -void CableTypeList::saveHeaderState(const QByteArray &state) -{ - QSettings settings; - settings.setValue(QStringLiteral("cable-management/cable-type-list-header"), state); -} - -/** - @brief CableTypeList::savedDialogSize - @return the size the selection window had the last time it was closed, - an invalid size when it was never opened. -*/ -QSize CableTypeList::savedDialogSize() -{ - QSettings settings; - const QSize size(settings.value(QStringLiteral("cable-management/cable-type-list-width")).toInt(), - settings.value(QStringLiteral("cable-management/cable-type-list-height")).toInt()); - //A size which was never written reads back as 0x0, which Qt still - //counts as valid : it must not be handed to resize(). - return size.isEmpty() ? QSize() : size; -} - -/** - @brief CableTypeList::saveDialogSize - Remember the size of the selection window. - @param size -*/ -void CableTypeList::saveDialogSize(const QSize &size) -{ - if (size.isEmpty()) { - return; - } - QSettings settings; - settings.setValue(QStringLiteral("cable-management/cable-type-list-width"), size.width()); - settings.setValue(QStringLiteral("cable-management/cable-type-list-height"), size.height()); -} - -/** - @brief CableTypeList::defaultColumns - @return the columns of a brand new cable type file, in writing order. -*/ -QStringList CableTypeList::defaultColumns() -{ - return canonicalColumns(); -} - -/** - @brief CableTypeList::translatedColumn - @param column a column of the cable type file - @return that column named the way it is shown to the user, in the - current language. A column QElectroTech doesn't know is returned as it - is, since it can only have been named by the user. -*/ -QString CableTypeList::translatedColumn(const QString &column) -{ - //The columns which are specific to a cable keep their own wording, - //the ones shared with the material file reuse the element - //information wording, so both catalogues read the same. The type - //column is the exception: it is called Designation here, the way - //the cable form calls it, and not "Article number", which is - //what an element means by it. - static const QMap own = { - {QStringLiteral("designation"), - QCoreApplication::translate("CableTypeList", "Designation")}, - {QStringLiteral("cores"), - QCoreApplication::translate("CableTypeList", "Number of cores")}, - {QStringLiteral("core_colors"), - QCoreApplication::translate("CableTypeList", "Core colors")}, - {QStringLiteral("section"), - QCoreApplication::translate("CableTypeList", "Cable section")}, - {QStringLiteral("color"), - QCoreApplication::translate("CableTypeList", "Sheath color")} - }; - - const auto it = own.constFind(column); - if (it != own.constEnd()) { - return it.value(); - } - - const QString translated = QETInformation::translatedInfoKey(column); - return translated.isEmpty() ? column : translated; -} - -/** - @brief CableTypeList::translatedHeader - The label line of a cable type file : the columns the way the user - reads them, in the current language. - @param columns - @return -*/ -QStringList CableTypeList::translatedHeader(const QStringList &columns) -{ - QStringList header; - header.reserve(columns.size()); - for (const QString &column : columns) { - header.append(translatedColumn(column)); - } - return header; -} - -/** - @brief CableTypeList::designation - @param record - @return the type name of that cable, the way it shows in the - selection list and on the drawing -*/ -QString CableTypeList::designation(const CableTypeRecord &record) -{ - return record.value(QStringLiteral("designation")); -} - -/** - @brief CableTypeList::coreCount - Tell how many cores a cable type holds. - @param record - @return the number of cores, or 0 when the file doesn't say. 0 means - "unknown", which the selection window treats as usable : a file which - says nothing is no reason to refuse a type. -*/ -int CableTypeList::coreCount(const CableTypeRecord &record) -{ - bool ok = false; - const int cores = record.value(QStringLiteral("cores")).trimmed().toInt(&ok); - return (ok && cores > 0) ? cores : 0; -} - -/** - @brief CableTypeList::coreColors - The colours of the cores, in the order they are written in the file, - which is the order they are given to the cores of a drawn cable. - @param record - @return - - The cell holds the colours separated by |, ; or ,. Writing always - uses | because it can't appear in a colour name and never needs - quoting, reading accepts the three so a hand written file works too. -*/ -QStringList CableTypeList::coreColors(const CableTypeRecord &record) -{ - QStringList colors; - const QString cell = record.value(QStringLiteral("core_colors")); - for (const QString &raw : cell.split(QLatin1Char('|'), Qt::SkipEmptyParts)) - { - for (const QString &part : raw.split(QLatin1Char(';'), Qt::SkipEmptyParts)) - { - for (const QString &color : part.split(QLatin1Char(','), Qt::SkipEmptyParts)) - { - const QString trimmed = color.trimmed(); - if (!trimmed.isEmpty()) { - colors.append(trimmed); - } - } - } - } - return colors; -} - -/** - @brief CableTypeList::headerAliasMap - @return every accepted header cell of a cable type file, lowercased, - mapped to the canonical column it means. - - Beside the key itself, a header may use the translated name of the - column ("Nombre d'âmes" for cores), or the name and translation of an - element information, so a catalogue sharing columns with the material - file is understood in both. -*/ -QMap CableTypeList::headerAliasMap() -{ - QMap map; - - for (const QString &key : canonicalColumns()) - { - map.insert(key.toLower(), key); - const QString translated = translatedColumn(key); - if (!translated.isEmpty()) { - map.insert(translated.toLower(), key); - } - //The element information spells the same column another way - //("Article number" for designation); both are accepted, so - //that a file written with either wording reads the same. - const QString info_key = QETInformation::translatedInfoKey(key); - if (!info_key.isEmpty() && info_key != translated) { - map.insert(info_key.toLower(), key); - } - } - - //Every element information is an acceptable column name, so that - //columns copied over from the material file keep their meaning. - const QStringList keys = QETInformation::elementInfoKeys(); - for (const QString &key : keys) - { - if (map.contains(key.toLower())) { - continue; - } - map.insert(key.toLower(), key); - const QString translated = QETInformation::translatedInfoKey(key); - if (!translated.isEmpty()) { - map.insert(translated.toLower(), key); - } - } - - //The wording older files were written with : these French names - //were the ones the label line carried before the interface text - //became English, and a catalogue written with them must keep - //reading, so each of them still means its column here. - static const QMap older_wording = { - {QStringLiteral("désignation"), QStringLiteral("designation")}, - {QStringLiteral("nombre d'âmes"), QStringLiteral("cores")}, - {QStringLiteral("couleurs des âmes"), QStringLiteral("core_colors")}, - {QStringLiteral("section du câble"), QStringLiteral("section")}, - {QStringLiteral("couleur du fourreau"), QStringLiteral("color")} - }; - for (auto it = older_wording.constBegin(); it != older_wording.constEnd(); ++it) - { - if (!map.contains(it.key())) { - map.insert(it.key(), it.value()); - } - } - - return map; -} - -/** - @brief CableTypeList::canonicalColumn - Turn one header cell into a canonical column name. - @param header_cell - @param already_used the names chosen for the previous cells, to keep - the header free of duplicates - @param alias_map lowercased alias -> canonical name - @return -*/ -QString CableTypeList::canonicalColumn(const QString &header_cell, - const QStringList &already_used, - const QMap &alias_map) -{ - const QString raw = header_cell.trimmed(); - - if (raw.isEmpty()) { - //A nameless column keeps its data and its place, it just can't be - //matched to anything. - QString name = QStringLiteral("column_%1").arg(already_used.size() + 1); - int i = already_used.size() + 1; - while (already_used.contains(name)) { - name = QStringLiteral("column_%1").arg(++i); - } - return name; - } - - const auto it = alias_map.constFind(raw.toLower()); - if (it != alias_map.constEnd() && !already_used.contains(it.value())) { - return it.value(); - } - - //Either an unknown name, or a second cell meaning something which is - //already used : the cell is kept as it is written, its data is never - //thrown away. - QString name = raw; - int i = 2; - while (already_used.contains(name)) { - name = raw + QStringLiteral("_%1").arg(i++); - } - return name; -} - -/** - @brief CableTypeList::isMachineHeaderLine - Tell whether the second line of a cable type file is the machine - header QElectroTech writes under the label line : the canonical - column names, kept so that a file written in one language is still - understood in another one. - @param label_line the first line of the file, the one the user reads - @param candidate the line which may be the machine header - @param alias_map lowercased alias -> canonical name - @return true when candidate must not be read as a record -*/ -bool CableTypeList::isMachineHeaderLine(const QStringList &label_line, - const QStringList &candidate, - const QMap &alias_map) -{ - if (candidate.isEmpty() || candidate.size() != label_line.size()) { - return false; - } - - //At least one cell the program really knows : a line made only - //of key shaped words nobody here gave a meaning to is far more - //likely to be a cable than a header. - bool has_known_column = false; - for (int i = 0; i < candidate.size(); ++i) - { - const QString cell = candidate.at(i).trimmed(); - if (cell.isEmpty()) { - continue; - } - if (alias_map.contains(cell.toLower())) - { - has_known_column = true; - continue; - } - if (cell == label_line.at(i).trimmed()) { - continue; - } - //An unknown key is still a key : the column comes back as - //it was written, its data is never thrown away. - if (isMachineKey(cell)) { - continue; - } - return false; - } - - return has_known_column; -} - -/** - @brief CableTypeList::load - Read a cable type file. - @param path - @param data receives the columns and the records - @param error receives a message when the file can't be read - @return true on success -*/ -bool CableTypeList::load(const QString &path, CableTypeListData *data, QString *error) -{ - if (error) { - error->clear(); - } - if (!data) { - if (error) { - *error = QCoreApplication::translate("CableTypeList", "No receiver for the cable types file."); - } - return false; - } - - data->columns.clear(); - data->records.clear(); - data->separator = QLatin1Char(';'); - - QFile file(path); - if (!file.open(QIODevice::ReadOnly)) - { - if (error) { - *error = file.errorString(); - } - return false; - } - const QByteArray raw = file.readAll(); - file.close(); - - const QString content = decode(raw); - if (content.trimmed().isEmpty()) { - return true; - } - - const int eol = content.indexOf(QLatin1Char('\n')); - data->separator = MaterialList::detectSeparator(eol < 0 ? content : content.left(eol)); - - QList rows = MaterialList::parseCsv(content, data->separator); - if (rows.isEmpty()) { - return true; - } - - const QStringList header = rows.takeFirst(); - const QMap alias_map = headerAliasMap(); - - //The label line is followed by the machine header QElectroTech - //writes itself : the canonical names, so that the file is read - //the same way whatever the language it was written in. - QStringList machine_line; - if (!rows.isEmpty() && isMachineHeaderLine(header, rows.first(), alias_map)) { - machine_line = rows.takeFirst(); - } - - QStringList used; - for (int i = 0; i < header.size(); ++i) - { - QString cell = header.at(i); - if (i < machine_line.size() && !machine_line.at(i).isEmpty()) { - cell = machine_line.at(i); - } - const QString column = canonicalColumn(cell, used, alias_map); - used.append(column); - data->columns.append(column); - } - - for (const QStringList &row : std::as_const(rows)) - { - CableTypeRecord record; - bool empty = true; - for (int i = 0; i < data->columns.size(); ++i) - { - const QString value = i < row.size() ? row.at(i) : QString(); - if (!value.isEmpty()) { - empty = false; - } - record.setValue(data->columns.at(i), value); - } - //Cells written past the header are not thrown away : they are - //written back the way they were read. - for (int i = data->columns.size(); i < row.size(); ++i) - { - if (!row.at(i).isEmpty()) { - empty = false; - } - record.extra.append(row.at(i)); - } - if (!empty) { - data->records.append(record); - } - } - - return true; -} - -/** - @brief CableTypeList::writeFile - Write a whole cable type file atomically : a crash or a full disk - never leaves a half written catalogue behind. - @param path - @param data - @param error receives a message when the file can't be written - @return true on success -*/ -bool CableTypeList::writeFile(const QString &path, const CableTypeListData &data, QString *error) -{ - if (error) { - error->clear(); - } - - QList rows; - rows.reserve(data.records.size()); - for (const CableTypeRecord &record : data.records) - { - QStringList row; - row.reserve(data.columns.size()); - for (const QString &column : data.columns) { - row.append(record.value(column)); - } - row.append(record.extra); - rows.append(row); - } - - QList header_lines; - header_lines.append(translatedHeader(data.columns)); - header_lines.append(data.columns); - const QByteArray content = MaterialList::serializeCsv(header_lines, rows, data.separator); - - //The folder may not be there yet: a fresh profile has no - //documents folder, and a path typed into the settings may name a - //folder which was never created. QSaveFile cannot write into a - //folder which does not exist, so without this the whole write - //fails and the message says nothing about the real reason. - const QString folder = QFileInfo(path).absolutePath(); - if (!folder.isEmpty() && !QDir(folder).exists() && !QDir().mkpath(folder)) - { - if (error) { - *error = QCoreApplication::translate( - "CableTypeList", - "Cannot create the folder %1 for the cable types file.").arg(folder); - } - return false; - } - - QSaveFile file(path); - if (!file.open(QIODevice::WriteOnly) || file.write(content) != content.size()) - { - if (error) { - *error = file.errorString(); - } - file.cancelWriting(); - return false; - } - if (!file.commit()) - { - if (error) { - *error = file.errorString(); - } - return false; - } - return true; -} - -/** - @brief CableTypeList::appendRecord - Add one cable type at the end of a cable type file. - The file is read again first: changes the user just saved from his - spreadsheet are part of what gets rewritten, never dropped. - @param path - @param record - @param error receives a message when the file can't be written - @return true on success -*/ -bool CableTypeList::appendRecord(const QString &path, const CableTypeRecord &record, QString *error) -{ - if (error) { - error->clear(); - } - - CableTypeListData data; - if (hasContent(path)) - { - if (!load(path, &data, error)) { - return false; - } - if (data.columns.isEmpty()) - { - if (error) { - *error = QCoreApplication::translate("CableTypeList", "The file contains no header: missing columns."); - } - return false; - } - } - else - { - data.columns = defaultColumns(); - data.separator = QLatin1Char(';'); - } - - data.records.append(record); - return writeFile(path, data, error); -} - -/** - @brief CableTypeList::updateRecord - Rewrite one line of a cable type file, the way a spreadsheet saves a - row he just edited: the line is found as it stands at this moment, - replaced by what the user left, and the whole file is written again - -- so the changes he saved from his spreadsheet in the meantime are - part of it rather than being thrown away. - @param path the file to write - @param before the line as it stands in the file right now - @param after the line as the user left it - @param error receives a message when the file can't be written, or - when the line he started from is no longer in it - @return true on success -*/ -bool CableTypeList::updateRecord(const QString &path, - const CableTypeRecord &before, - const CableTypeRecord &after, - QString *error) -{ - if (error) { - error->clear(); - } - - CableTypeListData data; - if (!load(path, &data, error)) { - return false; - } - if (data.columns.isEmpty()) - { - if (error) { - *error = QCoreApplication::translate("CableTypeList", "The file contains no header: missing columns."); - } - return false; - } - - int row = -1; - for (int i = 0; i < data.records.size(); ++i) - { - if (data.records.at(i) == before) { - row = i; - break; - } - } - if (row < 0) - { - //The line he started from is gone: rewriting whatever stands - //there now would overwrite somebody else's work. - if (error) { - *error = QCoreApplication::translate("CableTypeList", - "This entry no longer exists in the file: it may have been modified in the meantime."); - } - return false; - } - - data.records[row] = after; - return writeFile(path, data, error); -} - -/** - @brief CableTypeList::createFile - Create an empty cable type file holding only the header lines. - @param path - @param error receives a message when the file can't be written - @return true on success -*/ -bool CableTypeList::createFile(const QString &path, QString *error) -{ - if (error) { - error->clear(); - } - - if (hasContent(path)) - { - if (error) { - *error = QCoreApplication::translate("CableTypeList", "The file already exists and is not empty."); - } - return false; - } - - CableTypeListData data; - data.columns = defaultColumns(); - data.separator = QLatin1Char(';'); - return writeFile(path, data, error); -} - -/** - @brief CableTypeList::isEmptyFile - @param path - @return true when no file, or an empty one, exists at path -*/ -bool CableTypeList::isEmptyFile(const QString &path) -{ - return !hasContent(path); -} diff --git a/sources/cablelist/cabletypelist.h b/sources/cablelist/cabletypelist.h deleted file mode 100644 index a2e5bfcfa..000000000 --- a/sources/cablelist/cabletypelist.h +++ /dev/null @@ -1,134 +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 CABLETYPELIST_H -#define CABLETYPELIST_H - -#include -#include -#include -#include -#include -#include -#include - -/** - @brief One line of the cable type file. - - Same shape as MaterialRecord : the keys are column names, either one - QElectroTech knows (designation, manufacturer...) or one the user - invented, which is never interpreted but never thrown away either. -*/ -struct CableTypeRecord -{ - QMap values; - //Cells written after the last column of the header, kept as they - //are, exactly like MaterialRecord::extra. - QStringList extra; - - QString value(const QString &column) const {return values.value(column);} - void setValue(const QString &column, const QString &value) {values.insert(column, value);} - bool operator==(const CableTypeRecord &other) const {return values == other.values;} -}; - -/** - @brief The whole content of a cable type file. - - The separator and the column names are read from the file rather than - imposed, so a catalogue written in a spreadsheet keeps working. -*/ -struct CableTypeListData -{ - QStringList columns; - QList records; - QChar separator = QLatin1Char(';'); - - bool isEmpty() const {return columns.isEmpty() && records.isEmpty();} -}; - -/** - @brief The CableTypeList class reads and writes the cable type file (a - CSV file listing the cable kinds a project may use : their name, - how many cores they have and the colour of each of them). - - It is the cable counterpart of MaterialList and follows it step by - step : same settings handling, same two header lines (the labels the - user reads, then the canonical names QElectroTech matches against), - same lenient reading of a hand written file. - - This is a pure data class : it knows nothing about widgets, so it - stays usable from the tests and from the command line. -*/ -class CableTypeList -{ - public: - //Settings - static QString settingsKey(); - static QString configuredPath(); - static void setConfiguredPath(const QString &path); - static QString defaultPath(); - static QString defaultFileName(); - - //Geometry of the selection window, remembered from one opening - //to the next, the same way MaterialList remembers its own. - static QByteArray savedHeaderState(); - static void saveHeaderState(const QByteArray &state); - static QSize savedDialogSize(); - static void saveDialogSize(const QSize &size); - - //Columns - static QStringList defaultColumns(); - static QString translatedColumn(const QString &column); - static QStringList translatedHeader(const QStringList &columns); - - //Cable semantics - static QString designation(const CableTypeRecord &record); - static int coreCount(const CableTypeRecord &record); - static QStringList coreColors(const CableTypeRecord &record); - - //File io - static bool load(const QString &path, CableTypeListData *data, QString *error = nullptr); - static bool writeFile(const QString &path, const CableTypeListData &data, QString *error = nullptr); - static bool appendRecord(const QString &path, const CableTypeRecord &record, QString *error = nullptr); - /** - @brief Rewrite one line of a cable type file, the way a - spreadsheet saves a row he just edited. - @param path the file to write - @param before the line as it stands in the file right now - @param after the line as the user left it - @param error receives a message when the file can't be - written, or when that line is no longer in it - @return true on success - */ - static bool updateRecord(const QString &path, - const CableTypeRecord &before, - const CableTypeRecord &after, - QString *error = nullptr); - static bool createFile(const QString &path, QString *error = nullptr); - static bool isEmptyFile(const QString &path); - - private: - static QMap headerAliasMap(); - static QString canonicalColumn(const QString &header_cell, - const QStringList &already_used, - const QMap &alias_map); - static bool isMachineHeaderLine(const QStringList &label_line, - const QStringList &candidate, - const QMap &alias_map); -}; - -#endif // CABLETYPELIST_H diff --git a/sources/conductorproperties.cpp b/sources/conductorproperties.cpp index f65a2a340..6cf2f40fd 100644 --- a/sources/conductorproperties.cpp +++ b/sources/conductorproperties.cpp @@ -284,15 +284,6 @@ void ConductorProperties::toXml(QDomElement &e) const e.setAttribute("text_color", text_color.name()); e.setAttribute("formula", m_formula); e.setAttribute("cable", m_cable); - if (!m_cable_uuid.isNull()) - { - //Only written when there is a cable: a conductor which is no - //part of one keeps whatever cable text it had and no reference - //at all, so a hand written value is never mistaken for a - //generated one. - e.setAttribute("cable_uuid", m_cable_uuid.toString()); - e.setAttribute("cable_slot", QString::number(m_cable_slot)); - } e.setAttribute("bus", m_bus); e.setAttribute("function", m_function); e.setAttribute("tension_protocol", m_tension_protocol); @@ -352,8 +343,6 @@ void ConductorProperties::fromXml(QDomElement &e) text_color = (xml_text_color.isValid()? xml_text_color : QColor(Qt::black)); m_formula = e.attribute("formula"); m_cable = e.attribute("cable"); - m_cable_uuid = QUuid(e.attribute("cable_uuid")); - m_cable_slot = e.attribute("cable_slot", QString::number(-1)).toInt(); m_bus = e.attribute("bus"); m_function = e.attribute("function"); m_tension_protocol = e.attribute("tension_protocol"); @@ -855,8 +844,6 @@ bool ConductorProperties::operator==(const ConductorProperties &other) const other.text_color == text_color &&\ other.m_formula == m_formula &&\ other.m_cable == m_cable &&\ - other.m_cable_uuid == m_cable_uuid &&\ - other.m_cable_slot == m_cable_slot &&\ other.m_bus == m_bus &&\ other.m_function == m_function &&\ other.m_tension_protocol == m_tension_protocol &&\ diff --git a/sources/conductorproperties.h b/sources/conductorproperties.h index 6bb0fd004..d29bbdbaa 100644 --- a/sources/conductorproperties.h +++ b/sources/conductorproperties.h @@ -21,7 +21,6 @@ #include "qet.h" #include #include -#include class QPainter; @@ -98,14 +97,6 @@ class ConductorProperties m_bus, m_cable; - /// The cable of the project this conductor is a core of, null - /// when it belongs to no cable. The text shown in m_cable is - /// generated from the cable and never typed in: this reference - /// plus the slot below is all it takes to work it out again. - QUuid m_cable_uuid; - /// Which core of that cable this conductor shows, -1 when none - int m_cable_slot = -1; - int text_size, m_dash_size = 1; diff --git a/sources/diagram.cpp b/sources/diagram.cpp index a04d4b433..ab0c504f7 100644 --- a/sources/diagram.cpp +++ b/sources/diagram.cpp @@ -20,12 +20,9 @@ #include "ElementsCollection/elementcollectionhandler.h" #include "TerminalStrip/GraphicsItem/terminalstripitem.h" -#include "cable/cablecopy.h" -#include "cable/cablepart.h" #include "xml/terminalstripitemxml.h" #include "QPropertyUndoCommand/qpropertyundocommand.h" #include "diagramcontent.h" -#include "diagramevent/diagrameventaddcable.h" #include "diagramevent/diagrameventinterface.h" #include "diagramposition.h" #include "factory/elementfactory.h" @@ -903,23 +900,6 @@ bool Diagram::eventInterfaceIsRunning() const return m_event_interface && m_event_interface->isRunning(); } -/** - @brief Diagram::cableToolIsRunning - @return true while the cable drawing tool is the running tool. - - That tool is the one which takes the right mouse button away from - everything else: while it runs, a right click cancels the line being - drawn and must not open the quick command ring nor any context menu - with it. Every other tool lets go of the button again and is - surrounded by the ring the way it always was. -*/ -bool Diagram::cableToolIsRunning() const -{ - return m_event_interface - && m_event_interface->isRunning() - && qobject_cast(m_event_interface); -} - /** @brief Diagram::conductorsAutonumName @return the name of autonum to use. @@ -1440,10 +1420,9 @@ void Diagram::folioSequentialsToXml(QHash *cables_added) { + DiagramContent *content_ptr) { QDomElement root = document.documentElement(); - return(fromXml(root, position, consider_informations, content_ptr, cables_added)); + return(fromXml(root, position, consider_informations, content_ptr)); } /** @@ -1683,8 +1662,7 @@ QPair pickEnds(const QDomElement &f, bool Diagram::fromXml(QDomElement &document, QPointF position, bool consider_informations, - DiagramContent *content_ptr, - QList *cables_added) + DiagramContent *content_ptr) { const QDomElement& root = document; // The first element must be a diagram @@ -1950,17 +1928,6 @@ bool Diagram::fromXml(QDomElement &document, //Load terminal strip item QVector added_strips { TerminalStripItemXml::fromXml(this, root) }; - //Load the cable lines a copy fragment carries -- built here - //rather than in the caller so that they are placed with the rest - //of the selection, but only when the caller asked to be handed - //them: a folio loaded from a project file holds no cable of its - //own, the cables of a project are read at project level. - QList added_cables; - if (cables_added) { - added_cables = CableCopy::read(this, root); - *cables_added = added_cables; - } - //Translate items if a new position was given in parameter if (position != QPointF()) { @@ -1971,7 +1938,6 @@ bool Diagram::fromXml(QDomElement &document, for (auto image : std::as_const(added_images )) added_items << image; for (auto table : std::as_const(added_tables )) added_items << table; for (const auto &strip : std::as_const(added_strips)) added_items << strip; - for (const auto &cable : std::as_const(added_cables)) added_items << cable; //Get the top left corner of the rectangle that contain all added items QRectF items_rect; diff --git a/sources/diagram.h b/sources/diagram.h index c19cac53b..e0587a96a 100644 --- a/sources/diagram.h +++ b/sources/diagram.h @@ -33,7 +33,6 @@ #include class Conductor; -class CablePart; class CustomElement; class DiagramContent; class DiagramPosition; @@ -204,21 +203,14 @@ class Diagram : public QGraphicsScene QPointF = QPointF(), bool = true, DiagramContent * = nullptr); - //The last argument is only for a copy fragment: when it is - //given, the cable lines that fragment carries are built as - //well and handed back through it -- they are not part of a - //DiagramContent, so the caller has to know about them to - //put them on the undo stack (see CableCopy) bool fromXml(QDomDocument &, QPointF = QPointF(), bool = true, - DiagramContent * = nullptr, - QList * = nullptr); + DiagramContent * = nullptr); bool fromXml(QDomElement &, QPointF = QPointF(), bool = true, - DiagramContent * = nullptr, - QList * = nullptr); + DiagramContent * = nullptr); void folioSequentialsToXml(QHash*, QDomElement *, @@ -239,7 +231,6 @@ class Diagram : public QGraphicsScene virtual void addItem (QGraphicsItem *item); virtual void removeItem (QGraphicsItem *item); bool eventInterfaceIsRunning() const; - bool cableToolIsRunning() const; // methods related to graphics options ExportProperties applyProperties(const ExportProperties &); diff --git a/sources/diagramevent/diagrameventaddcable.cpp b/sources/diagramevent/diagrameventaddcable.cpp deleted file mode 100644 index 51d9aaacd..000000000 --- a/sources/diagramevent/diagrameventaddcable.cpp +++ /dev/null @@ -1,463 +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 "diagrameventaddcable.h" - -#include "../cable/addcablecommand.h" -#include "../cable/cablecreatedialog.h" -#include "../cable/cablepart.h" -#include "../diagram.h" -#include "../qetapp.h" -#include "../qetdiagrameditor.h" -#include "../qetproject.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace { - - ///A click closer than this to the start point is not an end point - const qreal min_end_length = 2.0; - -/** - @brief How far apart two grid points are, the same way the snapping - itself works it out -- the direction the line may be locked to has to - be judged against the same step the end point will land on. -*/ -qreal gridStep() -{ - QSettings settings; - const int x = settings.value(QStringLiteral("diagrameditor/Xgrid"), - Diagram::xGrid).toInt(); - return qMax(1, x); -} - -} // namespace - -/** - @brief DiagramEventAddCable::DiagramEventAddCable - @param diagram the folio the cable is drawn on -*/ -DiagramEventAddCable::DiagramEventAddCable(Diagram *diagram) : - DiagramEventInterface(diagram) -{ - m_running = true; - //Done here as well as in init(): Diagram::setEventInterface() - //only calls init() when it replaces another tool, so a fresh - //tool would otherwise never be set up at all. - init(); - //The message itself waits for the next turn of the event loop: - //setEventInterface() destroys the tool which was running before - //this constructor returns, and its destructor clears the status - //bar, which would wipe out anything shown before it is done. - QTimer::singleShot(0, this, [this]() { updateHint(); }); -} - -/** - @brief DiagramEventAddCable::~DiagramEventAddCable -*/ -DiagramEventAddCable::~DiagramEventAddCable() -{ - delete m_preview; - delete m_help_horiz; - delete m_help_verti; - - if (m_diagram) - { - for (QGraphicsView *view : m_diagram->views()) { - view->setContextMenuPolicy(Qt::DefaultContextMenu); - } - } - clearHint(); -} - -/** - @brief DiagramEventAddCable::init - While the tool runs, the right button belongs to it alone: it cancels - the line being drawn or ends the tool. The quick command ring and the - context menu of the folio would both pop up in the middle of the - drawing and take the tool away from under the user, so they are kept - out of the way until the tool is gone. -*/ -void DiagramEventAddCable::init() -{ - m_running = true; - if (m_diagram) - { - for (QGraphicsView *view : m_diagram->views()) { - view->setContextMenuPolicy(Qt::NoContextMenu); - } - } - updateHint(); -} - -/** - @brief DiagramEventAddCable::reset - Put the tool back to where it was before the first click: no line - kept, no preview, nothing remembered. The cross stays, since it - belongs to the tool rather than to one line. -*/ -void DiagramEventAddCable::reset() -{ - if (m_preview && m_diagram) { - m_diagram->removeItem(m_preview); - } - delete m_preview; - m_preview = nullptr; - m_has_anchor = false; - m_anchor = QPointF(); - m_axis = -1; - m_crossings.clear(); - updateHint(); -} - -/** - @brief DiagramEventAddCable::clearHint -*/ -void DiagramEventAddCable::clearHint() const -{ - if (!m_diagram || m_diagram->views().isEmpty()) return; - if (auto *editor = QETApp::diagramEditorAncestorOf(m_diagram->views().constFirst())) { - editor->statusBar()->clearMessage(); - } -} - -/** - @brief DiagramEventAddCable::updateHint - Say what the next click does, and -- once the line has been pulled - out -- how many conductors it runs across, so the user knows before - the dialog opens what the cable is going to be given. -*/ -void DiagramEventAddCable::updateHint() const -{ - if (!m_diagram || m_diagram->views().isEmpty()) return; - auto *editor = QETApp::diagramEditorAncestorOf(m_diagram->views().constFirst()); - if (!editor) return; - - if (!m_has_anchor) - { - editor->statusBar()->showMessage(tr( - "Left click: place the cable start; " - "drag or second click: finish; " - "right click: cancel; Esc: quit")); - return; - } - - editor->statusBar()->showMessage(tr( - "Left click or release: finish the line " - "(%n core(s) recognized; always horizontal or vertical); " - "right click: cancel the line", - "how many conductors the line being drawn already crosses", - m_crossings.size())); -} - -/** - @brief DiagramEventAddCable::mousePressEvent - @param event -*/ -void DiagramEventAddCable::mousePressEvent(QGraphicsSceneMouseEvent *event) -{ - if (Q_UNLIKELY(!m_diagram || m_diagram->isReadOnly())) return; - - if (event->button() == Qt::RightButton) - { - //Accepted first: emitting finish() makes the diagram destroy - //this very tool, so nothing may touch its members afterwards. - event->setAccepted(true); - - //First right click takes the line being drawn back, second one - //leaves the tool: the user always knows how far he has to press - //to get out, and never lands in a menu he did not ask for. - if (m_has_anchor) { - reset(); - } else { - m_running = false; - emit finish(); - } - return; - } - if (event->button() != Qt::LeftButton) return; - - const QPointF pos = Diagram::snapToGrid(event->scenePos()); - - if (!m_has_anchor) - { - m_has_anchor = true; - m_anchor = pos; - m_axis = -1; - updatePreview(pos); - event->setAccepted(true); - return; - } - - //A second click ends the line wherever it lands: it is snapped - //back onto whichever direction the line already runs along, so - //the user does not have to aim at the line itself. - const QPointF end = finishPoint(pos); - if (QLineF(m_anchor, end).length() >= min_end_length) { - finishLine(end); - } - event->setAccepted(true); -} - -/** - @brief DiagramEventAddCable::mouseMoveEvent - @param event -*/ -void DiagramEventAddCable::mouseMoveEvent(QGraphicsSceneMouseEvent *event) -{ - showGuides(event->scenePos()); - if (!m_has_anchor) return; - - updatePreview(event->scenePos()); - event->setAccepted(true); -} - -/** - @brief DiagramEventAddCable::mouseReleaseEvent - Letting go of a drag which was pulled out far enough lays the line - down straight away, which is how a line is drawn everywhere else in - QElectroTech -- a click without a drag, on the other hand, only puts - the start point down and waits for the next click. - @param event -*/ -void DiagramEventAddCable::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) -{ - if (event->button() != Qt::LeftButton || !m_has_anchor) return; - - const QPointF end = finishPoint(Diagram::snapToGrid(event->scenePos())); - if (QLineF(m_anchor, end).length() >= min_end_length) { - finishLine(end); - } - event->setAccepted(true); -} - -/** - @brief DiagramEventAddCable::keyPressEvent - @param event -*/ -void DiagramEventAddCable::keyPressEvent(QKeyEvent *event) -{ - if (event->key() == Qt::Key_Escape) - { - if (m_has_anchor) - { - reset(); - event->setAccepted(true); - return; - } - } - DiagramEventInterface::keyPressEvent(event); -} - -/** - @brief DiagramEventAddCable::finishPoint - The point the line really ends at: snapped to the grid, and pulled - onto the direction the line already runs along, so a second click - which does not sit exactly on the axis still ends the line instead - of starting a new one. - @param scene_pos where the user clicked - @return the end of the line, in scene coordinates -*/ -QPointF DiagramEventAddCable::finishPoint(const QPointF &scene_pos) const -{ - if (m_axis == 0) { - return Diagram::snapToGrid(QPointF(scene_pos.x(), m_anchor.y())); - } - if (m_axis == 1) { - return Diagram::snapToGrid(QPointF(m_anchor.x(), scene_pos.y())); - } - - //The line was never pulled far enough to have a direction: take - //the one which is clearly longer, so it never ends up diagonal. - const QPointF delta = scene_pos - m_anchor; - if (qAbs(delta.x()) >= qAbs(delta.y())) { - return Diagram::snapToGrid(QPointF(scene_pos.x(), m_anchor.y())); - } - return Diagram::snapToGrid(QPointF(m_anchor.x(), scene_pos.y())); -} - -/** - @brief DiagramEventAddCable::updatePreview - Show the line as it will be laid down. - - The direction is decided again on every move rather than once and - for all: the user pulls, and the line follows whichever way he pulls - harder. A dead band of half a grid step keeps it from flickering - when he moves along the diagonal. - @param scene_pos where the mouse stands -*/ -void DiagramEventAddCable::updatePreview(const QPointF &scene_pos) -{ - if (!m_has_anchor || !m_diagram) return; - - QPointF end = scene_pos; - { - const QPointF delta = end - m_anchor; - const qreal dx = qAbs(delta.x()); - const qreal dy = qAbs(delta.y()); - const qreal dead = gridStep() * 0.5; - - int wanted = -1; - if (dx > dy + dead) { - wanted = 0; - } else if (dy > dx + dead) { - wanted = 1; - } else if (m_axis < 0 && (dx > dead || dy > dead)) { - wanted = dx >= dy ? 0 : 1; - } - if (wanted >= 0) { - m_axis = wanted; - } - } - - if (m_axis == 0) { - end.setY(m_anchor.y()); - } else if (m_axis == 1) { - end.setX(m_anchor.x()); - } - end = Diagram::snapToGrid(end); - - if (!m_preview) - { - m_preview = new QGraphicsLineItem; - QPen pen(QColor(0, 120, 215)); - pen.setWidthF(2.0); - pen.setStyle(Qt::DashLine); - m_preview->setPen(pen); - m_preview->setZValue(1000.0); - m_diagram->addItem(m_preview); - } - m_preview->setLine(QLineF(m_anchor, end)); - - //Keep the count of crossed conductors up to date, so the status - //bar already says what the dialog is about to say. - m_crossings = CableManager::crossedConductors(m_diagram, QLineF(m_anchor, end)); - updateHint(); -} - -/** - @brief DiagramEventAddCable::showGuides - The thin cross QElectroTech draws while any shape is drawn, which - says where the mouse stands in the folio and, for a line, along - which of the two directions it may run. - @param scene_pos where the mouse stands -*/ -void DiagramEventAddCable::showGuides(const QPointF &scene_pos) -{ - if (!m_diagram) return; - - if (!m_help_horiz || !m_help_verti) - { - QPen pen; - pen.setWidthF(0.4); - pen.setCosmetic(true); - pen.setColor(Diagram::background_color == Qt::darkGray ? Qt::lightGray : Qt::darkGray); - - const QRectF rect = m_diagram->border_and_titleblock.insideBorderRect(); - - if (!m_help_horiz) - { - m_help_horiz = new QGraphicsLineItem(rect.topLeft().x(), 0, rect.topRight().x(), 0); - m_help_horiz->setPen(pen); - m_help_horiz->setZValue(999.0); - m_diagram->addItem(m_help_horiz); - } - if (!m_help_verti) - { - m_help_verti = new QGraphicsLineItem(0, rect.topLeft().y(), 0, rect.bottomLeft().y()); - m_help_verti->setPen(pen); - m_help_verti->setZValue(999.0); - m_diagram->addItem(m_help_verti); - } - } - - const QPointF point = Diagram::snapToGrid(scene_pos); - m_help_horiz->setY(point.y()); - m_help_verti->setX(point.x()); -} - -/** - @brief DiagramEventAddCable::finishLine - Put the line down and hand it over to the dialog which turns it into - a cable -- a new one from the cable type file, or one the project - already holds. - @param end where the line stops -*/ -void DiagramEventAddCable::finishLine(const QPointF &end) -{ - if (!m_diagram || !m_diagram->project()) { - reset(); - return; - } - - QPointF to = end; - if (m_axis == 0) { - to.setY(m_anchor.y()); - } else if (m_axis == 1) { - to.setX(m_anchor.x()); - } - to = Diagram::snapToGrid(to); - - const QLineF line(m_anchor, to); - if (line.length() < min_end_length) { - reset(); - return; - } - - m_crossings = CableManager::crossedConductors(m_diagram, line); - - CablePartData part; - part.uuid = QUuid::createUuid(); - part.diagram = m_diagram->uuid(); - part.p1 = line.p1(); - part.p2 = line.p2(); - - //Remember which cable the line was given to: a brand new one was - //brought into being by this very dialog and undo has to take it - //away again, an existing one only gains a section. - const QVector before = m_diagram->project()->cables(); - - { - CableCreateDialog dialog(m_diagram, m_crossings, part, m_diagram->views().isEmpty() - ? nullptr : m_diagram->views().constFirst()); - if (dialog.exec() != QDialog::Accepted || !dialog.cable()) - { - reset(); - return; - } - - const bool own_cable = !before.contains(dialog.cable()); - - auto *item = new CablePart(part); - item->setCable(dialog.cable()); - m_diagram->addItem(item); - - m_diagram->project()->undoStack()->push( - new AddCableCommand(dialog.cable(), item, m_diagram, own_cable, - dialog.takenCores())); - } - - reset(); -} diff --git a/sources/diagramevent/diagrameventaddcable.h b/sources/diagramevent/diagrameventaddcable.h deleted file mode 100644 index 40b6cf0b1..000000000 --- a/sources/diagramevent/diagrameventaddcable.h +++ /dev/null @@ -1,78 +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 DIAGRAMEVENTADDCABLE_H -#define DIAGRAMEVENTADDCABLE_H - -#include "../cable/cablemanager.h" -#include "diagrameventinterface.h" - -class QLineF; -class QGraphicsLineItem; - -/** - @brief The DiagramEventAddCable class draws the trunk line of a cable. - - It behaves like every other drawing tool of QElectroTech: the first - click puts the start point down, the mouse then shows the line as it - will be, and either a second click or the release of a drag lays it - -- whichever the user does, he does not have to aim at anything. - - The line stays horizontal or vertical, whichever way the user pulls - harder, and it keeps following him: the direction is worked out again - on every mouse move (with a dead band so it does not flicker), never - frozen by the first stray pixel of the gesture -- a cable trunk runs - straight along a row of drawings, but along the row the user meant. -*/ -class DiagramEventAddCable : public DiagramEventInterface -{ - Q_OBJECT - - public: - explicit DiagramEventAddCable(Diagram *diagram); - ~DiagramEventAddCable() override; - - void mousePressEvent(QGraphicsSceneMouseEvent *event) override; - void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; - void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; - void keyPressEvent(QKeyEvent *event) override; - void init() override; - - private: - void reset(); - void updatePreview(const QPointF &scene_pos); - QPointF finishPoint(const QPointF &scene_pos) const; - void finishLine(const QPointF &end); - void showGuides(const QPointF &scene_pos); - void clearHint() const; - void updateHint() const; - - private: - QGraphicsLineItem *m_preview = nullptr; - ///The cross QElectroTech shows while any shape is drawn - QGraphicsLineItem *m_help_horiz = nullptr; - QGraphicsLineItem *m_help_verti = nullptr; - ///True once the first click has put the line's start down - bool m_has_anchor = false; - QPointF m_anchor; - ///Which way the line is locked: - ///-1 undecided, 0 horizontal, 1 vertical - int m_axis = -1; - QList m_crossings; -}; - -#endif // DIAGRAMEVENTADDCABLE_H diff --git a/sources/diagramevent/diagrameventaddpaste.cpp b/sources/diagramevent/diagrameventaddpaste.cpp index c8a4e02ad..354bd8132 100644 --- a/sources/diagramevent/diagrameventaddpaste.cpp +++ b/sources/diagramevent/diagrameventaddpaste.cpp @@ -18,10 +18,6 @@ #include "diagrameventaddpaste.h" #include "../autoNum/ui/pastenumberingimport.h" -#include "../cable/addcablecommand.h" -#include "../cable/cablecopy.h" -#include "../cable/cablepart.h" -#include "../cable/editcablecommand.h" #include "../diagram.h" #include "../foliogrid.h" #include "../diagramcommands.h" @@ -34,15 +30,12 @@ #include #include -#include #include #include #include #include #include -#include - /** @brief DiagramEventAddPaste::DiagramEventAddPaste @param diagram : diagram to paste into @@ -81,12 +74,8 @@ db->setUpdateBlocked(true); } - //Load items at their original XML coordinates. A cable line - //comes back through its own list rather than through the - //content: the cable owns it, so it is not one of the folio's - //items -- and it travels as a whole, colour labels and all, - //whether or not the place it is going to holds a wire. - m_diagram->fromXml(document_xml, QPointF(), false, &m_content, &m_cables); + //Load items at their original XML coordinates. + m_diagram->fromXml(document_xml, QPointF(), false, &m_content); m_copied_schemes = PasteNumberingImport::copiedBy(document_xml); if (db) { @@ -94,15 +83,9 @@ db->setUpdateBlocked(false); db->updateDB(); } - if (!m_content.count() && m_cables.isEmpty()) return; + if (!m_content.count()) return; - QList movable = m_content.items(MovableItems); - for (CablePart *part : std::as_const(m_cables)) { - if (part) { - movable << part; - m_cable_items.insert(part); - } - } + const QList movable = m_content.items(MovableItems); if (movable.isEmpty()) return; //Compute the top-left of all items' actual on-screen bounding @@ -326,24 +309,11 @@ void DiagramEventAddPaste::moveTo(const QPointF &scene_pos) const QPointF delta = snapGrid(scene_pos) - m_initial_cursor; - //A cable line is handed a step rather than a place to stand: it - //folds a translation into its own geometry and into the colour - //labels it carries and keeps its own pos() at (0, 0) - //(CablePart::itemChange), so the position stored above -- always - //(0, 0) for it -- would be an addition again on every mouse move - //and the line would run away from the group it was pasted with. - //The difference to the last step keeps the translation the same - //one the rest of the paste gets, expressed their way: a place. - const QPointF cable_step = delta - m_cable_delta; for (auto it = m_relative_pos.constBegin() ; it != m_relative_pos.constEnd() ; ++it) { - if (!it.key()) continue; - if (m_cable_items.contains(it.key())) { - it.key()->setPos(cable_step); - } else { + if (it.key()) { it.key()->setPos(it.value() + delta); } } - m_cable_delta = delta; //Update conductor paths so they follow the moved terminals. const QList conductors = m_content.conductors(DiagramContent::AnyConductor); @@ -407,11 +377,6 @@ void DiagramEventAddPaste::keyPressEvent(QKeyEvent *event) PasteDiagramCommand's first redo() does not add the items to the scene -- it assumes they are already there, which is what Diagram::fromXml did when this started. So pushing it here adopts them rather than duplicating them. - - The cable lines are wired just before that, which is the first moment - the place they will occupy is final: each colour label takes the wire - it happens to stand on, and a line the user refuses to let take wires - away from another cable goes away again on its own. */ void DiagramEventAddPaste::commit() { @@ -419,35 +384,10 @@ void DiagramEventAddPaste::commit() m_finished = true; m_running = false; - const QList wired = CableCopy::wire( - m_diagram, m_cables, - m_content.conductors(DiagramContent::AnyConductor)); - m_cables.clear(); - - //The cable lines the copy brings are not part of the paste - //itself: they are handed to the paste so that one Ctrl+Z takes - //the whole of it back in one go. - QList cable_commands; - for (const CableCopy::Wired &line : std::as_const(wired)) - { - if (!line.part) continue; - - auto *added = new AddCableCommand(line.part->cable(), line.part.data(), - m_diagram, true, line.taken); - added->setText(QCoreApplication::translate("CableCopy", "Paste a cable")); - cable_commands << added; - } - //Asks whether to import the numberings the copy brings, if the - //project has not got them; the paste itself and the cable lines - //travel inside the same step of the undo stack. + //project has not got them PasteNumberingImport::push(m_diagram->views().isEmpty() ? nullptr : m_diagram->views().first(), - m_diagram, m_content, m_copied_schemes, cable_commands); - - //The undo stack has just run its first redo, which may have - //renewed the identity of a pasted conductor: the cores are - //pointed at the wires again so their cable field survives it. - CableCopy::repoint(wired); + m_diagram, m_content, m_copied_schemes); emit finish(); } @@ -479,15 +419,6 @@ void DiagramEventAddPaste::removeItems() delete item; } - //The cable lines never reached the undo stack either, so they - //are taken back the way they were built: off the folio, out of - //the project and deleted, leaving no trace of having been - //there -- not even a project asking to be saved for nothing. - for (CablePart *part : std::as_const(m_cables)) { - CableCopy::discard(m_diagram, part); - } - m_cables.clear(); - m_content.clear(); m_relative_pos.clear(); } diff --git a/sources/diagramevent/diagrameventaddpaste.h b/sources/diagramevent/diagrameventaddpaste.h index 5e6012290..12aafdd26 100644 --- a/sources/diagramevent/diagrameventaddpaste.h +++ b/sources/diagramevent/diagrameventaddpaste.h @@ -24,9 +24,7 @@ #include #include -#include -class CablePart; class QStatusBar; /** @@ -93,25 +91,6 @@ class DiagramEventAddPaste : public DiagramEventInterface void removeItems(); DiagramContent m_content; - ///The cable lines the same fragment carried, if any. They are - ///not part of a DiagramContent -- the cable owns them -- so - ///they travel beside it: moved with the rest of the paste, - ///wired where it lands, and taken away whole if it never lands. - QList m_cables; - ///The cable lines among them, as plain items. Their setPos() is - ///a translation rather than a move: CablePart works in scene - ///coordinates and keeps itself at the origin, folding whatever - ///it is handed into its own geometry and into the colour labels - ///it carries (CablePart::itemChange). Everything else in - ///m_relative_pos is given a place to stand; these are given a - ///step to take. - QSet m_cable_items; - ///The step already handed to those cable lines. They take the - ///difference to the new one, because for them a setPos() is an - ///addition: handing them the baseline-relative position on every - ///mouse move would add it again each time and let the line run - ///away from the rest of the paste. - QPointF m_cable_delta; /// The numberings the pasted copy carries QList m_copied_schemes; ///Each movable item's position relative to the group's top left, diff --git a/sources/diagramtoolbarsettings.cpp b/sources/diagramtoolbarsettings.cpp index 95964b227..9fed1b76a 100644 --- a/sources/diagramtoolbarsettings.cpp +++ b/sources/diagramtoolbarsettings.cpp @@ -193,11 +193,6 @@ QStringList DiagramToolbarSettings::defaultIds(const QString &name) QStringLiteral("diagrameditor.conductor_reset"), QStringLiteral("diagrameditor.auto_conductor"), QStringLiteral("diagrameditor.auto_break_conductor"), - //The cable tool is drawn across the conductors those two - //actions work on, so it sits with them rather than with - //the shapes of the "Ajouter" toolbar. - sep, - QStringLiteral("diagrameditor.add_cable"), QStringLiteral("widget:conductor_color")}; } if (name == QLatin1String("adding")) { diff --git a/sources/diagramview.cpp b/sources/diagramview.cpp index 548606aa0..e758b91de 100644 --- a/sources/diagramview.cpp +++ b/sources/diagramview.cpp @@ -23,10 +23,6 @@ #include "qetproject.h" #include "QPropertyUndoCommand/qpropertyundocommand.h" #include "diagramcommands.h" -#include "cable/addcablecommand.h" -#include "cable/cablecopy.h" -#include "cable/cablepart.h" -#include "cable/editcablecommand.h" #include "diagramevent/diagrameventaddelement.h" #include "diagramevent/diagrameventaddmacro.h" #include "dvevent/dveventinterface.h" @@ -71,29 +67,6 @@ static bool hasCabinetInfo(const DiagramContext &infos); -/** - @brief batchWith - Keep two undo steps together so one Ctrl+Z takes the whole thing - back in one go. - - The batch also keeps the name the undo history shows for what the - user actually did: BatchCommand's own name says "Supprimer", which - is what it was first written for and would be misleading for a paste - or a duplicate. - @param first the step already assembled, may be null - @param second the step to add; ownership goes to the result - @return both steps as one command, or the only one of them -*/ -static QUndoCommand *batchWith(QUndoCommand *first, QUndoCommand *second) -{ - if (!first) return second; - if (!second) return first; - - auto *batched = new BatchCommand(first, second); - if (!first->text().isEmpty()) batched->setText(first->text()); - return batched; -} - /** Constructeur @param diagram Schema a afficher ; si diagram vaut 0, un nouveau Diagram est utilise @@ -665,29 +638,8 @@ void DiagramView::cut() { copy(); DiagramContent cut_content(m_diagram); - //A cable line is not held by DiagramContent: it leaves with a - //command of its own, which takes the cable itself away too when - //no line of it is left on the folio. - QList cable_cut; - for (QGraphicsItem *item : m_diagram->selectedItems()) { - if (auto *part = qgraphicsitem_cast(item)) { - cable_cut << part; - } - } - m_diagram -> clearSelection(); - - if (cable_cut.isEmpty()) { - m_diagram -> undoStack().push(new CutDiagramCommand(m_diagram, cut_content)); - return; - } - - QUndoCommand *command = nullptr; - if (cut_content.count()) { - command = new CutDiagramCommand(m_diagram, cut_content); - } - command = batchWith(command, new RemoveCableCommand(m_diagram, cable_cut)); - m_diagram -> undoStack().push(command); + m_diagram -> undoStack().push(new CutDiagramCommand(m_diagram, cut_content)); } /** @@ -697,12 +649,7 @@ void DiagramView::cut() void DiagramView::copy() { QClipboard *clipboard = QApplication::clipboard(); - QDomDocument document = m_diagram -> toXml(false, true); - //A drawn cable line is not one of the folio's items -- the cable - //owns it -- so it travels in a block of its own, beside the XML - //of the folio rather than in it. - CableCopy::write(m_diagram, document); - QString content_clipboard = document.toString(4); + QString content_clipboard = m_diagram -> toXml(false, true).toString(4); if (clipboard -> supportsSelection()) clipboard -> setText(content_clipboard, QClipboard::Selection); clipboard -> setText(content_clipboard); } @@ -723,52 +670,16 @@ void DiagramView::paste(const QPointF &pos, QClipboard::Mode clipboard_mode) { if (!document_xml.setContent(text_clipboard)) return; DiagramContent content_pasted; - //A cable line is not part of a DiagramContent: it comes back - //through its own list, carrying the colour labels it was copied - //with, and each of them is given the wire it happens to stand - //on at the place it has just been put -- none at all, when that - //place holds no wire, in which case the labels go on standing - //on the line to be wired when it is pulled into place. - QList cable_pasted; - m_diagram->fromXml(document_xml, pos, false, &content_pasted, &cable_pasted); - const QList wired = CableCopy::wire( - m_diagram, cable_pasted, - content_pasted.conductors(DiagramContent::AnyConductor)); + m_diagram->fromXml(document_xml, pos, false, &content_pasted); //If something was really added to diagram, we create an undo object. - if (content_pasted.count() || !wired.isEmpty()) + if (content_pasted.count()) { m_diagram -> clearSelection(); - - //One single step for the whole paste, cable lines included: - //undoing has to take back everything which came out of the - //clipboard in one go, and redoing has to put it all back. - QList cable_commands; - for (const CableCopy::Wired &line : std::as_const(wired)) { - if (!line.part) continue; - auto *added = new AddCableCommand(line.part->cable(), line.part.data(), - m_diagram, true, line.taken); - added->setText(QCoreApplication::translate("CableCopy", "Paste a cable")); - cable_commands << added; - } - //The copies end up selected, like everything else a paste - //just added, so the next Ctrl+V or Ctrl+D carries on from them - for (const CableCopy::Wired &line : std::as_const(wired)) { - if (line.part) line.part->setSelected(true); - } - //Asks whether to import the numberings the copy brings, if the - //project has not got them; the paste itself and the cable lines - //travel inside the same step of the undo stack. + //project has not got them PasteNumberingImport::push(this, m_diagram, content_pasted, - PasteNumberingImport::copiedBy(document_xml), - cable_commands); - //The stack has just renewed the identity of a pasted - //conductor: the cores are pointed at the wires again and - //the cable fields written out once more, so the entries - //the copy was given as it was put down are on the wires it - //has actually landed on. - CableCopy::repoint(wired); + PasteNumberingImport::copiedBy(document_xml)); adjustSceneRect(); } } @@ -838,51 +749,21 @@ void DiagramView::duplicate(const QPoint &stepOffset) // Mirrors copy(), but does not touch the system clipboard: Ctrl+D // should not clobber whatever the user last copied with Ctrl+C. QDomDocument document = m_diagram->toXml(false, true); - CableCopy::write(m_diagram, document); DiagramContent pasted; - QList cable_pasted; // No position argument -- see the function comment above for why // the offset is not passed here. - m_diagram->fromXml(document, QPointF(), false, &pasted, &cable_pasted); - if (!pasted.count() && cable_pasted.isEmpty()) return; + m_diagram->fromXml(document, QPointF(), false, &pasted); + if (!pasted.count()) return; const int movable = DiagramContent::Elements | DiagramContent::TextFields | DiagramContent::Images | DiagramContent::Shapes | DiagramContent::Tables | DiagramContent::TerminalStrip; for (QGraphicsItem *item : pasted.items(movable)) item->setPos(item->pos() + offset); - //The new line is stamped beside the original like everything - //else -- and only after that, because where each colour label - //ends up decides which wire it is going to take - for (CablePart *part : std::as_const(cable_pasted)) - if (part) part->setPos(part->pos() + offset); - - const QList wired = CableCopy::wire( - m_diagram, cable_pasted, - pasted.conductors(DiagramContent::AnyConductor)); m_diagram->clearSelection(); - - QUndoCommand *command = nullptr; - if (pasted.count()) { - command = new PasteDiagramCommand(m_diagram, pasted); - } - for (const CableCopy::Wired &line : std::as_const(wired)) { - if (!line.part) continue; - auto *added = new AddCableCommand(line.part->cable(), line.part.data(), - m_diagram, true, line.taken); - added->setText(QCoreApplication::translate("CableCopy", "Duplicate a cable")); - command = batchWith(command, added); - } - for (const CableCopy::Wired &line : std::as_const(wired)) { - if (line.part) line.part->setSelected(true); - } - if (command) m_diagram->undoStack().push(command); - //Same as a paste: the stack has renewed the identity of the - //pasted conductors, so the cores are pointed at the wires again - //and the cable fields written out once more. - CableCopy::repoint(wired); + m_diagram->undoStack().push(new PasteDiagramCommand(m_diagram, pasted)); adjustSceneRect(); } @@ -912,59 +793,22 @@ void DiagramView::mousePressEvent(QMouseEvent *e) m_press_pos = e->position().toPoint(); } - //A line which waits for the click putting one of its cores down - //owns the right button as well: that click says "no" to the core, - //so it neither starts the quick command ring nor reaches the - //sheet. It is the context menu of this very click which ends the - //wait (see contextMenuEvent()), which is why nothing is ended - //here: the menu is then cancelled instead of opened, and no menu - //can open out of a click meant to say "no" -- neither QET's own - //nor the platform's, which arrives with the press on X11 and with - //the release on Windows. - if (e->button() == Qt::RightButton && CablePart::placingPart()) { - e->accept(); - return; - } - //Right button: a click opens the context menu on release, a drag is //a gesture (DiagramGestureOverlay). Left alone while a text is edited. m_swallow_native_menu = false; m_gesture_over_tool = false; - - //A right click on the running cable tool belongs to the tool - //alone: it cancels the line being drawn, and ending the tool puts - //the context menu policy of the views back before this very click - //is over. The platform then still sends its own context menu for - //the same click -- on X11 with the press, on Windows with the - //release -- and the menu would open on the click which was meant - //to cancel. So it is swallowed here, the way a gesture swallows - //it further down; the next press which no tool handles puts this - //flag back to false. No other tool is touched: they hand the - //button back and are surrounded by the ring, as they always were. - if (e->button() == Qt::RightButton && m_diagram - && m_diagram->cableToolIsRunning()) { - m_swallow_native_menu = true; - } if (e->button() == Qt::RightButton && DiagramGestureOverlay::isEnabled() - && !m_diagram->focusItem() - && !m_diagram->cableToolIsRunning()) + && !m_diagram->focusItem()) { - //The right button draws the quick command ring, or opens the - //context menu on release. The cable tool alone does not let go - //of it: while it runs no gesture is tracked at all, because - //tracking one would both pop the ring up in the middle of the - //drawing and take the tool away from the click meant to - //cancel it (see mouseMoveEvent, which ends the tool as soon - //as a gesture is recognised). m_gesture_tracking = true; m_gesture_origin = e->position().toPoint(); m_context_toolbar->hide(); - //Another tool is running, often one a gesture just started. A - //right click still goes to it -- it cancels or finishes the - //tool -- so the press carries on to the scene. A drag ends the - //tool and shows the ring (see mouseMoveEvent). + //A tool is running, often one a gesture just started. A right + //click still goes to it -- it cancels or finishes the tool -- so + //the press carries on to the scene. A drag ends the tool and + //shows the ring (see mouseMoveEvent). if (m_diagram->eventInterfaceIsRunning()) { m_gesture_over_tool = true; } @@ -1433,15 +1277,6 @@ void DiagramView::keyPressEvent(QKeyEvent *e) if (m_diagram && m_diagram->eventInterfaceIsRunning()) { QGraphicsView::keyPressEvent(e); // let the active tool see it return; - } - //A line waiting for the click putting a core down has the - //first call on Escape: it lets that core go back to being - //a free one and lets the mouse go, while everything else - //-- the selection included -- stays as it is, so that - //Escape keeps doing one thing at a time. - if (CablePart::cancelPlacing()) { - e->accept(); - return; } if (m_diagram && !m_diagram->selectedItems().isEmpty()) { m_diagram->clearSelection(); @@ -2160,67 +1995,12 @@ QList DiagramView::contextMenuActions() const return list; } -/** - @brief DiagramView::cableCoreMenu - The menu of one single core, opened where he right-clicked: taking - that core off its line, which is the exact opposite of the menu the - free cores offer in the panel, of putting one down on a line. - - Only the colour labels count -- a right-click anywhere else on the - line is still about the line itself, and opens the folio's own menu - instead. Nothing is changed until he picks the entry, so opening the - menu and clicking it away leaves everything standing as it was. - @param menu_pos where he right-clicked, in view coordinates - @param global_pos the same place, in global coordinates - @return true when that menu was the one to open -*/ -bool DiagramView::cableCoreMenu(const QPoint &menu_pos, const QPoint &global_pos) -{ - if (!m_diagram || m_diagram->isReadOnly()) return false; - - const QPointF scene_pos = mapToScene(menu_pos); - auto *part = qgraphicsitem_cast( - m_diagram->itemAt(scene_pos, transform())); - if (!part) return false; - - const int core = part->coreAt(scene_pos); - if (core < 0) return false; - - //The line he takes a core off is the one on the sheet, so it is - //also the one the panel beside it edits from now on. - if (!part->isSelected()) { - m_diagram->clearSelection(); - part->setSelected(true); - } - - QMenu menu(this); - QAction *take = menu.addAction(tr("Remove this core from the line")); - take->setIcon(QET::Icons::Cable); - - if (menu.exec(global_pos) == take) { - part->takeCoreOffLine(core); - } - return true; -} - /** @brief DiagramView::contextMenuEvent @param e */ void DiagramView::contextMenuEvent(QContextMenuEvent *e) { - //A line which waits for the click putting one of its cores down - //takes this very event: the right button ends that wait and no - //menu opens out of a click meant to say "no". Being asked here - //first is what makes it work for every way a menu can come up -- - //the gesture ring's own menu, the platform's (with the press on - //X11, with the release on Windows) and the one raised from the - //keyboard -- so the menu never has to be closed after the fact. - if (CablePart::cancelPlacing()) { - e->accept(); - return; - } - QPoint menu_pos = e->pos(); QPoint menu_global_pos = e->globalPos(); @@ -2269,16 +2049,6 @@ void DiagramView::contextMenuEvent(QContextMenuEvent *e) } else { - //A colour label standing on a line asks about that one core - //rather than about the whole sheet: the very opposite of the - //menu the free cores offer, of putting one down on a line. - //It comes first, so that a core drawn over something else - //still belongs to its own line. - if (cableCoreMenu(menu_pos, menu_global_pos)) { - e->accept(); - return; - } - QGraphicsView::contextMenuEvent(e); if(e->isAccepted()) return; diff --git a/sources/diagramview.h b/sources/diagramview.h index 4a3601ed7..96e5f3820 100644 --- a/sources/diagramview.h +++ b/sources/diagramview.h @@ -143,20 +143,6 @@ class DiagramView : public PaletteGraphicsView private: void handleElementDrop(QDropEvent *); void updateFolioReportMenu(); - /** - The menu of one single core: the right button coming down - on a colour label which stands on a line asks about that - core rather than about the whole folio -- taking it off the - line, which is the exact opposite of the menu the free - cores offer, of putting one down on a line. - - Nothing opens when the point carries no colour label: the - folio's own menu is then still the right one. - @param menu_pos where he right-clicked, in view coordinates - @param global_pos the same place, in global coordinates - @return true when that menu was the one to open - */ - bool cableCoreMenu(const QPoint &menu_pos, const QPoint &global_pos); void handleTitleBlockDrop(QDropEvent *); void handleTextDrop(QDropEvent *); void handleImageFilesDrop(QDropEvent *); diff --git a/sources/elementsmover.cpp b/sources/elementsmover.cpp index 947f1e015..c49ee9579 100644 --- a/sources/elementsmover.cpp +++ b/sources/elementsmover.cpp @@ -19,8 +19,6 @@ #include "qetproject.h" #include "autobreakconductor.h" #include "conductorautonumerotation.h" -#include "cable/cablepart.h" -#include "cable/editcablecommand.h" #include "diagram.h" #include "itemgroups.h" #include "qetgraphicsitem/conductor.h" @@ -118,54 +116,6 @@ int ElementsMover::beginMovement(Diagram *diagram, QGraphicsItem *driver_item) if (!m_moved_content.count()) return(-1); - //Steps left over by a gesture which never reached its end are - //never worth keeping: whatever starts here starts from nothing. - qDeleteAll(m_extra_commands); - m_extra_commands.clear(); - - //The cable lines he marked together with the elements come along - //too: a selection of elements and lines is one thing to move. - //When the finger is on a line itself, that line carries its own - //kind -- every line of the selection is begun by the one being - //dragged (see CablePart) -- so there is nothing to collect here. - m_moved_cables.clear(); - if (!qgraphicsitem_cast(m_movement_driver)) - { - for (QGraphicsItem *item : diagram->selectedItems()) - { - auto *cable = qgraphicsitem_cast(item); - if (!cable || !cable->cable()) continue; - - cable->beginLineGesture(); - m_moved_cables.append(cable); - } - } - - //Where every one of them stands at this moment: calling the - //movement off puts them all back here, recorded rather than - //worked out from the movement, since every step of that movement - //was snapped to the grid on its way and need not land on the - //same place coming back. - m_start_positions.clear(); - typedef DiagramContent dc; - for (auto &qgi : m_moved_content.items(dc::Elements - | dc::TextFields - | dc::Images - | dc::Shapes - | dc::ElementTextFields - | dc::TextGroup)) - { - m_start_positions.insert(qgi, qgi->pos()); - } - for (auto *conductor : m_moved_content.m_conductors_to_move) - { - if (conductor && conductor->textItem() - && conductor->textItem()->wasMovedByUser()) { - m_start_positions.insert(conductor->textItem(), - conductor->textItem()->pos()); - } - } - /* At this point, we've got all info to manage movement. * There is now a move in progress */ m_movement_running = true; @@ -227,25 +177,9 @@ void ElementsMover::continueMovement(const QPointF &movement) conductor->updatePath(); } - //The cable lines he marked together with the elements come along - //by the very same step: one gesture moves the whole selection - //instead of the lines standing still while the elements go, or - //the other way round. They are drawn where the gesture brings - //them, exactly as if the finger had been on the line itself, and - //settled once at the end (see endMovement). - for (CablePart *cable : std::as_const(m_moved_cables)) - { - if (cable) cable->continueLineGesture(movement); - } - if (m_status_bar && m_movement_driver) { - //A line does not stand at a point of its own -- it is drawn - //straight on the sheet -- so its own end is what is worth - //showing rather than its origin, which never moves. - auto *cable = qgraphicsitem_cast(m_movement_driver); - const auto point_{cable ? cable->firstPoint() - : m_movement_driver->scenePos()}; + const auto point_{m_movement_driver->scenePos()}; m_status_bar->showMessage(QString("x %1 : y %2").arg(QString::number(point_.x()), QString::number(point_.y()))); } } @@ -261,30 +195,6 @@ void ElementsMover::endMovement() // A movement must be inited if (!m_movement_running) return; - //The lines he marked together with the elements settle first: - //they may have to be asked something before they can rest, and - //their answer decides whether the whole gesture is written at - //all -- one of them calling it off calls off everything, the - //elements included, and there is nothing to undo because nothing - //at all has been written. - QList cable_steps; - for (CablePart *cable : std::as_const(m_moved_cables)) - { - if (!cable) continue; - if (!cable->settleLineGesture(cable_steps)) - { - qDeleteAll(cable_steps); - cable_steps.clear(); - cancelMovement(); - return; - } - } - for (CablePart *cable : std::as_const(m_moved_cables)) - { - if (cable) cable->doneLineGesture(); - } - m_moved_cables.clear(); - //empty command to be used has parent of commands below QUndoCommand *undo_object{new QUndoCommand()}; @@ -360,118 +270,18 @@ void ElementsMover::endMovement() } } - //Add undo_object if have child -- or, when the lines of this - //selection brought their own undo over (see addExtraCommands), - //one single step for the whole gesture: taking it back has to - //take all of it back, or half the selection would stay where - //the gesture left it. - QList steps = m_extra_commands; - m_extra_commands.clear(); - steps.append(cable_steps); - - if (steps.isEmpty()) - { - if (undo_object->childCount()) - m_diagram->undoStack().push(undo_object); - else - delete undo_object; - } + //Add undo_object if have child + if (undo_object->childCount()) + m_diagram->undoStack().push(undo_object); else - { - QString text = undo_object->text(); - if (text.isEmpty()) - text = steps.first()->text(); - - if (undo_object->childCount()) - steps.prepend(undo_object); - else - delete undo_object; - - if (steps.size() == 1) - m_diagram->undoStack().push(steps.takeFirst()); - else - m_diagram->undoStack().push(new BatchCommand(steps, text)); - } + delete undo_object; // There is no movement in progress now m_movement_running = false; m_moved_content.clear(); - m_moved_cables.clear(); - m_start_positions.clear(); //Keep saying why a held group did not move if (m_status_bar && !m_driver_held) { m_status_bar->clearMessage(); } } - -/** - @brief ElementsMover::addExtraCommands - Take over undo steps of items this mover does not move itself -- - the cable lines the user dragged himself -- so that the end of the - movement pushes them together with its own: one gesture, one step - in the history, whatever the selection is made of. - - They are thrown away by cancelMovement() if the gesture is called - off, and pushed by endMovement() otherwise. - @param steps the commands, given away with them -*/ -void ElementsMover::addExtraCommands(const QList &steps) -{ - for (QUndoCommand *step : steps) - { - if (step) m_extra_commands.append(step); - } -} - -/** - @brief ElementsMover::cancelMovement - Call the whole movement off without writing anything: every element - goes back to the place the gesture found it, every line of the - selection puts itself back where it stood and the undo steps which - were waiting for this movement are thrown away. - - One gesture is written as a whole or not at all, which is what a - "Annuler" answer to a question asked while the lines settle means. -*/ -void ElementsMover::cancelMovement() -{ - if (!m_movement_running) return; - - //Back to the places the gesture found them in: the recorded - //ones rather than the movement taken back, since every step of - //that movement was snapped to the grid on its way and coming - //back by the same amount need not land on the same place. - for (auto it = m_start_positions.constBegin(); - it != m_start_positions.constEnd(); ++it) - { - if (it.key()) it.key()->setPos(it.value()); - } - m_start_positions.clear(); - - for (auto *conductor : m_moved_content.m_conductors_to_move) - { - if (conductor) conductor->updatePath(); - } - - //Every line of the selection puts itself back where it stood - for (CablePart *cable : std::as_const(m_moved_cables)) - { - if (cable) cable->abortLineGesture(); - } - m_moved_cables.clear(); - - //Steps handed over by a line he dragged himself go with - //everything else: nothing has been written, so there is nothing - //to take back. - qDeleteAll(m_extra_commands); - m_extra_commands.clear(); - - m_current_movement -= m_current_movement; - m_movement_running = false; - m_moved_content.clear(); - - if (m_status_bar && !m_driver_held) { - m_status_bar->clearMessage(); - } -} diff --git a/sources/elementsmover.h b/sources/elementsmover.h index 5a210d905..2074a88eb 100644 --- a/sources/elementsmover.h +++ b/sources/elementsmover.h @@ -18,16 +18,13 @@ #ifndef ELEMENTS_MOVER_H #define ELEMENTS_MOVER_H -#include #include #include #include "diagramcontent.h" -class CablePart; class ConductorTextItem; class Diagram; class QStatusBar; -class QUndoCommand; /** This class manages the interactive movement of different items (elements, @@ -56,27 +53,6 @@ class ElementsMover { void continueMovement(const QPointF &); void endMovement(); bool holds(const QGraphicsItem *item) const; - /** - Steps of items this mover does not move itself -- the - cable lines the user dragged himself, which know how to - carry their own colour labels along -- handed over so - that the end of this movement pushes them together with - its own: one gesture, one undo step, whatever the - selection is made of. - @param steps the commands, given away with them - */ - void addExtraCommands(const QList &steps); - /** - Call the whole movement off without writing anything: - every element goes back to the place the gesture found it - and every line of the selection puts itself back too (see - CablePart), so nothing at all has to be undone. - - Answering "Annuler" to a question asked while the lines - settle is what calls it: one gesture is written as a - whole or not at all. - */ - void cancelMovement(); // attributes private: @@ -87,17 +63,6 @@ class ElementsMover { bool m_driver_held{false}; DiagramContent m_moved_content; QPointer m_status_bar; - ///The cable lines marked together with the elements, moved - ///by this mover from one gesture to the next (see - ///CablePart::beginLineGesture) -- empty whenever the finger - ///is on a line itself, since a line then carries its own - QList m_moved_cables; - ///Steps handed over by a line the user dragged himself, to - ///be pushed together with the rest of the movement - QList m_extra_commands; - ///Where every item of the movement stood when it began, so - ///that calling it off puts them all back exactly there - QHash m_start_positions; }; #endif diff --git a/sources/factory/propertieseditorfactory.cpp b/sources/factory/propertieseditorfactory.cpp index cc7ae1dc9..ba8b6acb3 100644 --- a/sources/factory/propertieseditorfactory.cpp +++ b/sources/factory/propertieseditorfactory.cpp @@ -18,10 +18,6 @@ #include "propertieseditorfactory.h" #include "../PropertiesEditor/propertieseditorwidget.h" -#include "../cable/cablepart.h" -#include "../cable/cablepropertieseditorwidget.h" -#include "../cablelist/cablelistmodel.h" -#include "../cablelist/cablelistmodelpropertieswidget.h" #include "../qetgraphicsitem/ViewItem/projectdbmodel.h" #include "../qetgraphicsitem/ViewItem/qetgraphicstableitem.h" #include "../qetgraphicsitem/ViewItem/ui/graphicstablepropertieseditor.h" @@ -59,7 +55,7 @@ PropertiesEditorWidget *PropertiesEditorFactory::propertiesEditor( PropertiesEditorWidget *editor, QWidget *parent) { - if (auto m = dynamic_cast(model)) + if (auto m = static_cast(model)) { if (editor && editor->metaObject()->className() @@ -70,17 +66,6 @@ PropertiesEditorWidget *PropertiesEditorFactory::propertiesEditor( } return new ProjectDBModelPropertiesWidget(m, parent); } - if (auto m = dynamic_cast(model)) - { - if (editor && - editor->metaObject()->className() - == CableListModelPropertiesWidget::staticMetaObject.className()) - { - static_cast(editor)->setModel(m); - return editor; - } - return new CableListModelPropertiesWidget(m, parent); - } return nullptr; } @@ -262,27 +247,6 @@ PropertiesEditorWidget *PropertiesEditorFactory::propertiesEditor( } return new GraphicsTablePropertiesEditor(table, parent); } - case CablePart::Type: //1012 - { - if (count_ > 1) { - return nullptr; - } - auto part = static_cast(item); - //The fields belong to the cable, never to one of its - //lines: whichever line is picked, it is the cable which - //is edited. - Cable *cable = part->cable(); - if (!cable) { - return nullptr; - } - - if (class_name == CablePropertiesEditorWidget::staticMetaObject.className()) - { - static_cast(editor)->setCable(cable, part); - return editor; - } - return new CablePropertiesEditorWidget(cable, part, parent); - } default: return nullptr; } diff --git a/sources/factory/qetgraphicstablefactory.cpp b/sources/factory/qetgraphicstablefactory.cpp index 03f44e717..44b7bc716 100644 --- a/sources/factory/qetgraphicstablefactory.cpp +++ b/sources/factory/qetgraphicstablefactory.cpp @@ -17,8 +17,6 @@ */ #include "qetgraphicstablefactory.h" -#include "../cablelist/cablelistmodel.h" -#include "../cablelist/cablequerywidget.h" #include "../dataBase/ui/elementquerywidget.h" #include "../dataBase/ui/summaryquerywidget.h" #include "../diagram.h" @@ -73,26 +71,6 @@ void QetGraphicsTableFactory::createAndAddSummary(Diagram *diagram) } } -/** - @brief QetGraphicsTableFactory::createAndAddCableList - Open a dialog for ask user the config of the table, - create a cable list table and add it to diagram - @param diagram -*/ -void QetGraphicsTableFactory::createAndAddCableList(Diagram *diagram) -{ - QScopedPointer d( - new AddTableDialog( - new CableQueryWidget(), - diagram->views().first())); - d->setWindowTitle(QObject::tr("Add a cable list")); - d->setTableName(QObject::tr("Cable list")); - - if (d->exec()) { - create(diagram, d.data()); - } -} - void QetGraphicsTableFactory::create(Diagram *diagram, AddTableDialog *dialog) { auto table_ = newTable(diagram, dialog); @@ -146,33 +124,20 @@ QetGraphicsTableItem *QetGraphicsTableFactory::newTable(Diagram *diagram, AddTab if (!previous_table) { - QAbstractTableModel *model = nullptr; + QString identifier_; + QString query_; if (auto query_widget = dynamic_cast(dialog->contentWidget())) { - auto *db_model = new ProjectDBModel(diagram->project(), diagram->project()); - db_model->setIdentifier(query_widget->modelIdentifier()); - db_model->setQuery(query_widget->queryStr()); - model = db_model; + identifier_ = query_widget->modelIdentifier(); + query_ = query_widget->queryStr(); } else if (auto query_widget = dynamic_cast(dialog->contentWidget())) { - auto *db_model = new ProjectDBModel(diagram->project(), diagram->project()); - db_model->setIdentifier(query_widget->modelIdentifier()); - db_model->setQuery(query_widget->queryStr()); - model = db_model; - } else if (auto query_widget = dynamic_cast(dialog->contentWidget())) { - auto *cable_model = new CableListModel(diagram->project(), diagram->project()); - cable_model->setIdentifier(CableQueryWidget::modelIdentifier()); - //No entries chosen is no mistake to correct: an empty - //set of columns makes an empty table, the same grey - //frame a nomenclature table makes without entries - cable_model->setFields(query_widget->selectedKeys()); - model = cable_model; - } else { - //A content widget this factory does not know leaves an - //empty database model behind, which is what it has - //always done - model = new ProjectDBModel(diagram->project(), diagram->project()); + identifier_ = query_widget->modelIdentifier(); + query_ = query_widget->queryStr(); } + auto model = new ProjectDBModel(diagram->project(), diagram->project()); + model->setIdentifier(identifier_); + model->setQuery(query_); model->setData(model->index(0,0), int(dialog->tableAlignment()), Qt::TextAlignmentRole); model->setData(model->index(0,0), dialog->tableFont(), Qt::FontRole); model->setData(model->index(0,0), QETUtils::marginsToString(dialog->headerMargins()), Qt::UserRole+1); diff --git a/sources/factory/qetgraphicstablefactory.h b/sources/factory/qetgraphicstablefactory.h index 5c3118310..0a93b0b5b 100644 --- a/sources/factory/qetgraphicstablefactory.h +++ b/sources/factory/qetgraphicstablefactory.h @@ -32,7 +32,6 @@ class QetGraphicsTableFactory static void createAndAddNomenclature(Diagram *diagram); static void createAndAddSummary(Diagram *diagram); - static void createAndAddCableList(Diagram *diagram); // Public so a caller that has already built and configured an // AddTableDialog itself (never shown or exec'd -- the two // methods above always exec() one, which the scripting API diff --git a/sources/pdf_links.cpp b/sources/pdf_links.cpp index 4fc9fc357..af892836a 100644 --- a/sources/pdf_links.cpp +++ b/sources/pdf_links.cpp @@ -17,7 +17,6 @@ */ #include "pdf_links.h" -#include "cable/cablepart.h" #include "diagram.h" #include "qetgraphicsitem/crossrefitem.h" #include "qetgraphicsitem/dynamicelementtextitem.h" @@ -54,13 +53,10 @@ void injectCrossRefLinks(QPdfEngine *engine, Diagram *diagram, const QRectF &pageBounds = geom.pageBounds; // Compute, in PDF points on its OWN page, the rectangle to frame for a - // target (used as a /FitR destination so the link zooms onto it). - // The target is given as a rectangle in the scene of @p dg, since what - // is pointed at is not always an element: a cable section is a plain - // drawn line somewhere on another folio. - auto frameRectPdf = [&](Diagram *dg, const QRectF &targetScene) -> QRectF { - if (!dg || targetScene.width() <= 0.0 || targetScene.height() <= 0.0) - return QRectF(); + // target element (used as a /FitR destination so the link zooms onto it). + auto destRectPdf = [&](Element *tgt) -> QRectF { + Diagram *dg = tgt ? tgt->diagram() : nullptr; + if (!dg) return QRectF(); const QRectF srcT = geom.sourceRectOf(dg); if (srcT.width() <= 0.0 || srcT.height() <= 0.0) return QRectF(); const qreal sT = qMin(target.width() / srcT.width(), @@ -70,20 +66,20 @@ void injectCrossRefLinks(QPdfEngine *engine, Diagram *diagram, fitT.scale(sT, sT); fitT.translate(-srcT.x(), -srcT.y()); - // Frame the target with a little context, and enforce a minimum + QRectF elemScene = tgt->mapRectToScene(tgt->boundingRect()); + // Frame the element with a little context, and enforce a minimum // framed size so tiny contacts don't zoom in extremely. - QRectF framed = targetScene; const qreal pad = 25.0; - framed.adjust(-pad, -pad, pad, pad); + elemScene.adjust(-pad, -pad, pad, pad); const qreal minSide = 160.0; - if (framed.width() < minSide) - framed.adjust(-(minSide - framed.width()) / 2.0, 0, - (minSide - framed.width()) / 2.0, 0); - if (framed.height() < minSide) - framed.adjust(0, -(minSide - framed.height()) / 2.0, - 0, (minSide - framed.height()) / 2.0); + if (elemScene.width() < minSide) + elemScene.adjust(-(minSide - elemScene.width()) / 2.0, 0, + (minSide - elemScene.width()) / 2.0, 0); + if (elemScene.height() < minSide) + elemScene.adjust(0, -(minSide - elemScene.height()) / 2.0, + 0, (minSide - elemScene.height()) / 2.0); - const QRectF devT = fitT.mapRect(framed); + const QRectF devT = fitT.mapRect(elemScene); const QPointF a = geom.devToPdf(devT.topLeft()); const QPointF b = geom.devToPdf(devT.bottomRight()); return QRectF(QPointF(qMin(a.x(), b.x()), qMin(a.y(), b.y())), @@ -98,18 +94,15 @@ void injectCrossRefLinks(QPdfEngine *engine, Diagram *diagram, struct Link { QRectF rect; QUrl url; }; QList links; - // One clickable link: @p sceneRect on THIS folio leads to - // @p targetDiagram, framed around @p targetScene over there. - auto injectLinkTo = [&](const QRectF &sceneRect, Diagram *targetDiagram, - const QRectF &targetScene) { - if (!targetDiagram) return; - const int targetPage = pageMap.value(targetDiagram, -1); + auto injectLink = [&](const QRectF &sceneRect, Element *targetElmt) { + if (!targetElmt || !targetElmt->diagram()) return; + const int targetPage = pageMap.value(targetElmt->diagram(), -1); if (targetPage < 1) return; const QRectF devRect = fit.mapRect(sceneRect); if (!devRect.isValid() || !pageBounds.intersects(devRect)) return; QString frag = QString("page=%1").arg(targetPage); - const QRectF d = frameRectPdf(targetDiagram, targetScene); // /FitR L_B_R_T + const QRectF d = destRectPdf(targetElmt); // /FitR L_B_R_T if (d.isValid()) frag += QString("&fitr=%1_%2_%3_%4") .arg(qRound(d.left())).arg(qRound(d.top())) @@ -120,12 +113,6 @@ void injectCrossRefLinks(QPdfEngine *engine, Diagram *diagram, links.append({devRect, url}); }; - auto injectLink = [&](const QRectF &sceneRect, Element *targetElmt) { - if (!targetElmt || !targetElmt->diagram()) return; - injectLinkTo(sceneRect, targetElmt->diagram(), - targetElmt->mapRectToScene(targetElmt->boundingRect())); - }; - for (auto *item : diagram->items()) { // --- CrossRefItem links --- @@ -141,15 +128,6 @@ void injectCrossRefLinks(QPdfEngine *engine, Diagram *diagram, continue; } - // --- Cable cross-reference links (the lines under a cable which - // name the folio it goes on to) --- - if (auto *cable = dynamic_cast(item)) { - for (const CablePart::PdfRef &ref : cable->pdfRefs()) { - injectLinkTo(ref.here, ref.diagram, ref.there); - } - continue; - } - // --- Folio report links (DynamicElementTextItem) --- if (auto *deti = dynamic_cast(item)) { Element *parent = deti->parentElement(); diff --git a/sources/properties/xrefproperties.cpp b/sources/properties/xrefproperties.cpp index 4fbfbc4ea..e6ed7cd53 100644 --- a/sources/properties/xrefproperties.cpp +++ b/sources/properties/xrefproperties.cpp @@ -67,7 +67,6 @@ XRefProperties::XRefProperties() m_prefix_keys << "power" << "delay" << "switch"; m_master_label = "%f-%l%c"; m_slave_label = "(%f-%l%c)"; - m_font = QString(); m_offset = 0; m_slave_offset = 0; m_xref_pos = Qt::AlignBottom; @@ -98,7 +97,6 @@ void XRefProperties::toSettings(QSettings &settings, settings.setValue(prefix % "master_label", master_label); QString slave_label = m_slave_label; settings.setValue(prefix % "slave_label", slave_label); - settings.setValue(prefix % "font", m_font); QMetaEnum var = QMetaEnum::fromType(); @@ -130,7 +128,6 @@ void XRefProperties::fromSettings(const QSettings &settings, m_slave_offset = settings.value(prefix % "slave_offset", "0").toInt(); m_master_label = settings.value(prefix % "master_label", "%f-%l%c").toString(); m_slave_label = settings.value(prefix % "slave_label", "(%f-%l%c)").toString(); - m_font = settings.value(prefix % "font").toString(); m_xref_pos = xrefPosFromKey(settings.value(prefix % "xrefpos").toString()); @@ -172,14 +169,6 @@ QDomElement XRefProperties::toXml(QDomDocument &xml_document) const xml_element.setAttribute("master_label", master_label); QString slave_label = m_slave_label; xml_element.setAttribute("slave_label", slave_label); - //Only the cable cross-reference has a font of its own, and only - //it ever sets one. Writing the attribute unconditionally put - //font="" on every cross-reference type of every project the - //program saved; a file which does not hold the attribute reads - //back as no font, which is what all of them are. - if (!m_font.isEmpty()) { - xml_element.setAttribute("font", m_font); - } foreach (QString key, m_prefix.keys()) { xml_element.setAttribute(key % "prefix", m_prefix.value(key)); } @@ -208,7 +197,6 @@ bool XRefProperties::fromXml(const QDomElement &xml_element) { m_slave_offset = xml_element.attribute("slave_offset", "0").toInt(); m_master_label = xml_element.attribute("master_label", "%f-%l%c"); m_slave_label = xml_element.attribute("slave_label","(%f-%l%c)"); - m_font = xml_element.attribute("font"); foreach (QString key, m_prefix_keys) { m_prefix.insert(key, xml_element.attribute(key % "prefix")); } @@ -218,17 +206,15 @@ bool XRefProperties::fromXml(const QDomElement &xml_element) { /** @brief XRefProperties::defaultProperties @return the default properties stored in the setting file - One entry per kind of element which shows a cross reference, stored - under its own key: "coil", "protection", "commutator", "plc" -- and - "cable", which is not an element at all: it is the reference a cable - writes in the label of each of its sections when the cable runs on - several folios. + For the xref, there are 2 properties. + For coil, stored with the string "coil" in the returned QHash. + For protection, stored with the string "protection" in the returned QHash. */ QHash XRefProperties::defaultProperties() { QHash hash; QStringList keys; - keys << "coil" << "protection" << "commutator" << "plc" << "cable"; + keys << "coil" << "protection" << "commutator" << "plc"; QSettings settings; @@ -237,13 +223,6 @@ QHash XRefProperties::defaultProperties() XRefProperties properties; QString str("diagrameditor/defaultxref"); properties.fromSettings(settings, str += key); - - //A cable takes the very same default text as a relay master - //and the other types, "%f-%l%c": what it names is then the - //other section of the cable -- the folio it stands on and - //the place it stands at there, which is what an element - //points at as well. No default of its own, so that setting - //the five of them apart needs no thought at all. hash.insert(key, properties); } @@ -262,8 +241,7 @@ bool XRefProperties::operator ==(const XRefProperties &xrp) const{ && m_offset == xrp.m_offset && m_slave_offset== xrp.m_slave_offset && m_xref_pos == xrp.m_xref_pos - && m_slave_label == xrp.m_slave_label - && m_font == xrp.m_font); + && m_slave_label == xrp.m_slave_label); } bool XRefProperties::operator !=(const XRefProperties &xrp) const diff --git a/sources/properties/xrefproperties.h b/sources/properties/xrefproperties.h index 50fa569f8..08dfeca02 100644 --- a/sources/properties/xrefproperties.h +++ b/sources/properties/xrefproperties.h @@ -80,14 +80,6 @@ class XRefProperties : public PropertiesInterface void setMasterLabel (const QString master) {m_master_label = master;} QString masterLabel () const {return m_master_label;} - /** - The font the reference of a cable is written with, as the - settings describe it -- empty when he has not set one, which - leaves the reference with the font of the cable texts. - */ - void setFont (const QString &font) {m_font = font;} - QString font () const {return m_font;} - void setSlaveLabel(const QString slave) {m_slave_label = slave;} QString slaveLabel () const {return m_slave_label;} @@ -111,7 +103,6 @@ class XRefProperties : public PropertiesInterface QStringList m_prefix_keys; QString m_master_label; QString m_slave_label; - QString m_font; int m_offset; int m_slave_offset; QString m_key; diff --git a/sources/qetapp.cpp b/sources/qetapp.cpp index 39a6009b2..9fddcead5 100644 --- a/sources/qetapp.cpp +++ b/sources/qetapp.cpp @@ -1536,97 +1536,6 @@ QFont QETApp::indiTextsItemFont(qreal size) return(font_); } -/** - @brief QETApp::cableTextsFont - @return the font the texts of a cable (designation, type, - installation, location, length) are written with when the cable - itself has been given no font of its own. - - That is what the preferences offer on the "Textes" page, and -- when - nothing has been chosen there yet -- the font the drawing writes its - own texts with, a shade smaller: the way every cable has always been - drawn, so that opening the preferences changes nothing until he - picks something. -*/ -QFont QETApp::cableTextsFont() -{ - QSettings settings; - if (settings.contains("cable-management/cable-text-font")) - { - QFont font; - if (QETUtils::fontFromString( - font, settings.value("cable-management/cable-text-font").toString())) { - return font; - } - } - - QFont font = QApplication::font(); - font.setPointSizeF(font.pointSizeF() * 0.9); - return font; -} - -/** - @brief QETApp::cableCoreFont - @return the font the colour labels of the cores are written with - when the cable itself has been given no font of its own. - - That is what the preferences offer on the "Textes" page; until he - picks something there, the colours follow the texts of the cable, - so one setting alone already resizes the whole label of a cable. -*/ -QFont QETApp::cableCoreFont() -{ - QSettings settings; - if (settings.contains("cable-management/cable-core-font")) - { - QFont font; - if (QETUtils::fontFromString( - font, settings.value("cable-management/cable-core-font").toString())) { - return font; - } - } - return cableTextsFont(); -} - -/** - @brief QETApp::cableTextAlignment - @return how the texts of a cable line up at the left end of their - line when the cable itself asks for nothing: what the preferences - offer on the "Textes" page, and -- until he has picked something - there -- the way every text has always stood, with its right end at - the place where the line begins. -*/ -Qt::Alignment QETApp::cableTextAlignment() -{ - const QSettings settings; - const QString name = settings.value("cable-management/cable-align").toString(); - if (name == QLatin1String("left")) { - return Qt::AlignLeft; - } - if (name == QLatin1String("center")) { - return Qt::AlignHCenter; - } - return Qt::AlignRight; -} - -/** - @brief QETApp::setCableTextAlignment - Keep what the preferences chose as the way every cable lines up its - texts, for the cables which say nothing about it themselves. - @param alignment Qt::AlignRight, Qt::AlignHCenter or Qt::AlignLeft -*/ -void QETApp::setCableTextAlignment(Qt::Alignment alignment) -{ - QString name = QLatin1String("right"); - if (alignment == Qt::AlignLeft) { - name = QLatin1String("left"); - } else if (alignment == Qt::AlignHCenter) { - name = QLatin1String("center"); - } - QSettings settings; - settings.setValue("cable-management/cable-align", name); -} - /** @brief QETApp::diagramEditors @return schema editors diff --git a/sources/qetapp.h b/sources/qetapp.h index 7810547c8..234db5fa5 100644 --- a/sources/qetapp.h +++ b/sources/qetapp.h @@ -175,24 +175,6 @@ class QETApp : public QObject static QFont diagramTextsItemFont(qreal = -1.0); static QFont dynamicTextsItemFont(qreal = -1.0); static QFont indiTextsItemFont (qreal = -1.0); - /** - @brief The font the texts of a cable (designation, type, - installation, location, length) are written with when the - cable itself asks for none. - */ - static QFont cableTextsFont(); - /** - @brief The font the colour labels of the cores of a cable - are written with when the cable itself asks for none. - */ - static QFont cableCoreFont(); - /** - @brief How the texts of a cable line up at the left end of - their line when the cable asks for nothing itself: right, - centred or left of where the line begins. - */ - static Qt::Alignment cableTextAlignment(); - static void setCableTextAlignment(Qt::Alignment alignment); static QETDiagramEditor *diagramEditorForFile(const QString &); static QETDiagramEditor *diagramEditorAncestorOf (const QWidget *child); static QList diagramEditors(); diff --git a/sources/qetdiagrameditor.cpp b/sources/qetdiagrameditor.cpp index a25842303..02538c4dd 100644 --- a/sources/qetdiagrameditor.cpp +++ b/sources/qetdiagrameditor.cpp @@ -46,11 +46,8 @@ #endif #include "diagramevent/diagrameventaddshape.h" #include "diagramevent/diagrameventaddpath.h" -#include "diagramevent/diagrameventaddcable.h" #include "diagramevent/diagrameventfillet.h" #include "diagramevent/diagrameventaddtext.h" -#include "cable/cablepart.h" -#include "cable/editcablecommand.h" #include "diagramevent/diagrameventaddpaste.h" #include "diagramview.h" #include "elementspanelwidget.h" @@ -58,7 +55,6 @@ #include "factory/qetgraphicstablefactory.h" #include "print/projectprintwindow.h" #include "project/projectpropertieshandler.h" -#include "ui/projectpropertiesdialog.h" #include "projectview.h" #include "qetproject.h" #include "qetgraphicsitem/ViewItem/qetgraphicstableitem.h" @@ -69,7 +65,6 @@ #include "recentfiles.h" #include "textgrid.h" #include "shortcutmanager.h" -#include "cablelist/cableexportdialog.h" #include "ui/bomexportdialog.h" #include "ui/conductorcolortoolbutton.h" #include "ui/diagrambgcolorbutton.h" @@ -95,7 +90,6 @@ #include "wiringlistexport.h" #include "ui/wiringlistdialog.h" #include "ui/terminalnumberingdialog.h" -#include "ui/cablenumberingdialog.h" #include "toolbarsettings.h" #include #include @@ -750,27 +744,6 @@ void QETDiagramEditor::setUpActions() ShortcutManager::instance().registerAction(m_project_terminalBloc, "diagrameditor.terminal_strip_plugin", tr("Diagram editors"), QKeySequence()); connect(m_project_terminalBloc, &QAction::triggered, this, &QETDiagramEditor::generateTerminalBlock); - //Add a cable list - m_add_cable_list = new QAction(QET::Icons::TableOfContent, tr("Add a cable list"), this); - connect(m_add_cable_list, &QAction::triggered, this, [this]() { - if (this->currentDiagramView()) { - QetGraphicsTableFactory::createAndAddCableList(this->currentDiagramView()->diagram()); - } - }); - - //Export the whole cable list to CSV, every column of the list - m_export_cable_list = new QAction(QET::Icons::DocumentSpreadsheet, tr("Export the cable list to CSV format"), this); - connect(m_export_cable_list, &QAction::triggered, [this]() { - QETProject *project = this->currentProject(); - if (!project) { - return; - } - //The same dialog the material list is exported with, only - //with the columns of the cable list - CableExportDialog dialog(project, this); - dialog.exec(); - }); - //Export conductor num to csv m_project_export_conductor_num = new QAction(QET::Icons::DocumentSpreadsheet, tr("Export the list of names of wires"), this); ShortcutManager::instance().registerAction(m_project_export_conductor_num, "diagrameditor.export_conductor_names", tr("Diagram editors"), QKeySequence()); @@ -811,14 +784,6 @@ void QETDiagramEditor::setUpActions() ShortcutManager::instance().registerAction(m_terminal_numbering, "diagrameditor.terminal_numbering", tr("Diagram editors"), QKeySequence()); connect(m_terminal_numbering, &QAction::triggered, this, &QETDiagramEditor::slot_terminalNumbering); - // Cable numbering: the rule the project numbers its cables with, and - // the numbering of every cable of that project at once - m_cable_numbering = new QAction(QET::Icons::AutoNum, tr("Cable numbering"), this); - m_cable_numbering->setStatusTip( - tr("Shows the cable numbering rule of the project and " - "renumbers its cables with it")); - connect(m_cable_numbering, &QAction::triggered, this, &QETDiagramEditor::slot_cableNumbering); - // Reload element drawings from their current definition (bugtracker #802) m_reload_element_drawings = new QAction(QET::Icons::ViewRefresh, tr("Reload element drawings"), this); ShortcutManager::instance().registerAction(m_reload_element_drawings, "diagrameditor.reload_element_drawings", tr("Diagram editors"), QKeySequence()); @@ -1278,9 +1243,6 @@ void QETDiagramEditor::setUpActions() QAction *add_fillet = m_add_item_actions_group.addAction(QET::Icons::DrawFillet, tr("Add a fillet")); QAction *add_terminal_strip = m_add_item_actions_group.addAction(QET::Icons::TerminalStrip, tr("Add a terminal plan")); QAction *add_generic_device = m_add_item_actions_group.addAction(QET::Icons::GenericDevice, tr("Add a generic device…")); - //Not an element of the drawing but a line drawn across the - //conductors: what it crosses becomes the cores of a cable. - m_add_cable = m_add_item_actions_group.addAction(QET::Icons::Cable, tr("Draw a cable")); add_text ->setStatusTip(tr("Adds a text field to the current sheet")); add_image ->setStatusTip(tr("Add an image to the current sheet")); @@ -1296,7 +1258,6 @@ void QETDiagramEditor::setUpActions() add_fillet ->setStatusTip(tr("Rounds the corner between two lines of the current sheet")); add_terminal_strip->setStatusTip(tr("Adds a terminal plan to the current sheet")); add_generic_device->setStatusTip(tr("Makes a box symbol with terminals on any side and places it on the current sheet")); - m_add_cable ->setStatusTip(tr("Draws the main line of a cable: every conductor crossed becomes one of its cores")); add_text ->setData(QStringLiteral("text")); add_image ->setData(QStringLiteral("image")); @@ -1312,7 +1273,6 @@ void QETDiagramEditor::setUpActions() add_fillet ->setData(QStringLiteral("fillet")); add_terminal_strip->setData(QStringLiteral("terminal_strip")); add_generic_device->setData(QStringLiteral("generic_device")); - m_add_cable ->setData(QStringLiteral("cable")); add_text->setCheckable(true); add_line->setCheckable(true); @@ -1321,7 +1281,6 @@ void QETDiagramEditor::setUpActions() add_arc->setCheckable(true); add_polyline->setCheckable(true); add_path->setCheckable(true); - m_add_cable->setCheckable(true); add_fillet->setCheckable(true); connect(&m_add_item_actions_group, &QActionGroup::triggered, this, &QETDiagramEditor::addItemGroupTriggered); @@ -1483,10 +1442,6 @@ void QETDiagramEditor::setUpMenu() QMenu* menu_fichier = new QMenu(tr("&File"), this); QMenu* menu_edition = new QMenu(tr("&Edit"), this); QMenu* menu_project = new QMenu(tr("&Project"), this); - //Numbering of everything the project numbers, kept together - //and sitting right behind the project it belongs to - QMenu* menu_numbering = new QMenu(tr("Numbering"), this); - QMenu* menu_lists = new QMenu(tr("Lists"), this); QMenu* menu_affichage = new QMenu(tr("Displ&ay"), this); // QMenu *menu_outils = new QMenu(tr("O&utils"), this); windows_menu = new QMenu(tr("Wi&ndows"), this); @@ -1494,8 +1449,6 @@ void QETDiagramEditor::setUpMenu() insertMenu(settings_menu_, menu_fichier); insertMenu(settings_menu_, menu_edition); insertMenu(settings_menu_, menu_project); - insertMenu(settings_menu_, menu_numbering); - insertMenu(settings_menu_, menu_lists); insertMenu(settings_menu_, menu_affichage); insertMenu(help_menu_, windows_menu); @@ -1603,11 +1556,15 @@ void QETDiagramEditor::setUpMenu() menu_project -> addAction(m_remove_diagram_from_project); menu_project -> addAction(m_clean_project); menu_project -> addSeparator(); + menu_project -> addAction(m_add_summary); + menu_project -> addAction(m_add_nomenclature); menu_project -> addAction(m_csv_export); - menu_project -> addAction(m_export_cable_list); menu_project -> addAction(m_project_export_conductor_num); + menu_project -> addAction(m_terminal_strip_dialog); + menu_project -> addAction(m_project_terminalBloc); menu_project -> addAction(m_project_export_wiring_list); menu_project -> addAction(m_project_wiring_list_view); + menu_project -> addAction(m_terminal_numbering); menu_project -> addAction(m_reload_element_drawings); #ifdef QET_HAS_SCRIPTING m_scripts_menu = menu_project -> addMenu(tr("Scripts")); @@ -1621,27 +1578,6 @@ void QETDiagramEditor::setUpMenu() menu_project -> addAction(m_export_project_db); #endif - // menu Numbering - //Everything the project numbers sits here together: the - //terminals have come out of the project menu, the cables are - //the new one - menu_numbering -> addAction(m_terminal_numbering); - menu_numbering -> addAction(m_cable_numbering); - - // menu Listes - //Everything which drops a table or a list onto the sheets has - //come out of the project menu and sits here together: the - //summary, the material list, the terminal strip tools and the - //cable list. The CSV exports stay with the other exports in the - //project menu, the cable list's among them. - menu_lists -> addAction(m_add_summary); - menu_lists -> addAction(m_add_nomenclature); - menu_lists -> addSeparator(); - menu_lists -> addAction(m_terminal_strip_dialog); - menu_lists -> addAction(m_project_terminalBloc); - menu_lists -> addSeparator(); - menu_lists -> addAction(m_add_cable_list); - main_tool_bar -> toggleViewAction() -> setStatusTip(tr("Display or hide the main toolbar")); view_tool_bar -> toggleViewAction() -> setStatusTip(tr("Display or hide the Display toolbar")); diagram_tool_bar -> toggleViewAction() -> setStatusTip(tr("Display or hide the Diagram toolbar")); @@ -2306,90 +2242,6 @@ void QETDiagramEditor::selectGroupTriggered(QAction *action) diagram->selectAllTextFields(); } -/** - @brief QETDiagramEditor::cableNumberingGate - Decide whether the cable tool may start, asking about the numbering - rule when the project numbers its cables by no rule at all. - - A project which does have one is asked nothing: the tool starts and - that rule gives the numbers. A project which has none is asked once - whether one should be defined now: - - - Oui opens the window where the rule is written, straight onto the - cable page of its automatic numbering; whatever he does in there, - the tool only starts if a rule really exists by the time that - window is closed. - - Non says no, and says it for good: it is remembered in the program - settings so the question does not come up on the next start - either. Drawing goes on all the same, with W as the cable's name: - no number is worked out where none has been defined. - - Annuler leaves everything as it was, the tool does not start, and - the question comes again the next time he picks the tool up. - - @param diagram the folio the cable would be drawn on - @return true when the cable tool may start -*/ -bool QETDiagramEditor::cableNumberingGate(Diagram *diagram) -{ - if (!diagram || !diagram->project()) return true; - QETProject *project = diagram->project(); - if (project->hasCableAutoNum()) return true; - - //A read-only project is left alone without a question: there is - //nowhere to define a rule in it, so asking whether he wants to - //would be a way to nowhere. Its cables are drawn called W, the - //same as any project without a rule. - if (project->isReadOnly()) return true; - - //Whether he has once answered no in THIS project. The answer is - //kept with the project rather than in the program settings, so - //one project being answered with no does not stop every other - //project from being asked. - if (!project->cableAskNumbering()) { - return true; - } - - QMessageBox box(QMessageBox::Question, - tr("Automatic numbering", "window title"), - tr("No automatic cable numbering is defined.", - "shown when a project has no cable numbering rule"), - QMessageBox::NoButton, - this); - box.setInformativeText(tr( - "Do you want to define one now?\n\n" - "You can also number the cables automatically later: the rule " - "is set at any time in the project properties, on the " - "\"Auto Numbering\" page.", - "ask whether a cable numbering rule should be defined now")); - QPushButton *define_button = box.addButton(tr("Yes", "yes"), QMessageBox::YesRole); - QPushButton *later_button = box.addButton(tr("No", "no"), QMessageBox::NoRole); - box.addButton(tr("Cancel", "cancel"), QMessageBox::RejectRole); - box.setDefaultButton(later_button); - box.exec(); - - if (box.clickedButton() == later_button) { - //With this project only: he has answered once here and - //should not be asked again every time he picks the tool - //up in this project -- another project is free to ask. - project->setCableAskNumbering(false); - project->setModified(true); - return true; - } - if (box.clickedButton() != define_button) { - return false; - } - - //Oui: the window itself, opened where the rule is written. It - //has to really be there by the time that window is closed -- - //a Cancel in it must not leave the tool running all the same. - ProjectPropertiesDialog dialog(diagram->project(), this); - dialog.setCurrentPage(ProjectPropertiesDialog::Autonum); - dialog.changeToCable(); - dialog.exec(); - - return diagram->project()->hasCableAutoNum(); -} - /** @brief QETDiagramEditor::addItemGroupTriggered This slot is called when an item must be added to the current diagram, @@ -2442,22 +2294,6 @@ void QETDiagramEditor::addItemGroupTriggered(QAction *action) { diagram_event = new DiagramEventAddPath (d); } - else if (value == QLatin1String("cable")) - { - //The rule the number comes from -- or the word about there - //being no rule yet -- comes before anything is drawn. - if (!cableNumberingGate(d)) - { - //Nothing is drawn, so the button must not go on looking - //like a tool which is running, and whichever tool was - //active before this click must not keep going with its - //own button unchecked either. - action->setChecked(false); - d->clearEventInterface(); - return; - } - diagram_event = new DiagramEventAddCable(d); - } else if (value == "fillet") diagram_event = new DiagramEventFillet (d); else if (value == "image") @@ -2552,30 +2388,8 @@ void QETDiagramEditor::selectionGroupTriggered(QAction *action) "Unbridge and/or remove the levels from the affected terminals so that they can be " "deleted")); } else { - //A cable line is not held by DiagramContent: it is - //removed by its own command, which also lets the cable - //it belongs to go when no line of it is left anywhere. - QList cable_parts; - for (QGraphicsItem *item : diagram->selectedItems()) { - if (auto *part = qgraphicsitem_cast(item)) { - cable_parts << part; - } - } - const bool other_items = dc.hasDeletableItems(); - diagram->clearSelection(); - - if (cable_parts.isEmpty()) { - diagram->undoStack().push(new DeleteQGraphicsItemCommand(diagram, dc)); - } else if (!other_items) { - diagram->undoStack().push(new RemoveCableCommand(diagram, cable_parts)); - } else { - //One single step, so a selection holding both a - //cable and ordinary drawings comes back whole. - diagram->undoStack().push(new BatchCommand( - new DeleteQGraphicsItemCommand(diagram, dc), - new RemoveCableCommand(diagram, cable_parts))); - } + diagram->undoStack().push(new DeleteQGraphicsItemCommand(diagram, dc)); dv->adjustSceneRect(); } } @@ -2779,9 +2593,6 @@ void QETDiagramEditor::slot_updateActions() m_project_export_wiring_list -> setEnabled(opened_project); m_project_wiring_list_view -> setEnabled(opened_project); m_terminal_numbering -> setEnabled(editable_project); - m_cable_numbering -> setEnabled(editable_project); - m_add_cable_list -> setEnabled(editable_project); - m_export_cable_list -> setEnabled(editable_project); m_reload_element_drawings -> setEnabled(opened_project); #ifdef QET_HAS_SCRIPTING m_run_script -> setEnabled(opened_project); @@ -2875,20 +2686,8 @@ void QETDiagramEditor::slot_updateComplexActions() m_mirror_vertical->setEnabled(!ro && selected_elements_count); //Actions that need items (elements, conductors, texts...) selected, to be enabled - //A cable line is drawn on the folio but held by the cable, not - //by DiagramContent: it counts on its own for deleting and editing. - int selected_cables = 0; - for (QGraphicsItem *item : diagram_->selectedItems()) { - if (item && item->type() == CablePart::Type) { - selected_cables++; - } - } - - //A drawn cable line is copyable, cuttable and duplicatable on - //its own, although it belongs to its cable rather than to the - //folio and DiagramContent never sees it. - bool copiable_items = dc.hasCopiableItems() || selected_cables > 0; - bool deletable_items = dc.hasDeletableItems() || selected_cables > 0; + bool copiable_items = dc.hasCopiableItems(); + bool deletable_items = dc.hasDeletableItems(); m_cut -> setEnabled(!ro && copiable_items); m_copy -> setEnabled(copiable_items); m_duplicate -> setEnabled(!ro && copiable_items); @@ -2944,8 +2743,7 @@ void QETDiagramEditor::slot_updateComplexActions() - selected_dynamic_elmt_text) + selected_image + selected_shape - + selected_conductors_count - + selected_cables; + + selected_conductors_count; if (selected_editable == 1) { @@ -2971,13 +2769,6 @@ void QETDiagramEditor::slot_updateComplexActions() "edit image")); m_edit_selection -> setIcon(QET::Icons::resize_image); } - //edit cable - else if (selected_cables) - { - m_edit_selection -> setText(tr("Edit the cable", - "edit cable")); - m_edit_selection -> setIcon(QET::Icons::Cable); - } //edit conductor else if (selected_conductors_count) { @@ -4220,21 +4011,6 @@ void QETDiagramEditor::slot_terminalNumbering() { } } -/** - * @brief QETDiagramEditor::slot_cableNumbering - * Opens the window holding the numbering rule of the project's cables - * and the button which numbers every one of them again. The window - * writes the project and pushes its own undo step itself, so nothing - * is done with its result here beyond letting it work. - */ -void QETDiagramEditor::slot_cableNumbering() { - QETProject *project = currentProject(); - if (!project) return; - - CableNumberingDialog dialog(project, this); - dialog.exec(); -} - /** @brief QETDiagramEditor::slot_reloadElementDrawings Redraw every placed element of the current project from its current diff --git a/sources/qetdiagrameditor.h b/sources/qetdiagrameditor.h index 2ff471b3f..9e9e0a01d 100644 --- a/sources/qetdiagrameditor.h +++ b/sources/qetdiagrameditor.h @@ -102,18 +102,6 @@ class QETDiagramEditor : public QETMainWindow void setUpMenu (); bool addProject(QETProject *, bool = true); - /** - Checked while the cable tool is about to start, when the - project has no numbering rule for its cables. He is then - asked whether one should be defined now (the window which - opens goes straight to the cable page of the automatic - numbering) or not -- and having answered no, he is not - asked again, whatever he draws from then on. A project - which does number its cables is never asked at all, and - one which lost the rule again is. - @return true when the cable tool may start. - */ - bool cableNumberingGate(Diagram *diagram); DiagramView *currentDiagramView() const; Element *currentElement() const; ProjectView *findProject(DiagramView *) const; @@ -170,7 +158,6 @@ class QETDiagramEditor : public QETMainWindow void editProjectProperties(ProjectView *); void editProjectProperties(QETProject *); void slot_terminalNumbering(); - void slot_cableNumbering(); void slot_reloadElementDrawings(); #ifdef QET_HAS_SCRIPTING void slot_runScript(); @@ -254,7 +241,6 @@ class QETDiagramEditor : public QETMainWindow *m_configure_duplicate, ///< Reopen the duplicate offset/direction dialog (#991) *m_auto_conductor, ///< Enable/Disable the use of auto conductor *m_auto_break_conductor, ///< Enable/Disable the use of auto break conductor - *m_add_cable = nullptr, ///< Draw the trunk line of a cable *m_draw_grid, ///< Switch the background grid display or not *m_draw_guides = nullptr, ///< Switch the custom guides display or not *m_cell_rulers = nullptr, ///< Keep the folio column/row headers in sight or not @@ -272,9 +258,6 @@ class QETDiagramEditor : public QETMainWindow *m_project_export_wiring_list, ///< Action to export the wiring list *m_project_wiring_list_view, ///< Action to show the wiring list read from the project database *m_terminal_numbering, ///< Action to launch terminal numbering - *m_cable_numbering, ///< Action to open the cable numbering window (rule + numbering of every cable) - *m_add_cable_list, ///< Add a cable list graphics item - *m_export_cable_list, ///< Export the cable list of the project to CSV *m_reload_element_drawings, ///< Action to redraw every placed element from its current definition #ifdef QET_HAS_SCRIPTING *m_run_script, ///< Action to run a JavaScript macro against the current project diff --git a/sources/qetgraphicsitem/ViewItem/qetgraphicstableitem.cpp b/sources/qetgraphicsitem/ViewItem/qetgraphicstableitem.cpp index 7a4f50155..df090c36d 100644 --- a/sources/qetgraphicsitem/ViewItem/qetgraphicstableitem.cpp +++ b/sources/qetgraphicsitem/ViewItem/qetgraphicstableitem.cpp @@ -19,7 +19,6 @@ #include "../../shownkinds.h" #include "../../qetproject.h" #include "../../QPropertyUndoCommand/qpropertyundocommand.h" -#include "../../cablelist/cablelistmodel.h" #include "../../createdxf.h" #include "../../diagram.h" #include "../../elementprovider.h" @@ -451,13 +450,7 @@ void QetGraphicsTableItem::setPreviousTable(QetGraphicsTableItem *table) } else if (old_previous_table->model()) //Copie the model of old previous table { - auto *old_model = old_previous_table ? old_previous_table->model() : nullptr; - if (auto *db_model = dynamic_cast(old_model)) { - setModel(new ProjectDBModel(*db_model)); - } - else if (auto *cable_model = dynamic_cast(old_model)) { - setModel(new CableListModel(*cable_model)); - } + setModel(new ProjectDBModel(*static_cast(old_previous_table->model()))); } if (old_previous_table && @@ -626,12 +619,8 @@ QDomElement QetGraphicsTableItem::toXml(QDomDocument &dom_document) const { //Add model auto dom_model = dom_document.createElement("model"); - if (auto project_db_model = dynamic_cast(m_model.data())) { - dom_model.appendChild(project_db_model->toXml(dom_document)); - } - else if (auto cable_model = dynamic_cast(m_model.data())) { - dom_model.appendChild(cable_model->toXml(dom_document)); - } + auto project_db_model = static_cast(m_model.data()); + dom_model.appendChild(project_db_model->toXml(dom_document)); dom_table.appendChild(dom_model); } @@ -683,30 +672,16 @@ void QetGraphicsTableItem::fromXml(const QDomElement &dom_element) } else if (this->diagram()) //The table haven't got a previous table, so there should be a model save to xml { - //Get table: which model wrote itself in there decides which - //class reads it back -- a cable list is not a database query - auto model_element = dom_element.firstChildElement("model"); - auto project_model_element = model_element.firstChildElement( - ProjectDBModel::xmlTagName()); - auto cable_model_element = model_element.firstChildElement( - CableListModel::xmlTagName()); - - if (!cable_model_element.isNull()) - { - auto model_ = new CableListModel( - this->diagram()->project(), - this->diagram()->project()); - model_->fromXml(cable_model_element); - this->setModel(model_); - } - else - { - auto model_ = new ProjectDBModel( - this->diagram()->project(), - this->diagram()->project()); - model_->fromXml(project_model_element); - this->setModel(model_); - } + //Get table + auto model_ = new ProjectDBModel( + this->diagram()->project(), + this->diagram()->project()); + model_->fromXml( + dom_element + .firstChildElement("model") + .firstChildElement( + ProjectDBModel::xmlTagName())); + this->setModel(model_); } //Restore the header from xml diff --git a/sources/qetgraphicsitem/conductor.cpp b/sources/qetgraphicsitem/conductor.cpp index dc2715139..66fe92dbb 100644 --- a/sources/qetgraphicsitem/conductor.cpp +++ b/sources/qetgraphicsitem/conductor.cpp @@ -1845,32 +1845,6 @@ ConductorProperties Conductor::properties() const return(m_properties); } -/** - @brief Conductor::setCableReference - Record the cable core this conductor shows, together with the label - that assignment generates. Called only by CableManager, which works - the values out from the cable itself: the user never types them. - @param label the generated cable field, e.g. "15W1:br", empty to - detach the conductor from its cable - @param cable_uuid the cable this conductor is a core of - @param slot which core of that cable it shows, -1 when none -*/ -void Conductor::setCableReference(const QString &label, - const QUuid &cable_uuid, - int slot) -{ - if (m_properties.m_cable == label - && m_properties.m_cable_uuid == cable_uuid - && m_properties.m_cable_slot == slot) { - return; - } - - m_properties.m_cable = label; - m_properties.m_cable_uuid = cable_uuid; - m_properties.m_cable_slot = slot; - emit propertiesChange(); -} - /** @return true si le conducteur est mis en evidence */ diff --git a/sources/qetgraphicsitem/conductor.h b/sources/qetgraphicsitem/conductor.h index f75931064..34edfe436 100644 --- a/sources/qetgraphicsitem/conductor.h +++ b/sources/qetgraphicsitem/conductor.h @@ -125,15 +125,6 @@ class Conductor : public QGraphicsObject bool only_text = false); void setProperties(const ConductorProperties &property); ConductorProperties properties() const; - /// Records which cable core this conductor shows and writes the - /// label that assignment generates (15W1:br) into its cable - /// field. Deliberately not routed through - /// setPropertyToPotential(): a core of a cable belongs to this - /// conductor alone and must never spill over a pass-through - /// terminal onto the neighbouring half conductor. - void setCableReference(const QString &label, - const QUuid &cable_uuid, - int slot); void setProfile(const ConductorProfile &, Qt::Corner); ConductorProfile profile(Qt::Corner) const; diff --git a/sources/qeticons.cpp b/sources/qeticons.cpp index e64fdcae6..8436b77a9 100644 --- a/sources/qeticons.cpp +++ b/sources/qeticons.cpp @@ -37,7 +37,6 @@ namespace QET { QIcon ArrowRightDouble; QIcon Autoconnect; QIcon BringForward; - QIcon Cable; QIcon Cancel; QIcon Company; QIcon Conductor; @@ -417,7 +416,6 @@ void QET::Icons::initIcons() ArrowRightDouble = QIcon::fromTheme("arrow-right-double"); Autoconnect = QIcon::fromTheme("autoconnect"); BringForward = QIcon::fromTheme("bring_forward"); - Cable = QIcon::fromTheme("cable"); Cancel = QIcon::fromTheme("item-cancel"); Company = QIcon::fromTheme("go-company"); Conductor = QIcon::fromTheme("conductor"); diff --git a/sources/qeticons.h b/sources/qeticons.h index 35aedfcc5..aec05c184 100644 --- a/sources/qeticons.h +++ b/sources/qeticons.h @@ -44,7 +44,6 @@ namespace QET { extern QIcon ArrowRightDouble; extern QIcon Autoconnect; extern QIcon BringForward; - extern QIcon Cable; extern QIcon Cancel; extern QIcon Company; extern QIcon Conductor; diff --git a/sources/qetproject.cpp b/sources/qetproject.cpp index 37ca5ed9a..829ffd3b6 100644 --- a/sources/qetproject.cpp +++ b/sources/qetproject.cpp @@ -23,9 +23,6 @@ #include "autoNum/numerotationcontext.h" #include "autoNum/numerotationcontextcommands.h" #include "autoNum/renumberelementscommand.h" -#include "cable/cable.h" -#include "cable/cablemanager.h" -#include "cable/cablepart.h" #include "autoNum/elementautonumschemecommand.h" #include "diagram.h" #include "qetgraphicsitem/element.h" @@ -120,17 +117,6 @@ m_project_properties_handler{this} for (auto it = folioData.first.constBegin(); it != folioData.first.constEnd(); ++it) { addFolioAutoNum(it.key(), it.value()); } - - //The cable numbering rule a project starts with, so a project - //made from these defaults numbers its cables the way the - //global settings say without having to be opened first. - auto cableData = NumerotationContext::loadFromSettings(settings, QStringLiteral("autonum/cable")); - for (auto it = cableData.first.constBegin(); it != cableData.first.constEnd(); ++it) { - addCableAutoNum(it.key(), it.value()); - } - if (!cableData.second.isEmpty()) { - setCurrentCableAutoNum(cableData.second); - } } } @@ -840,16 +826,6 @@ QHash QETProject::elementAutoNum() const return m_element_autonum; } -/** - @brief QETProject::cableAutoNum - @return the cable numerotation stored in the project -- one entry at - most, since cables are numbered by a single rule -*/ -QHash QETProject::cableAutoNum() const -{ - return m_cable_autonum; -} - /** @brief QETProject::elementAutoNumFormula @param key : autonum title @@ -890,109 +866,6 @@ void QETProject::setCurrrentElementAutonum(QString autoNum) { m_current_element_autonum = std::move(autoNum); } -/** - @brief QETProject::cableAutoNumFormula - @param key : autonum title - @return Formula of the cable numerotation stored with key -*/ -QString QETProject::cableAutoNumFormula(const QString& key) const -{ - if (m_cable_autonum.contains(key)) { - return autonum::numerotationContextToFormula(m_cable_autonum.value(key)); - } - - return QString(); -} - -/** - @brief QETProject::hasCableAutoNum - @return true when a numbering rule for cables is set: a context - stored under the name which is in use. A project which has never been - given one answers false, and that is what makes the cable tool ask - whether one should be defined (see QETDiagramEditor). -*/ -bool QETProject::hasCableAutoNum() const -{ - if (m_current_cable_autonum.isEmpty()) return false; - return !cableAutoNum(m_current_cable_autonum).isEmpty(); -} - -/** - @brief QETProject::cableCurrentAutoNum - @return title of the cable numerotation in use (one rule only) -*/ -QString QETProject::cableCurrentAutoNum() const -{ - return m_current_cable_autonum; -} - -/** - @brief QETProject::setCurrentCableAutoNum - @param autoNum : the rule which from now on numbers the cables -*/ -void QETProject::setCurrentCableAutoNum(QString autoNum) -{ - m_current_cable_autonum = std::move(autoNum); -} - -/** - @brief QETProject::cableAutoNumRuleName - @return the name the one and only cable numbering rule is kept under -*/ -QString QETProject::cableAutoNumRuleName() -{ - return QStringLiteral("Cable"); -} - -/** - @brief QETProject::cableXAxisFirst - @return true when the cables are numbered along the X axis (the - leftmost first), false when they are numbered along the Y axis (the - topmost first) -*/ -bool QETProject::cableXAxisFirst() const -{ - return m_cable_axis_x_first; -} - -/** - @brief QETProject::setCableXAxisFirst - Which axis the whole project numbers its cables along. Stored with - the rule rather than on its own, since it only means anything while - a rule is being applied to every cable at once. - @param x_axis_first -*/ -void QETProject::setCableXAxisFirst(bool x_axis_first) -{ - m_cable_axis_x_first = x_axis_first; -} - -/** - @brief QETProject::cableAskNumbering - @return true when the cable tool may still ask this project whether - a numbering rule should be defined. The answer to that question is - kept with the project rather than in the program settings, so that - one project being answered with no does not stop every other - project from asking. -*/ -bool QETProject::cableAskNumbering() const -{ - return m_cable_ask_numbering; -} - -/** - @brief QETProject::setCableAskNumbering - Take the answer he gave to the numbering question down with the - project. False means he has said no once here and is not to be - asked again in this project; taking the rule away puts it back to - true so the question comes again. - @param ask -*/ -void QETProject::setCableAskNumbering(bool ask) -{ - m_cable_ask_numbering = ask; -} - /** @brief QETProject::renumberElementsBySchemeTitle Renumber existing elements by element autonumbering scheme title. @@ -1314,32 +1187,6 @@ void QETProject::addFolioAutoNum(const QString& key, const NumerotationContext& emit autoNumContextUpdated(); } -/** - @brief QETProject::addCableAutoNum - Store the cable numbering rule. There is room for exactly one: saving - it under a name drops every other entry, so a project can never end up - with a list of cable rules to choose from. - @param key - @param context -*/ -void QETProject::addCableAutoNum(const QString& key, const NumerotationContext& context) -{ - if (!key.isEmpty()) { - const QStringList others = m_cable_autonum.keys(); - for (const QString &other : others) { - if (other != key) m_cable_autonum.remove(other); - } - } - m_cable_autonum.insert(key, context); - //The rule in use must always be one which exists: after a save - //under another name the old entry is gone, so follow the new one. - if (!key.isEmpty() && (m_current_cable_autonum.isEmpty() - || !m_cable_autonum.contains(m_current_cable_autonum))) { - m_current_cable_autonum = key; - } - emit autoNumContextUpdated(); -} - /** @brief QETProject::removeConductorAutoNum Remove Conductor Numerotation Context stored with key @@ -1370,21 +1217,6 @@ void QETProject::removeFolioAutoNum(const QString& key) { m_folio_autonum.remove(key); } -/** - @brief QETProject::removeCableAutoNum - Forget the cable numbering rule stored with key - @param key -*/ -void QETProject::removeCableAutoNum(const QString& key) -{ - m_cable_autonum.remove(key); - if (m_current_cable_autonum == key) { - m_current_cable_autonum = m_cable_autonum.isEmpty() - ? QString() : m_cable_autonum.keys().first(); - } - emit autoNumContextUpdated(); -} - /** @brief QETProject::conductorAutoNum Return conductor numerotation context stored with key. @@ -1420,18 +1252,6 @@ NumerotationContext QETProject::folioAutoNum (const QString &key) const else return NumerotationContext(); } -/** - @brief QETProject::cableAutoNum - Return the cable numerotation context stored with key. - If key is not found, return an empty numerotation context - @param key -*/ -NumerotationContext QETProject::cableAutoNum(const QString &key) const -{ - if (m_cable_autonum.contains(key)) return m_cable_autonum.value(key); - else return NumerotationContext(); -} - /** @brief QETProject::freezeExistentConductorLabel Freeze Existent Conductors in the selected folios @@ -1776,18 +1596,6 @@ QDomDocument QETProject::toXml() project_root.appendChild(xml_strip); } - //Write the cables to xml. Their sections are written here too, - //beside the cable they belong to, so a cable and everything drawn - //for it are read back together and can never come apart. - if (m_cables.count()) - { - auto xml_cables = xml_doc.createElement(QStringLiteral("cables")); - for (auto &cable : m_cables) { - xml_cables.appendChild(cable->toXml(xml_doc)); - } - project_root.appendChild(xml_cables); - } - // Write the elements collection. project_root.appendChild(m_elements_collection->root().cloneNode(true)); @@ -2301,23 +2109,10 @@ void QETProject::readProjectXml(QDomDocument &xml_project) readTerminalStripXml(xml_project); const qint64 strips_ms = phase_timer.restart(); - //Load the cables, after the diagrams their sections are drawn on - readCableXml(xml_project); - const qint64 cables_ms = phase_timer.restart(); - //Now that all are loaded we refresh content of the project. refresh(); const qint64 refresh_ms = phase_timer.restart(); - //refresh() above resolved the folio report links for the first - //time (refreshContents() -> initLink). The cable label pass at - //the end of readCableXml() ran before that, while no element - //knew its partners yet, so a core running through a report could - //not be carried over there -- it was even cleared off the far - //wire again. Run the pass once more now that the links exist: - //it recomputes everything from the project as it now stands. - CableManager::refreshLabels(this); - m_data_base.blockSignals(false); m_data_base.setUpdateBlocked(false); m_data_base.updateDB(xml_project); @@ -2325,12 +2120,11 @@ void QETProject::readProjectXml(QDomDocument &xml_project) qInfo().nospace() << "Project content built in " - << (elements_ms + diagrams_ms + strips_ms + cables_ms + << (elements_ms + diagrams_ms + strips_ms + refresh_ms + database_ms) / 1000.0 << " seconds (elements collection " << elements_ms / 1000.0 << ", diagrams " << diagrams_ms / 1000.0 << ", terminal strips " << strips_ms / 1000.0 - << ", cables " << cables_ms / 1000.0 << ", refresh " << refresh_ms / 1000.0 << ", database " << database_ms / 1000.0 << ")"; @@ -2524,7 +2318,7 @@ void QETProject::readDefaultPropertiesXml(QDomDocument &xml_project) m_default_xref_properties = XRefProperties:: defaultProperties(); //Read values indicate in project - QDomElement border_elmt, titleblock_elmt, conductors_elmt, report_elmt, xref_elmt, conds_autonums, folio_autonums, element_autonums, cable_autonums, guides_elmt; + QDomElement border_elmt, titleblock_elmt, conductors_elmt, report_elmt, xref_elmt, conds_autonums, folio_autonums, element_autonums, guides_elmt; for (QDomNode child = newdiagrams_elmt.firstChild() ; !child.isNull() ; child = child.nextSibling()) { @@ -2547,8 +2341,6 @@ void QETProject::readDefaultPropertiesXml(QDomDocument &xml_project) folio_autonums = child_elmt; else if (child_elmt.tagName()== QLatin1String("element_autonums")) element_autonums = child_elmt; - else if (child_elmt.tagName()== QLatin1String("cable_autonums")) - cable_autonums = child_elmt; else if (child_elmt.tagName() == QLatin1String("guides")) guides_elmt = child_elmt; } @@ -2617,37 +2409,6 @@ void QETProject::readDefaultPropertiesXml(QDomDocument &xml_project) m_current_element_autonum = current_title; } } - if (!cable_autonums.isNull()) - { - m_current_cable_autonum = cable_autonums.attribute(QStringLiteral("current_autonum")); - //Which axis the cables are numbered along. A file written - //before this existed has no say in it and stays with the - //axis the terminals have always started from. - m_cable_axis_x_first = cable_autonums.attribute( - QStringLiteral("axis_priority"), QStringLiteral("x")) - != QLatin1String("y"); - //Whether the numbering question was already answered with - //no in this project. A file which does not hold the answer - //yet asks again rather than staying silent: it is his own - //answer per project which is kept, not a default saying no. - m_cable_ask_numbering = cable_autonums.attribute( - QStringLiteral("ask_numbering_rule"), QStringLiteral("true")) - != QLatin1String("false"); - for (auto elmt : QET::findInDomElement(cable_autonums, QStringLiteral("cable_autonum"))) - { - NumerotationContext nc; - nc.fromXml(elmt); - if (elmt.attribute(QStringLiteral("title")).isEmpty()) continue; - m_cable_autonum.insert(elmt.attribute(QStringLiteral("title")), nc); - } - //A file written by hand, or written before this rule existed, - //may name a rule which is not in the list: the numbering must - //never be left pointing at something which is not there. - if (!m_cable_autonum.contains(m_current_cable_autonum)) { - m_current_cable_autonum = m_cable_autonum.isEmpty() - ? QString() : m_cable_autonum.keys().first(); - } - } // Read guides from XML (if missing, e.g. in old projects, list stays empty) m_default_guides.clear(); @@ -2682,55 +2443,6 @@ void QETProject::readTerminalStripXml(const QDomDocument &xml_project) } } -/** - * @brief QETProject::readCableXml - * Read the cables of the project, and draw a section for each of them on - * the folio it belongs to. - * - * The diagrams have already been read at this point, which is what makes - * it possible to put the sections back where they were: a folio is - * identified by its uuid, so reordering or renumbering pages never moves - * a cable onto the wrong one. - * @param xml_project - */ -void QETProject::readCableXml(const QDomDocument &xml_project) -{ - auto xml_elmt = xml_project.documentElement(); - auto xml_cables = xml_elmt.firstChildElement(QStringLiteral("cables")); - if (xml_cables.isNull()) return; - - for (auto xml_cable = xml_cables.firstChildElement(QStringLiteral("cable")); - !xml_cable.isNull(); - xml_cable = xml_cable.nextSiblingElement(QStringLiteral("cable"))) - { - auto cable = new Cable(this); - cable->fromXml(xml_cable); - addCable(cable); - - for (const CablePartData &part_data : cable->parts()) - { - Diagram *diagram = diagramByUuid(part_data.diagram); - if (!diagram) - { - //The folio this section was drawn on has been removed: - //the section goes with it, and so do the cores which - //were only ever shown on it. - cable->removePart(part_data.uuid); - continue; - } - - auto part = new CablePart(part_data); - part->setCable(cable); - diagram->addItem(part); - } - } - - //The file holds the generated cable field of every conductor as - //well as the cables themselves; working it out again here makes - //sure the two agree even if a file was edited by hand. - CableManager::refreshLabels(this); -} - /** Export project properties under the \a xml_element XML element. */ @@ -2895,52 +2607,6 @@ void QETProject::writeDefaultPropertiesXml(QDomElement &xml_element) } xml_element.appendChild(element_autonums); - //Export Cable Autonum -- one rule, but stored exactly like the others - //so it travels with the project and survives a re-save untouched. - // - //Only when it holds something, though. An element which is not there - //reads back as exactly the defaults a project which never touched - //cable numbering already has, so writing it every time put - // into every file the program saved -- into the - //example projects and into projects without a single cable in them. - // - //That is not the same as writing it only for projects with cables, - //which would cost data: a rule named before the first cable is - //drawn, an axis chosen before it, and the numbering question - //answered with no are answers only this element can carry. - QDomElement cable_autonums = xml_document.createElement("cable_autonums"); - cable_autonums.setAttribute("current_autonum", m_current_cable_autonum); - cable_autonums.setAttribute("axis_priority", - m_cable_axis_x_first ? "x" : "y"); - //Only the no is written down: a project which has not answered - //the numbering question yet asks again, so silence in the file - //means asking rather than holding back. - if (!m_cable_ask_numbering) { - cable_autonums.setAttribute("ask_numbering_rule", "false"); - } - int written_cable_rules = 0; - QStringList cable_autonum_keys = cableAutoNum().keys(); - cable_autonum_keys.sort(); - for (const QString &key : std::as_const(cable_autonum_keys)) { - QDomElement cable_autonum = cableAutoNum(key).toXml(xml_document, "cable_autonum"); - if (key != "" && cableAutoNumFormula(key) != "") { - cable_autonum.setAttribute("title", key); - cable_autonum.setAttribute("formula", cableAutoNumFormula(key)); - cable_autonums.appendChild(cable_autonum); - ++written_cable_rules; - } - } - //Nothing to keep when the project never named a rule, never - //chose an axis other than the default and has not answered the - //numbering question with no -- then this element would say - //nothing the reader does not assume anyway. - if (!m_current_cable_autonum.isEmpty() - || !m_cable_ask_numbering - || !m_cable_axis_x_first - || written_cable_rules > 0) { - xml_element.appendChild(cable_autonums); - } - // Export default guides QDomElement guides_elmt = xml_document.createElement("guides"); for (const auto &g : m_default_guides) { @@ -3126,86 +2792,6 @@ bool QETProject::removeTerminalStrip(TerminalStrip *strip) { return m_terminal_strip_vector.removeOne(strip); } -/** - * @brief QETProject::cables - * @return every cable of this project - */ -QVector QETProject::cables() const { - return m_cables; -} - -/** - * @brief QETProject::newCable - * Create a cable owned by this project and watch it: whenever it changes, - * the cable field of every conductor it feeds is worked out again. - * @return the new cable - */ -Cable *QETProject::newCable() -{ - auto cable = new Cable(this); - addCable(cable); - return cable; -} - -/** - * @brief QETProject::addCable - * @param cable - * @return true when the cable is now held by this project - */ -bool QETProject::addCable(Cable *cable) -{ - if (!cable || cable->parent() != this) - return false; - - if (m_cables.contains(cable)) - return true; - - m_cables.append(cable); - connect(cable, &Cable::changed, this, [this]() { - CableManager::refreshLabels(this); - setModified(true); - }); - emit cableAdded(cable); - return true; -} - -/** - * @brief QETProject::removeCable - * Cable is removed from the list but not deleted, so an undo command can - * put it back. - * @param cable - * @return true when the cable was in the list - */ -bool QETProject::removeCable(Cable *cable) { - if (!m_cables.removeOne(cable)) - return false; - - disconnect(cable, nullptr, this, nullptr); - - //A cable which is no longer one of ours stops writing its labels - //into the conductors it used to feed. - CableManager::refreshLabels(this); - setModified(true); - emit cableRemoved(cable); - return true; -} - -/** - * @brief QETProject::diagramByUuid - * @param uuid - * @return the folio with that identity, or nullptr. A cable section says - * which folio it is drawn on by identity, since folios are reordered and - * renumbered freely. - */ -Diagram *QETProject::diagramByUuid(const QUuid &uuid) -{ - if (uuid.isNull()) return nullptr; - for (Diagram *diagram : m_diagrams_list) { - if (diagram && diagram->uuid() == uuid) return diagram; - } - return nullptr; -} - /** Cette methode sert a reperer un projet vide, c-a-d un projet identique a ce que l'on obtient en faisant Fichier > Nouveau. diff --git a/sources/qetproject.h b/sources/qetproject.h index 56b1fc517..212d08169 100644 --- a/sources/qetproject.h +++ b/sources/qetproject.h @@ -56,7 +56,6 @@ class QUndoStack; class XmlElementCollection; class QTimer; class TerminalStrip; -class Cable; #include @@ -183,49 +182,6 @@ class QETProject : public QObject QString elementCurrentAutoNum() const; void setCurrrentElementAutonum(QString autoNum); - /// The cable numbering rule. Only one of them is ever kept: - /// a cable is numbered by one rule and that is all there is, - /// so the hash holds a single entry (see the cable tab of - /// the automatic numbering pages). - QHash cableAutoNum() const; - void addCableAutoNum (const QString& key, const NumerotationContext& context); - void removeCableAutoNum (const QString& key); - NumerotationContext cableAutoNum(const QString &key) const; - - QString cableAutoNumFormula(const QString& key) const; - bool hasCableAutoNum() const; - QString cableCurrentAutoNum() const; - void setCurrentCableAutoNum(QString autoNum); - /** - Which axis the cables of the project are laid out along - when the whole project is numbered again: true (the - default) for the X axis, so the leftmost cable of a folio - comes first, false for the Y axis, so the topmost does. - It belongs to the numbering and is written into the - project file next to the rule itself. - */ - bool cableXAxisFirst() const; - void setCableXAxisFirst(bool x_axis_first); - /** - Whether the cable tool asks this project whether a - numbering rule should be defined, when he is picked up - and the project has none. Saying no in that question is - remembered with the project rather than in the program - settings, so one project saying no does not silence - every other one -- each project asks again until it - has answered for itself, and taking the rule away - brings the question back (see the cable tab of the - project properties and the numbering window). - */ - bool cableAskNumbering() const; - void setCableAskNumbering(bool ask); - /** - The name the single cable numbering rule is kept under. - Fixed and never translated: it is a key which goes into - the settings and into the project file, so it must read - the same whatever language the program runs in. - */ - static QString cableAutoNumRuleName(); //Identity of the element numbering schemes. The title is the //name shown to the user and the lookup key of the API; the uuid //is what an element's ELMT_FORMULA_ID refers to, so a scheme can @@ -306,12 +262,6 @@ class QETProject : public QObject bool addTerminalStrip(TerminalStrip *strip); bool removeTerminalStrip(TerminalStrip *strip); - QVector cables() const; - Cable *newCable(); - bool addCable(Cable *cable); - bool removeCable(Cable *cable); - Diagram *diagramByUuid(const QUuid &uuid); - public slots: Diagram *addNewDiagram(int pos = -1); void removeDiagram(Diagram *); @@ -345,12 +295,6 @@ class QETProject : public QObject void folioAutoNumRemoved(); void defaultTitleBlockPropertiesChanged(); void conductorAutoNumChanged(); - /// A cable joined or left this project. Views over the - /// cables -- the cable list table -- listen to both to work - /// their rows out again; during a load the cables arrive one - /// signal at a time, long after the folios were read. - void cableAdded(Cable *cable); - void cableRemoved(Cable *cable); private slots: void updateDiagramsFolioData(); @@ -385,7 +329,6 @@ class QETProject : public QObject void readProjectPropertiesXml(QDomDocument &xml_project); void readDefaultPropertiesXml(QDomDocument &xml_project); void readTerminalStripXml(const QDomDocument &xml_project); - void readCableXml(const QDomDocument &xml_project); void readUsageXml(QDomDocument &xml_project); void readWireHopsXml(QDomDocument &xml_project); void readSymbolTextsXml(QDomDocument &xml_project); @@ -455,16 +398,6 @@ class QETProject : public QObject /// Title -> uuid of each element numbering scheme QHash m_element_autonum_id; QString m_current_element_autonum; - /// Cable auto numbering: exactly one rule, kept the same way - /// the others are so it stores and travels with the project - /// alike, but never a list to choose from. - QHash m_cable_autonum; - QString m_current_cable_autonum; - ///< which axis the cables are numbered along (see cableXAxisFirst) - bool m_cable_axis_x_first = true; - ///< whether the cable tool still asks about a numbering rule - ///< for this project (see cableAskNumbering) - bool m_cable_ask_numbering = true; /// True when the loaded file had element numbering schemes /// saved without an id (written before ids existed) bool m_legacy_element_autonums = false; @@ -490,7 +423,6 @@ class QETProject : public QObject QSet m_saved_item_uuids; //symbol and wire uuids the file carries, see derivedItemUuid() projectDataBase m_data_base; QVector m_terminal_strip_vector; - QVector m_cables; ProjectPropertiesHandler m_project_properties_handler; }; diff --git a/sources/scripting/qetscriptapi.cpp b/sources/scripting/qetscriptapi.cpp index 3b315f784..eec89c0e5 100644 --- a/sources/scripting/qetscriptapi.cpp +++ b/sources/scripting/qetscriptapi.cpp @@ -24,9 +24,6 @@ #include "../QPropertyUndoCommand/qpropertyundocommand.h" #include "../cli_export.h" #include "../diagram.h" -#include "../bomexport.h" -#include "../cablelist/cablelistrows.h" -#include "../cablelist/cablequerywidget.h" #include "../dataBase/ui/elementquerywidget.h" #include "../dataBase/ui/summaryquerywidget.h" #include "../diagramcontent.h" @@ -339,31 +336,6 @@ bool QetScriptApi::exportBom(const QString &output, bool noSlaves, return runFlag(QStringLiteral("--export-bom"), args); } -/** - @brief QetScriptApi::exportCableList - Write the cable list of this project to @p output as CSV -- the same - bytes the "Exporter la liste des câbles au format CSV" action writes, - every column of CableList, every cable of the project. Written from - the project in memory rather than through runFlag(): the rows are - worked out from the cables themselves, there is no CLI flag and no - reopening of the file to do it through. - @param output path of the file to write - @return true when the file was written -*/ -bool QetScriptApi::exportCableList(const QString &output) -{ - if (!m_project) { - log(QStringLiteral("qet.exportCableList: no project")); - return false; - } - QString error; - if (!BomExport::writeCsv(output, CableList::toCsv(m_project), &error)) { - log(QStringLiteral("qet.exportCableList: %1").arg(error)); - return false; - } - return true; -} - bool QetScriptApi::exportWiring(const QString &output) { return runFlag(QStringLiteral("--export-wiring"), {output}); @@ -2990,16 +2962,12 @@ QStringList QetScriptApi::tables(int folioIndex) const file, so one call creates exactly the one table asked for. A script that wants either behaviour can resize the result or add a folio itself. - kind is "nomenclature" (an ElementQueryWidget, over placed elements), - "summary" (a SummaryQueryWidget, over folios) or "cable_list" (a - CableQueryWidget, over the cables of the project); query is required - for the first two, since + kind is "nomenclature" (an ElementQueryWidget, over placed elements) or + "summary" (a SummaryQueryWidget, over folios); query is required, since both widgets otherwise build their own from a set of checkboxes that default to none checked, and "SELECT with no columns" is not a useful table -- query() against element_nomenclature_view or - project_summary_view is the way to find one that is. For - "cable_list" the query is comma-joined column keys of CableList, and - empty means the default columns. + project_summary_view is the way to find one that is. Not undoable: newTable(), which create() calls, calls Diagram::addItem() directly, with no undo command of its own, in the @@ -3023,7 +2991,7 @@ int QetScriptApi::addTable(int folioIndex, const QString &kind, const QString &n log(QStringLiteral("qet.%1: no folio at index %2").arg(caller).arg(folioIndex)); return -1; } - if (query.isEmpty() && kind != QLatin1String("cable_list")) { + if (query.isEmpty()) { // Both widgets build their query from a set of checkboxes that // default to none checked, so "no query" is not "the sensible // default" here the way it might look -- it is SELECT with no @@ -3032,9 +3000,6 @@ int QetScriptApi::addTable(int folioIndex, const QString &kind, const QString &n // instead, over query() -- the same project database and the same // two views this project already exposes, element_nomenclature_view // for a nomenclature table and project_summary_view for a summary. - // A cable list is not read from that database (its rows come - // straight from the cables of the project), so for that kind an - // empty query is allowed and simply means "the default columns". log(QStringLiteral("qet.%1: a query is required -- try qet.query() against " "element_nomenclature_view or project_summary_view first " "to find one that returns what is wanted").arg(caller)); @@ -3050,27 +3015,8 @@ int QetScriptApi::addTable(int folioIndex, const QString &kind, const QString &n auto *w = new SummaryQueryWidget(); w->setQuery(query); content = w; - } else if (kind == QLatin1String("cable_list")) { - auto *w = new CableQueryWidget(); - if (!query.isEmpty()) { - // Comma-joined column keys; keys the list does not know are - // ignored by setFields(), and an empty list of usable keys - // leaves the default columns rather than a blank table. - const QStringList fields = query.split(QLatin1Char(','), Qt::SkipEmptyParts); - w->setFields(fields); - if (w->selectedKeys().isEmpty()) { - w->setFields(CableList::defaultKeys()); - } - } else { - // No query for this kind means the default columns, as - // promised above. The dialog a user opens leaves an empty - // table when nothing is picked, but a script asking for - // "a cable list" asks for the columns everybody gets. - w->setFields(CableList::defaultKeys()); - } - content = w; } else { - log(QStringLiteral("qet.%1: unknown kind '%2'; expected nomenclature, summary or cable_list").arg(caller, kind)); + log(QStringLiteral("qet.%1: unknown kind '%2'; expected nomenclature or summary").arg(caller, kind)); return -1; } diff --git a/sources/scripting/qetscriptapi.h b/sources/scripting/qetscriptapi.h index f6b8b0c0e..2e3f327ac 100644 --- a/sources/scripting/qetscriptapi.h +++ b/sources/scripting/qetscriptapi.h @@ -189,8 +189,7 @@ class QetGraphicsTableItem; button applies, not a rule reimplemented here. sortTerminalStrip() reorders the strip's physical positions into the canonical order the editor's own sort button computes. - - @b Tables: a BOM/nomenclature, a summary (table of contents) or a - cable list placed + - @b Tables: a BOM/nomenclature or a summary (table of contents) placed on a folio, through QetGraphicsTableFactory::create() -- the same factory call the "add table" menu action makes, minus the modal AddTableDialog it collects its settings from first. That dialog is @@ -202,11 +201,7 @@ class QetGraphicsTableItem; calling addTable() once should create exactly the one table it asked for, not possibly several spread across folios it never asked to add. A script that wants either behaviour can resize the result itself or - add its own folio. kind is "nomenclature" (over placed elements), - "summary" (over folios) or "cable_list" (over the cables of the - project): the first two require a real SQL query, the last takes - comma-joined column keys instead and accepts an empty query, which - means its default columns. + add its own folio. Neither creating nor deleting a table is undoable: QetGraphicsTableFactory::newTable(), which create() calls, calls @@ -391,7 +386,6 @@ class QetScriptApi : public QObject Q_INVOKABLE bool exportWires(const QString &output); Q_INVOKABLE bool exportBom(const QString &output, bool noSlaves = false, bool noJunctions = false); - Q_INVOKABLE bool exportCableList(const QString &output); Q_INVOKABLE bool exportWiring(const QString &output); Q_INVOKABLE bool exportNets(const QString &output); Q_INVOKABLE bool exportLinks(const QString &output); diff --git a/sources/ui/cablenumberingdialog.cpp b/sources/ui/cablenumberingdialog.cpp deleted file mode 100644 index ec833f062..000000000 --- a/sources/ui/cablenumberingdialog.cpp +++ /dev/null @@ -1,383 +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 "cablenumberingdialog.h" - -#include "../autoNum/ui/selectautonumw.h" -#include "../cable/cablerenumber.h" -#include "../cable/editcablecommand.h" -#include "../qetproject.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** - @brief CableNumberingDialog::CableNumberingDialog - Build the window around the numbering rule of the project: the rule - itself, the axis the cables are laid out along when the whole - project is numbered again, and the button which does that numbering. - @param project the project whose cables are numbered - @param parent -*/ -CableNumberingDialog::CableNumberingDialog(QETProject *project, QWidget *parent) : - QDialog(parent), - m_project(project) -{ - setWindowTitle(tr("Cable numbering")); - //The rule editor needs room to be read: this window opens with - //the size it needs rather than with whatever its contents - //happen to add up to, so nothing of the rule is cramped from - //the first moment - setMinimumSize(QSize(900, 620)); - - auto *layout = new QVBoxLayout(this); - - auto *note = new QLabel(tr( - "This window shows the automatic numbering rule for the cables " - "of this project and edits it. A rule changed here is the one " - "the project keeps: it is written into the project when you " - "apply it."), this); - note->setWordWrap(true); - layout->addWidget(note); - - m_rule = new SelectAutonumW(3, this); - //One rule only, so the row which picks between several named - //numberings is hidden: what is on show is the project's rule - m_rule->setSingleRuleMode(true); - layout->addWidget(m_rule); - - auto *axis_group = new QGroupBox(tr("Axis priority"), this); - auto *axis_layout = new QHBoxLayout(axis_group); - m_axis_x = new QRadioButton(tr("Priority to the X axis (horizontal)"), axis_group); - m_axis_y = new QRadioButton(tr("Priority to the Y axis (vertical)"), axis_group); - axis_layout->addWidget(m_axis_x); - axis_layout->addWidget(m_axis_y); - layout->addWidget(axis_group); - - auto *axis_note = new QLabel(tr( - "The chosen axis decides the order in which the cables are " - "renumbered: X numbers them from left to right, Y numbers them " - "from top to bottom."), this); - axis_note->setWordWrap(true); - layout->addWidget(axis_note); - - auto *actions = new QHBoxLayout(); - m_renumber_pb = new QPushButton(tr("Renumber all cables"), this); - m_renumber_pb->setToolTip(tr( - "Sets the number of each cable of the project again from the " - "rule, starting at the beginning: a number left missing after " - "a cable is deleted is filled in this way.")); - actions->addWidget(m_renumber_pb); - actions->addStretch(); - - //No confirm button: this window is there to number the cables - //again and to show the rule which does it. What the window - //holds is written by the editor's own Apply button, and by the - //numbering button which writes it before numbering -- so a - //second button only saving and closing would say the same - //thing twice. - m_box = new QDialogButtonBox(this); - m_box->addButton(QDialogButtonBox::Cancel); - m_box->button(QDialogButtonBox::Cancel)->setText(tr("Cancel")); - actions->addWidget(m_box); - layout->addLayout(actions); - - connect(m_box, &QDialogButtonBox::rejected, this, &QDialog::reject); - connect(m_renumber_pb, &QPushButton::clicked, this, &CableNumberingDialog::renumber); - //The editor's own buttons are wired the same way the project - //properties wire them, so this window saves exactly what that - //page would have saved - connect(m_rule, &SelectAutonumW::applyPressed, this, &CableNumberingDialog::applyRule); - connect(m_rule, &SelectAutonumW::removeClicked, this, &CableNumberingDialog::removeRule); - //Filling a rule in lights the numbering button up as soon as - //there is something to number with - connect(m_rule, &SelectAutonumW::contextEdited, this, - &CableNumberingDialog::updateEnabling); - - if (m_project) - { - const QString key = m_project->cableCurrentAutoNum(); - const bool has_rule = !key.isEmpty() - && !m_project->cableAutoNum(key).isEmpty(); - m_rule->setContext(has_rule ? m_project->cableAutoNum(key) - : NumerotationContext()); - //The button which takes the rule away is only worth showing - //when there is one, and only while the project may be - //written to - m_rule->setRuleRemovable(has_rule && !m_project->isReadOnly()); - - m_axis_x->setChecked(m_project->cableXAxisFirst()); - m_axis_y->setChecked(!m_project->cableXAxisFirst()); - - if (m_project->isReadOnly()) - { - m_rule->setEnabled(false); - m_axis_x->setEnabled(false); - m_axis_y->setEnabled(false); - } - } - updateEnabling(); -} - -/** - @brief CableNumberingDialog::applyRule - What the editor's own Apply button does: write the rule into the - project and leave the window open, the way that button behaves on - the numbering pages of the settings. -*/ -void CableNumberingDialog::applyRule() -{ - save(); - updateEnabling(); -} - -/** - @brief CableNumberingDialog::save - Write the rule and the axis into the project. Nothing is written - when nothing was changed, so confirming a window which was only - ever looked at leaves the project exactly as it was. - @return true when the window holds nothing which could not be written -*/ -bool CableNumberingDialog::save() -{ - if (!m_project || m_project->isReadOnly()) return false; - - bool changed = false; - - if (m_rule->isModified()) - { - if (!m_rule->isValid()) - { - QMessageBox::warning(this, tr("Cable numbering"), - tr("The rule is not complete yet: " - "there is still an empty field in it.")); - return false; - } - - const QString title = QETProject::cableAutoNumRuleName(); - m_project->addCableAutoNum(title, m_rule->toNumContext()); - m_project->setCurrentCableAutoNum(title); - //What was edited is written now, so there is nothing of it - //left for a second save to write again - m_rule->setContext(m_rule->toNumContext()); - m_rule->setRuleRemovable(true); - changed = true; - } - - const bool axis_first = m_axis_x->isChecked(); - if (m_project->cableXAxisFirst() != axis_first) changed = true; - m_project->setCableXAxisFirst(axis_first); - - if (changed) m_project->setModified(true); - return true; -} - -/** - @brief CableNumberingDialog::removeRule - Take the cable numbering rule out of the project. The project then - numbers its cables by no rule at all -- they are called W until one - is defined again -- and the cable tool asks once more whether one - should be defined. -*/ -void CableNumberingDialog::removeRule() -{ - if (!m_project || m_project->isReadOnly()) return; - - const QString title = m_project->cableCurrentAutoNum(); - if (title.isEmpty()) return; - - m_project->removeCableAutoNum(title); - m_rule->setContext(NumerotationContext()); - //No rule left, so the button which takes one away goes too. And - //having just taken the rule away means numbering cables by hand - //again: the question of defining one comes back the next time a - //cable is drawn, so it is not held back in this project any more. - m_rule->setRuleRemovable(false); - m_project->setCableAskNumbering(true); - m_project->setModified(true); - updateEnabling(); -} - -/** - @brief CableNumberingDialog::updateEnabling - Renumbering needs something to number with: a rule filled in well - enough to be one, and a project which may be written to. -*/ -void CableNumberingDialog::updateEnabling() -{ - if (!m_project || m_project->isReadOnly() || !m_rule) { - m_renumber_pb->setEnabled(false); - return; - } - - const QString key = m_project->cableCurrentAutoNum(); - const bool saved_rule = !key.isEmpty() - && !m_project->cableAutoNum(key).isEmpty(); - //A rule which is still being filled in counts as soon as it is - //written, and the numbering writes it together with the numbers - //it hands out - const bool drawn_rule = m_rule->isModified() && m_rule->isValid() - && !m_rule->toNumContext().isEmpty(); - - m_renumber_pb->setEnabled((saved_rule || drawn_rule) - && m_rule->isValid() - && !m_rule->toNumContext().isEmpty()); -} - -/** - @brief CableNumberingDialog::askAboutHandWritten - Some cables carry a name he typed in himself rather than one the - rule handed out. Taking such a name away is his to allow: yes and - they are numbered like all the others, no and they keep their name - while the rest is numbered around them, and cancelling leaves every - cable exactly where it stands. - @param count how many such names the project holds - @return 1 to number them too, 0 to leave them alone, -1 for nothing -*/ -int CableNumberingDialog::askAboutHandWritten(int count) -{ - if (count <= 0) return 1; - - QMessageBox box(QMessageBox::Question, tr("Cable numbering"), - tr("%n cable(s) of this project were entered by " - "hand.\n\n" - "Do you want to renumber them too?", "", count), - QMessageBox::NoButton, this); - QPushButton *take = box.addButton( - tr("Yes", "Les noms saisis à la main sont remplacés eux aussi."), - QMessageBox::YesRole); - QPushButton *leave = box.addButton( - tr("No", "Les noms saisis à la main sont laissés tels quels."), - QMessageBox::NoRole); - QPushButton *cancel = box.addButton( - tr("Cancel", "Ne renumérote rien du tout."), - QMessageBox::RejectRole); - //Keeping his own names is the safe answer under his finger - box.setDefaultButton(leave); - box.setEscapeButton(cancel); - box.exec(); - - const QAbstractButton *clicked = box.clickedButton(); - if (clicked == take) return 1; - if (clicked == leave) return 0; - return -1; -} - -/** - @brief CableNumberingDialog::renumber - Number every cable of the project again with the rule on show, - from the beginning of that rule and in the order the cables stand - along the chosen axis. One single undo step takes it all back, and - the window closes once the numbering has been written so that he - can see what became of his cables. -*/ -void CableNumberingDialog::renumber() -{ - if (!m_project || m_project->isReadOnly()) return; - - //A rule which is not complete yet is refused before anything is - //worked out at all. What the window holds is written only together - //with the numbering itself, so every way out coming before that - //point -- a rule which does not fit, him saying no, nothing to - //number -- leaves the project exactly as it stands. - const NumerotationContext rule = m_rule->toNumContext(); - if (!m_rule->isValid() || rule.isEmpty()) - { - QMessageBox::warning(this, tr("Cable numbering"), - tr("The rule is not complete yet.")); - return; - } - const bool axis_first = m_axis_x->isChecked(); - - //Whether he lets his own names be taken away or not, a project - //with nothing to number with says so before he is asked - //anything: the rule has to be one first - CableRenumberPlan plan = CableRenumber::plan(m_project, rule, axis_first, - true); - if (!plan.ok()) - { - QMessageBox::warning(this, tr("Cable numbering"), plan.error); - return; - } - - const int answer = askAboutHandWritten(CableRenumber::byHandCount(m_project)); - if (answer < 0) return; - - //Leaving his own names alone means the numbering runs around - //them, which can hand out a number somebody else keeps: that - //is worked out again here and refused if it does not fit - if (answer == 0) - { - plan = CableRenumber::plan(m_project, rule, axis_first, false); - if (!plan.ok()) - { - QMessageBox::warning(this, tr("Cable numbering"), plan.error); - return; - } - } - if (plan.cables.isEmpty()) - { - QMessageBox::information(this, tr("Cable numbering"), - tr("No cable to renumber in this project.")); - return; - } - - //Every cable already carries the number the rule says: there is - //nothing to write, and an undo step which undoes nothing would - //only be in the way - bool changed = false; - for (int i = 0; i < plan.cables.size(); ++i) - { - if (plan.cables.at(i)->designation() != plan.designations.at(i) - || plan.cables.at(i)->designationByHand()) { - changed = true; - break; - } - } - if (!changed) - { - QMessageBox::information(this, tr("Cable numbering"), - tr("All the cables already carry the number " - "that the rule gives them.")); - return; - } - - //The rule on show is written now, right with the numbering it - //belongs to, and the numbering then leaves the counters of that - //rule where the last number was taken from: both go into the - //project with the names, as one single act which undo takes - //back together. Nothing was written before this point, which is - //why he may still back out of here without the project having - //been touched. - if (!save()) return; - - const QString num_key = QETProject::cableAutoNumRuleName(); - if (QUndoStack *stack = m_project->undoStack()) { - stack->push(new RenumberCablesCommand( - plan.cables, plan.designations, m_project, num_key, - m_project->cableAutoNum(num_key), plan.after)); - } - accept(); -} diff --git a/sources/ui/cablenumberingdialog.h b/sources/ui/cablenumberingdialog.h deleted file mode 100644 index c52a65fb9..000000000 --- a/sources/ui/cablenumberingdialog.h +++ /dev/null @@ -1,86 +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 CABLENUMBERINGDIALOG_H -#define CABLENUMBERINGDIALOG_H - -#include - -class QDialogButtonBox; -class QETProject; -class QPushButton; -class QRadioButton; -class SelectAutonumW; - -/** - @brief The window behind the "Cable numbering" entry of the - numbering menu: the one numbering rule the project numbers its - cables with, the axis those cables are laid out along when the whole - project is numbered again, and the button which does that numbering. - - The rule is the very editor the project properties hold on their - cable tab, read from the project when the window opens and written - back into the project by that editor's own Apply button -- and by - the numbering button, which writes what the window holds before it - numbers, so numbering always happens with the rule on show: a rule - which is changed here is the rule the project keeps, not a rule of - its own. There is no confirm button of this window's own: what the - window holds is either applied by hand or left behind when it is - closed. - - Renumbering works every cable of the project out from the beginning - of that rule, so the hole a deleted cable leaves in the numbering - closes -- and it only happens when he asks for it. -*/ -class CableNumberingDialog : public QDialog -{ - Q_OBJECT - - public: - explicit CableNumberingDialog(QETProject *project, QWidget *parent = nullptr); - - private slots: - ///Put the rule and the axis into the project - void applyRule(); - ///Take the cable numbering rule out of the project - void removeRule(); - ///Number every cable of the project with the rule on show - void renumber(); - ///Let the button say whether there is a rule to number with - void updateEnabling(); - - private: - ///Write what the window holds into the project - bool save(); - /** - Ask what to do with the names he typed in himself. - @param count how many such names the project holds - @return 1 to number them too, 0 to leave them alone, - -1 to do nothing at all - */ - int askAboutHandWritten(int count); - - private: - QETProject *m_project = nullptr; - SelectAutonumW *m_rule = nullptr; - QRadioButton *m_axis_x = nullptr; - QRadioButton *m_axis_y = nullptr; - QPushButton *m_renumber_pb = nullptr; - QDialogButtonBox *m_box = nullptr; -}; - -#endif // CABLENUMBERINGDIALOG_H diff --git a/sources/ui/configpage/configpages.cpp b/sources/ui/configpage/configpages.cpp index 6144349ea..6cba2f872 100644 --- a/sources/ui/configpage/configpages.cpp +++ b/sources/ui/configpage/configpages.cpp @@ -108,26 +108,19 @@ NewDiagramPage::NewDiagramPage(QETProject *project, auto saw_conductor = new SelectAutonumW(1); auto saw_element = new SelectAutonumW(0); auto saw_folio = new SelectAutonumW(2); - auto saw_cable = new SelectAutonumW(3); - //A cable is numbered by one rule and that is all there is: - //the row which lists the available numberings is hidden. - saw_cable->setSingleRuleMode(true); initAutoNumTab(m_autonum_conductor, saw_conductor, QStringLiteral("autonum/conductor")); initAutoNumTab(m_autonum_element, saw_element, QStringLiteral("autonum/element")); initAutoNumTab(m_autonum_folio, saw_folio, QStringLiteral("autonum/folio")); - initAutoNumTab(m_autonum_cable, saw_cable, QStringLiteral("autonum/cable")); - m_autonum_cable.fixed_name = QETProject::cableAutoNumRuleName(); QSettings autonum_settings; loadAutoNumTab(m_autonum_conductor, autonum_settings); loadAutoNumTab(m_autonum_element, autonum_settings); loadAutoNumTab(m_autonum_folio, autonum_settings); - loadAutoNumTab(m_autonum_cable, autonum_settings); // Intercept Return key in the combo line edits so it doesn't // activate the dialog's default button (OK). - for (auto *tab : {&m_autonum_conductor, &m_autonum_element, &m_autonum_folio, &m_autonum_cable}) { + for (auto *tab : {&m_autonum_conductor, &m_autonum_element, &m_autonum_folio}) { if (QComboBox *combo = tab->widget->contextComboBox()) { if (combo->lineEdit()) { combo->lineEdit()->installEventFilter(this); @@ -176,7 +169,6 @@ NewDiagramPage::NewDiagramPage(QETProject *project, autonum_inner_tab->addTab(m_autonum_conductor.widget, tr("Conductors")); autonum_inner_tab->addTab(m_autonum_element.widget, tr("Elements")); autonum_inner_tab->addTab(m_autonum_folio.widget, tr("Sheets")); - autonum_inner_tab->addTab(m_autonum_cable.widget, tr("Cables")); autonum_layout->addWidget(autonum_inner_tab); tab_widget -> addTab (autonum_widget, tr("Auto Numbering")); } @@ -288,20 +280,6 @@ void NewDiagramPage::applyConf() settings.endArray(); // save global auto-numbering defaults - // - // The cable rule is written here as well as by its own Apply - // button. The cable tool asks a project for that rule the - // moment he tries to draw a cable, and a project built out of - // settings he has just filled in and confirmed must not turn - // up without one -- which is what would happen if only the - // Apply button of that one tab could save it. A rule nobody - // has finished filling in is left alone, and a tab he has not - // touched writes nothing at all. - if (m_autonum_cable.widget - && m_autonum_cable.widget->isModified() - && m_autonum_cable.widget->isValid()) { - saveAutoNumContext(m_autonum_cable); - } persistAutonumSettings(); } } @@ -415,12 +393,6 @@ void NewDiagramPage::loadAutoNumTab(AutoNumTab &tab, QSettings &settings) tab.widget->contextComboBox()->setCurrentText(data.second); tab.widget->setContext(tab.contexts.value(data.second)); } - //A tab which keeps one single rule can take it away again with - //a button of its own -- the row the others delete from is - //hidden here -- but only when there really is one to take away. - if (!tab.fixed_name.isEmpty()) { - tab.widget->setRuleRemovable(tab.contexts.contains(tab.fixed_name)); - } } /** @@ -430,25 +402,15 @@ void NewDiagramPage::loadAutoNumTab(AutoNumTab &tab, QSettings &settings) */ void NewDiagramPage::saveAutoNumContext(AutoNumTab &tab) { - // A tab which keeps one single rule has no visible combo box to name - // it with (the row is hidden): it saves under its fixed name. - QString name = tab.fixed_name; - if (name.isEmpty()) { - name = tab.widget->contextComboBox()->currentText().trimmed(); - if (name.isEmpty() || isPlaceholder(tab.widget->contextComboBox(), name)) { - return; - } + QString name = tab.widget->contextComboBox()->currentText().trimmed(); + if (name.isEmpty() || isPlaceholder(tab.widget->contextComboBox(), name)) { + return; } tab.contexts.insert(name, tab.widget->toNumContext()); if (tab.widget->contextComboBox()->findText(name) == -1) { tab.widget->contextComboBox()->addItem(name); } - tab.widget->contextComboBox()->setCurrentText(name); persistAutonumSettings(); - //There is a rule now, so there is something to take away again. - if (!tab.fixed_name.isEmpty()) { - tab.widget->setRuleRemovable(true); - } } /** @@ -457,35 +419,17 @@ void NewDiagramPage::saveAutoNumContext(AutoNumTab &tab) */ void NewDiagramPage::removeAutoNumContext(AutoNumTab &tab) { - QString name = tab.fixed_name; - if (name.isEmpty()) { - name = tab.widget->contextComboBox()->currentText().trimmed(); - if (name.isEmpty() || isPlaceholder(tab.widget->contextComboBox(), name)) { - return; - } + QString name = tab.widget->contextComboBox()->currentText().trimmed(); + if (name.isEmpty() || isPlaceholder(tab.widget->contextComboBox(), name)) { + return; } int idx = tab.widget->contextComboBox()->findText(name); - // A name which is not in the list was never saved, so there is - // nothing to take out -- unless it is a single rule, which is - // always under its fixed name whether the combo knows it or not. - if (idx == -1 && tab.fixed_name.isEmpty()) return; - if (idx != -1) { - tab.widget->contextComboBox()->removeItem(idx); - tab.widget->contextComboBox()->setCurrentText(QString()); - } + if (idx == -1) return; tab.contexts.remove(name); + tab.widget->contextComboBox()->removeItem(idx); + tab.widget->contextComboBox()->setCurrentText(QString()); tab.widget->setContext(NumerotationContext()); persistAutonumSettings(); - - //A tab which keeps one single rule has no list to delete it - //from, so this is where the button of its own goes away too. - //And having just taken the rule away means numbering cables by - //hand again: every project of its own asks once more whether - //one should be defined -- that answer is kept per project, so - //there is nothing to take back here. - if (!tab.fixed_name.isEmpty()) { - tab.widget->setRuleRemovable(false); - } } /** @@ -495,21 +439,12 @@ void NewDiagramPage::removeAutoNumContext(AutoNumTab &tab) void NewDiagramPage::persistAutonumSettings() { QSettings settings; - for (auto *tab : {&m_autonum_conductor, &m_autonum_element, &m_autonum_folio, &m_autonum_cable}) { + for (auto *tab : {&m_autonum_conductor, &m_autonum_element, &m_autonum_folio}) { QString current; - if (!tab->fixed_name.isEmpty()) { - // The one rule of a single-rule tab is in use as soon as - // it exists: there is nothing to choose between. - if (tab->contexts.contains(tab->fixed_name)) { - current = tab->fixed_name; - } - } - else { - QComboBox *combo = tab->widget->contextComboBox(); - if (!isPlaceholder(combo, combo->currentText().trimmed()) - && tab->contexts.contains(combo->currentText().trimmed())) { - current = combo->currentText().trimmed(); - } + QComboBox *combo = tab->widget->contextComboBox(); + if (!isPlaceholder(combo, combo->currentText().trimmed()) + && tab->contexts.contains(combo->currentText().trimmed())) { + current = combo->currentText().trimmed(); } NumerotationContext::saveToSettings(tab->contexts, current, settings, tab->prefix); @@ -527,7 +462,7 @@ bool NewDiagramPage::eventFilter(QObject *obj, QEvent *event) auto *ke = static_cast(event); if ((ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter)) { // Check if this is a line edit inside one of our autonum combos - for (auto *tab : {&m_autonum_conductor, &m_autonum_element, &m_autonum_folio, &m_autonum_cable}) { + for (auto *tab : {&m_autonum_conductor, &m_autonum_element, &m_autonum_folio}) { if (QComboBox *combo = tab->widget->contextComboBox()) { if (combo->lineEdit() && combo->lineEdit() == obj) { return true; // eat the event diff --git a/sources/ui/configpage/configpages.h b/sources/ui/configpage/configpages.h index e5676611a..9ddb2a523 100644 --- a/sources/ui/configpage/configpages.h +++ b/sources/ui/configpage/configpages.h @@ -82,16 +82,10 @@ public slots: SelectAutonumW *widget = nullptr; QHash contexts; QString prefix; - /// When set, this tab keeps one single rule, always saved - /// and read under this name: there is no visible combo box - /// to name a rule with (see SelectAutonumW:: - /// setSingleRuleMode). The cable numbering works that way. - QString fixed_name; }; AutoNumTab m_autonum_conductor; AutoNumTab m_autonum_element; AutoNumTab m_autonum_folio; - AutoNumTab m_autonum_cable; void initAutoNumTab(AutoNumTab &tab, SelectAutonumW *w, const QString &prefix); void loadAutoNumTab(AutoNumTab &tab, QSettings &settings); diff --git a/sources/ui/configpage/generalconfigurationpage.cpp b/sources/ui/configpage/generalconfigurationpage.cpp index ccbc24cdd..b2f7123dd 100644 --- a/sources/ui/configpage/generalconfigurationpage.cpp +++ b/sources/ui/configpage/generalconfigurationpage.cpp @@ -23,7 +23,6 @@ #include "../../qeticons.h" #include "ui_generalconfigurationpage.h" #include "../../materiallist/materiallist.h" -#include "../../cablelist/cabletypelist.h" #include "../../utils/qetsettings.h" #include "../../utils/qetutils.h" #include "../../qetmessagebox.h" @@ -34,7 +33,6 @@ #include "../../ElementsCollection/qetlabelsfile.h" #include "../prefixconfigurationdialog.h" #include "../nokde/kcolorbutton.h" -#include #include #include #include @@ -42,35 +40,6 @@ #include #include -#include "../../diagram.h" -#include "../../qetproject.h" - -namespace { - /** - @brief What one of those font buttons says: the family, the size - and the style, the way it would read on the sheet. - @param font the font that text is written with - @return e.g. "Liberation Sans 8,1 (Regular)" - */ - QString fontButtonLabel(const QFont &font) - { - QString style = font.styleName(); - if (font.bold()) { - style = font.italic() - ? QCoreApplication::translate("GeneralConfigurationPage", - "Bold italic") - : QCoreApplication::translate("GeneralConfigurationPage", "Bold"); - } else if (font.italic()) { - style = QCoreApplication::translate("GeneralConfigurationPage", "Italic"); - } else if (style.isEmpty()) { - style = QCoreApplication::translate("GeneralConfigurationPage", "Normal"); - } - return font.family() + QLatin1Char(' ') - + QString::number(font.pointSizeF(), 'g', 3) - + QLatin1String(" (") + style + QLatin1Char(')'); - } -} - /** @brief GeneralConfigurationPage::GeneralConfigurationPage @param parent @@ -223,17 +192,6 @@ GeneralConfigurationPage::GeneralConfigurationPage(QWidget *parent) : font.styleName() + ")"; ui->m_indi_text_font_pb->setText(fontInfos); } else { ui->m_indi_text_font_pb->setText("Liberation Sans 9 (Regular)"); } - - //Cable texts: what every cable writes along its own line when - //it has not been given a font of its own. - ui->m_cable_text_font_pb->setText(fontButtonLabel(QETApp::cableTextsFont())); - ui->m_cable_core_font_pb->setText(fontButtonLabel(QETApp::cableCoreFont())); - - //Where those texts stop in relation to the beginning of their - //own line, for the cables which say nothing about it themselves - const int align_index = int(QETApp::cableTextAlignment()) == int(Qt::AlignLeft) ? 2 - : (int(QETApp::cableTextAlignment()) == int(Qt::AlignHCenter) ? 1 : 0); - ui->m_cable_align_cb->setCurrentIndex(align_index); ui->m_highlight_integrated_elements->setChecked(settings.value("diagrameditor/highlight-integrated-elements", true).toBool()); ui->m_default_elements_info->setPlainText(settings.value("elementeditor/default-informations", "").toString()); @@ -313,17 +271,6 @@ GeneralConfigurationPage::GeneralConfigurationPage(QWidget *parent) : .arg(MaterialList::defaultPath())); } - //CABLE TYPE FILE - path = CableTypeList::configuredPath(); - ui->m_cable_type_list_path_le->setText(path); - if (path.isEmpty()) - { - ui->m_cable_type_list_path_le->setPlaceholderText( - tr("Not set (default: %1)", - "hint shown in the cable type file field when no file is configured yet") - .arg(CableTypeList::defaultPath())); - } - fillLang(); } @@ -536,30 +483,6 @@ void GeneralConfigurationPage::applyConf() //kept as chosen even when it doesn't exist yet : the user may well //point QElectroTech at a file he intends to write later. MaterialList::setConfiguredPath(ui->m_material_list_path_le->text().trimmed()); - - //CABLE TYPE FILE - //Same rule as the material file : it is a plain file, kept as - //chosen even when it doesn't exist yet. - CableTypeList::setConfiguredPath(ui->m_cable_type_list_path_le->text().trimmed()); - - //The way a cable lines up its texts at the beginning of its - //line, for every cable which says nothing about it itself -- - //written now, so the sheets drawn again just below show it. - const int align_index = ui->m_cable_align_cb->currentIndex(); - QETApp::setCableTextAlignment(align_index == 2 ? Qt::AlignLeft - : (align_index == 1 - ? Qt::AlignHCenter - : Qt::AlignRight)); - - //Every folio is drawn again: the texts of the cables are written - //from the settings, so that what he just picked reaches the - //sheets without his having to touch anything on them first. - for (QETProject *project : QETApp::registeredProjects()) - { - for (Diagram *diagram : project->diagrams()) { - diagram->update(); - } - } } /** @@ -678,45 +601,6 @@ void GeneralConfigurationPage::on_m_dyn_text_font_pb_clicked() } } -/** - @brief GeneralConfigurationPage::on_m_cable_text_font_pb_clicked - Let him pick the font the texts of a cable are written with by - default: designation, type, installation, location and length. - - Every cable which has not been given a font of its own follows it, - from now on: one which has keeps the one it was given, which is why - those are set on the cable itself rather than here. -*/ -void GeneralConfigurationPage::on_m_cable_text_font_pb_clicked() -{ - bool ok = false; - const QFont font = QFontDialog::getFont(&ok, QETApp::cableTextsFont(), this, - tr("Cable text font")); - if (!ok) return; - - QSettings settings; - settings.setValue("cable-management/cable-text-font", QETUtils::fontToString(font)); - ui->m_cable_text_font_pb->setText(fontButtonLabel(QETApp::cableTextsFont())); -} - -/** - @brief GeneralConfigurationPage::on_m_cable_core_font_pb_clicked - Let him pick the font the colours of the cores are written with by - default -- all of them together, the way they are drawn: one row of - colours beside the line, never set apart from each other. -*/ -void GeneralConfigurationPage::on_m_cable_core_font_pb_clicked() -{ - bool ok = false; - const QFont font = QFontDialog::getFont(&ok, QETApp::cableCoreFont(), this, - tr("Core colors font")); - if (!ok) return; - - QSettings settings; - settings.setValue("cable-management/cable-core-font", QETUtils::fontToString(font)); - ui->m_cable_core_font_pb->setText(fontButtonLabel(QETApp::cableCoreFont())); -} - @@ -954,70 +838,6 @@ void GeneralConfigurationPage::on_m_material_list_create_pb_clicked() ui->m_material_list_path_le->setText(path); } -/** - @brief GeneralConfigurationPage::on_m_cable_type_list_browse_pb_clicked - Let the user pick an existing cable type file. -*/ -void GeneralConfigurationPage::on_m_cable_type_list_browse_pb_clicked() -{ - QString start_dir = ui->m_cable_type_list_path_le->text(); - start_dir = start_dir.isEmpty() - ? QETApp::documentDir() - : QFileInfo(start_dir).absolutePath(); - - const QString path = QFileDialog::getOpenFileName( - this, - tr("Select the cable types file"), - start_dir, - tr("CSV files (*.csv)")); - - if (!path.isEmpty()) { - ui->m_cable_type_list_path_le->setText(path); - } -} - -/** - @brief GeneralConfigurationPage::on_m_cable_type_list_create_pb_clicked - Create the cable type file with its header line, so the columns are - known before the user fills them from his spreadsheet. -*/ -void GeneralConfigurationPage::on_m_cable_type_list_create_pb_clicked() -{ - QString path = ui->m_cable_type_list_path_le->text(); - path = path.isEmpty() - ? CableTypeList::defaultPath() - : QFileInfo(path).absolutePath() + QLatin1Char('/') + CableTypeList::defaultFileName(); - - path = QFileDialog::getSaveFileName( - this, - tr("Create the cable types file"), - path, - tr("CSV files (*.csv)")); - if (path.isEmpty()) { - return; - } - if (QFileInfo(path).suffix().isEmpty()) { - path += QStringLiteral(".csv"); - } - - //An existing file is kept as it is : this button creates the - //header, it never overwrites a catalogue. - if (CableTypeList::isEmptyFile(path)) - { - QString error; - if (!CableTypeList::createFile(path, &error)) - { - QET::QetMessageBox::critical(this, - tr("Cannot be created"), - tr("Cannot create the file:\n%1\n%2") - .arg(path, error)); - return; - } - } - - ui->m_cable_type_list_path_le->setText(path); -} - void GeneralConfigurationPage::on_m_indi_text_font_pb_clicked() { bool ok; diff --git a/sources/ui/configpage/generalconfigurationpage.h b/sources/ui/configpage/generalconfigurationpage.h index e613c7b88..4954a6d3c 100644 --- a/sources/ui/configpage/generalconfigurationpage.h +++ b/sources/ui/configpage/generalconfigurationpage.h @@ -50,11 +50,7 @@ class GeneralConfigurationPage : public ConfigPage void on_m_prefix_pb_clicked(); void on_m_material_list_browse_pb_clicked(); void on_m_material_list_create_pb_clicked(); - void on_m_cable_type_list_browse_pb_clicked(); - void on_m_cable_type_list_create_pb_clicked(); void on_m_indi_text_font_pb_clicked(); - void on_m_cable_text_font_pb_clicked(); - void on_m_cable_core_font_pb_clicked(); void on_MaxPartsElementEditorList_sb_valueChanged(int value); void on_DiagramEditor_Grid_PointSize_min_sb_valueChanged(int value); void on_ElementEditor_Grid_PointSize_min_sb_valueChanged(int value); diff --git a/sources/ui/configpage/generalconfigurationpage.ui b/sources/ui/configpage/generalconfigurationpage.ui index 4114b51ee..3edac907e 100644 --- a/sources/ui/configpage/generalconfigurationpage.ui +++ b/sources/ui/configpage/generalconfigurationpage.ui @@ -600,47 +600,6 @@ - - - - Cable type list file - - - - - - - - - Directory of the cable types used to define a cable (number of cores, colors, cross-section) - - - true - - - Not set - - - - - - - Browse... - - - - - - - Create the cable type list file with its header line - - - Create... - - - - - @@ -905,113 +864,6 @@ - - - - Cable texts - - - - - - Designation, type, plant, location, length : - - - true - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Font of the texts written along the cable line; a cable which has its own keeps it. - - - - - - - - - - Core colors (all of them) : - - - true - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Font of the core colors written along the line; a cable which has its own keeps it. - - - - - - - - - - Text alignment : - - - true - - - - - - - Where the texts of a cable stop relative to the start of its line; a cable which has its own keeps it. - - - - Right aligned - - - - - Centred - - - - - Left aligned - - - - - - - diff --git a/sources/ui/configpage/projectconfigpages.cpp b/sources/ui/configpage/projectconfigpages.cpp index 6710fbc3e..4e1c62964 100644 --- a/sources/ui/configpage/projectconfigpages.cpp +++ b/sources/ui/configpage/projectconfigpages.cpp @@ -471,20 +471,9 @@ QIcon ProjectAutoNumConfigPage::icon() const /** @brief ProjectAutoNumConfigPage::applyProjectConf - Save what this page has to save when the window is confirmed. - The cable rule is written from here as well as by its own Apply - button: the cable tool asks the project for that rule the moment - he tries to draw a cable, so filling it in and confirming this - window only to be asked once more would be a surprise. A rule he - has not finished filling in is left alone, and a page he has not - touched leaves the project exactly as it was. */ void ProjectAutoNumConfigPage::applyProjectConf() -{ - if (m_saw_cable && m_saw_cable->isModified() && m_saw_cable->isValid()) { - saveContextCable(); - } -} +{} namespace { /** @@ -566,12 +555,6 @@ void ProjectAutoNumConfigPage::initWidgets() m_saw_folio = new SelectAutonumW(2); m_saw_folio->setExplicitNaming(); tab_widget->addTab(schemeTab(m_saw_folio, m_folio_users_box, m_folio_users), tr("Sheets")); - - //Cable tab: one rule only, so the row which lists the available - //numberings stays hidden and there is nothing to choose between. - m_saw_cable = new SelectAutonumW(3); - m_saw_cable->setSingleRuleMode(true); - tab_widget->addTab(m_saw_cable, tr("Cables")); //AutoNumbering Tab m_faw = new FolioAutonumberingW(project()); @@ -606,7 +589,6 @@ void ProjectAutoNumConfigPage::readValuesFromProject() m_saw_conductor->contextComboBox()->clear(); m_saw_element->contextComboBox()->clear(); m_saw_folio->contextComboBox()->clear(); - m_saw_cable->contextComboBox()->clear(); //Conductor Tab refreshSchemes(SchemeKind::Conductor, m_project->conductorCurrentAutoNum()); @@ -616,25 +598,6 @@ void ProjectAutoNumConfigPage::readValuesFromProject() //Folio Tab refreshSchemes(SchemeKind::Folio, QString()); - - //Cable tab: the rule goes straight into the editor, since with the - //row of available numberings hidden he can never pick it out of - //the combo box himself. With no rule at all the editor shows an - //empty one to fill in, exactly like a numbering which has just - //been opened for the first time. - const QStringList strlk(m_project->cableAutoNum().keys()); - m_saw_cable->contextComboBox()->addItems(strlk); - const QString cable_rule = m_project->cableCurrentAutoNum(); - const bool cable_rule_ok = !cable_rule.isEmpty() - && !m_project->cableAutoNum(cable_rule).isEmpty(); - if (cable_rule_ok) { - m_saw_cable->setContext(m_project->cableAutoNum(cable_rule)); - } else { - m_saw_cable->setContext(NumerotationContext()); - } - //The button which takes the rule away is only worth showing when - //there is one, and only clickable when the project allows changes. - m_saw_cable->setRuleRemovable(cable_rule_ok && !m_project->isReadOnly()); //Folio AutoNumbering Tab m_faw->setContext(m_project->folioAutoNum().keys()); @@ -649,15 +612,6 @@ void ProjectAutoNumConfigPage::adjustReadOnly() if (m_import_pb && m_project) { m_import_pb->setDisabled(m_project->isReadOnly()); } - //The button which takes the cable numbering rule away does the - //same thing as the import button: nothing while the project - //cannot be written. - if (m_saw_cable && m_project) { - const QString title = m_project->cableCurrentAutoNum(); - m_saw_cable->setRuleRemovable(!m_project->isReadOnly() - && !title.isEmpty() - && !m_project->cableAutoNum(title).isEmpty()); - } } /** @@ -690,11 +644,6 @@ void ProjectAutoNumConfigPage::buildConnections() connect(m_saw_folio, &SelectAutonumW::renameClicked, this, &ProjectAutoNumConfigPage::renameContextFolio); connect(m_saw_folio->contextComboBox(), &QComboBox::textActivated, this, &ProjectAutoNumConfigPage::updateContextFolio); - //Cable tab: one rule, so there is no name to pick in the hidden - //combo box -- applying always writes the single rule. - connect(m_saw_cable, &SelectAutonumW::applyPressed, this, &ProjectAutoNumConfigPage::saveContextCable); - connect(m_saw_cable, &SelectAutonumW::removeClicked, this, &ProjectAutoNumConfigPage::removeContextCable); - // Auto Folio Numbering connect(m_faw, &FolioAutonumberingW::applyPressed, this, &ProjectAutoNumConfigPage::applyAutoNum); @@ -1085,66 +1034,6 @@ void ProjectAutoNumConfigPage::saveContextElement() } } -/** - @brief ProjectAutoNumConfigPage::saveContextCable - Save the cable numbering rule into the project. - - There is one rule only, and it has no name the user could have typed - (the row which holds those names is hidden): it always goes in under - the fixed name, and it is the rule in use from the moment it exists. -*/ -void ProjectAutoNumConfigPage::saveContextCable() -{ - if (!m_project || m_project->isReadOnly()) return; - - const QString title = QETProject::cableAutoNumRuleName(); - m_project->addCableAutoNum(title, m_saw_cable->toNumContext()); - m_project->setCurrentCableAutoNum(title); - - //Keep the hidden combo box in step with what the project holds, - //so the next reading of this page shows the rule just saved. - if (m_saw_cable->contextComboBox()->findText(title) == -1) { - m_saw_cable->contextComboBox()->addItem(title); - } - m_saw_cable->contextComboBox()->setCurrentText(title); - //There is a rule now, so there is something to take away again. - m_saw_cable->setRuleRemovable(true); - //This is applied straight away rather than at the end of the - //window, so the project has to say so itself: otherwise the rule - //would be dropped by a Cancel which comes after it. - m_project->setModified(true); -} - -/** - @brief ProjectAutoNumConfigPage::removeContextCable - Take the cable numbering rule out of the project. The project then - numbers its cables by no rule at all -- they are called W until one - is defined again -- and the cable tool asks once more whether one - should be defined. -*/ -void ProjectAutoNumConfigPage::removeContextCable() -{ - if (!m_project || m_project->isReadOnly()) return; - - const QString title = m_project->cableCurrentAutoNum(); - if (title.isEmpty()) return; - - m_project->removeCableAutoNum(title); - const int idx = m_saw_cable->contextComboBox()->findText(title); - if (idx != -1) { - m_saw_cable->contextComboBox()->removeItem(idx); - } - m_saw_cable->contextComboBox()->setCurrentText(QString()); - m_saw_cable->setContext(NumerotationContext()); - //No rule left, so the button which takes one away goes too. And - //having just taken the rule away means numbering cables by hand - //again: the question of defining one comes back the next time a - //cable is drawn, so it is not held back in this project any more. - m_saw_cable->setRuleRemovable(false); - m_project->setCableAskNumbering(true); - m_project->setModified(true); -} - /** @brief ProjectAutoNumConfigPage::importFromProject Read the automatic numbering rules stored in another .qet project and @@ -1764,14 +1653,3 @@ void ProjectAutoNumConfigPage::changeToTab(int i) m_tab_widget->setCurrentIndex(i); } } - -/** - @brief ProjectAutoNumConfigPage::changeToCableTab - Show the cable tab -- the one the cable tool opens when it finds out - that the project has no numbering rule yet. -*/ -void ProjectAutoNumConfigPage::changeToCableTab() -{ - //Tabs in order : Management, Conductors, Elements, Folios, Cables - changeToTab(4); -} diff --git a/sources/ui/configpage/projectconfigpages.h b/sources/ui/configpage/projectconfigpages.h index 39df7702f..435df2c62 100644 --- a/sources/ui/configpage/projectconfigpages.h +++ b/sources/ui/configpage/projectconfigpages.h @@ -160,9 +160,6 @@ class ProjectAutoNumConfigPage : public ProjectConfigPage { QIcon icon() const override; void applyProjectConf() override; virtual void changeToTab(int); - ///Jump straight to the cable tab (used when the cable tool - ///asks whether a numbering rule should be defined) - void changeToCableTab(); protected: void initWidgets() override; void initLayout() override {} @@ -180,8 +177,6 @@ class ProjectAutoNumConfigPage : public ProjectConfigPage { void updateContextElement(const QString&);//element void saveContextElement(); void removeContextElement(); - void saveContextCable(); //cable: one rule, no name to pick - void removeContextCable(); void newContextElement(); void renameContextElement(); void refreshElementSchemes(const QString &selected); @@ -224,7 +219,6 @@ class ProjectAutoNumConfigPage : public ProjectConfigPage { SelectAutonumW *m_saw_conductor; SelectAutonumW *m_saw_folio; SelectAutonumW *m_saw_element; - SelectAutonumW *m_saw_cable; QTabWidget *m_tab_widget = nullptr; QGroupBox *m_conductor_users_box = nullptr; QTableWidget *m_conductor_users = nullptr; diff --git a/sources/ui/projectpropertiesdialog.cpp b/sources/ui/projectpropertiesdialog.cpp index 81ab0223f..f27f71464 100644 --- a/sources/ui/projectpropertiesdialog.cpp +++ b/sources/ui/projectpropertiesdialog.cpp @@ -94,20 +94,6 @@ void ProjectPropertiesDialog::changeToFolio() autoNumPage->changeToTab(3); } -/** - @brief ProjectPropertiesDialog::changeToCable - Change the current displayed tab to the cable numbering tab. - The caller is the cable tool, which opens this window when it finds - that the project numbers its cables by no rule at all. -*/ -void ProjectPropertiesDialog::changeToCable() -{ - ProjectAutoNumConfigPage *autoNumPage = - static_cast ( - m_properties_dialog->pages.at(2)); - autoNumPage->changeToCableTab(); -} - /** @brief ProjectPropertiesDialog::changeToElement Change the current displayed tab to the element numberings tab. diff --git a/sources/ui/projectpropertiesdialog.h b/sources/ui/projectpropertiesdialog.h index e54c9f866..075423907 100644 --- a/sources/ui/projectpropertiesdialog.h +++ b/sources/ui/projectpropertiesdialog.h @@ -41,8 +41,6 @@ class ProjectPropertiesDialog : public QObject { void exec(); void setCurrentPage(ProjectPropertiesDialog::Page); void changeToFolio(); - ///Show the automatic numbering page with the cable tab on top - void changeToCable(); void changeToElement(); private: diff --git a/sources/ui/xrefpropertieswidget.cpp b/sources/ui/xrefpropertieswidget.cpp index 0ee65331e..f3f44d5fa 100644 --- a/sources/ui/xrefpropertieswidget.cpp +++ b/sources/ui/xrefpropertieswidget.cpp @@ -17,16 +17,12 @@ */ #include -#include #include #include -#include #include "xrefpropertieswidget.h" #include "ui_xrefpropertieswidget.h" #include "qdebug.h" -#include "../qetapp.h" -#include "../utils/qetutils.h" /** @brief XRefPropertiesWidget::XRefPropertiesWidget @@ -41,12 +37,10 @@ XRefPropertiesWidget::XRefPropertiesWidget(QHash prope { ui->setupUi(this); buildUi(); - fillMissingTypes(); connect(ui->m_display_has_cross_rb, &QRadioButton::toggled, ui->m_cross_properties_gb, &QWidget::setEnabled); connect(ui->m_display_has_contacts_rb, &QRadioButton::toggled, ui->m_show_all_slaves_cb, &QWidget::setEnabled); connect(ui->m_type_cb, qOverload(&QComboBox::currentIndexChanged), this, &XRefPropertiesWidget::typeChanged); connect(ui->m_snap_to_cb, qOverload(&QComboBox::currentIndexChanged), this, &XRefPropertiesWidget::enableOffsetSB); - connect(ui->m_font_pb, &QPushButton::clicked, this, &XRefPropertiesWidget::chooseXRefFont); updateDisplay(); } @@ -60,7 +54,6 @@ XRefPropertiesWidget::~XRefPropertiesWidget() disconnect(ui->m_display_has_contacts_rb, &QRadioButton::toggled, ui->m_show_all_slaves_cb, &QWidget::setEnabled); disconnect(ui->m_type_cb, qOverload(&QComboBox::currentIndexChanged), this, &XRefPropertiesWidget::typeChanged); disconnect(ui->m_snap_to_cb, qOverload(&QComboBox::currentIndexChanged), this, &XRefPropertiesWidget::enableOffsetSB); - disconnect(ui->m_font_pb, &QPushButton::clicked, this, &XRefPropertiesWidget::chooseXRefFont); delete ui; } @@ -72,33 +65,10 @@ XRefPropertiesWidget::~XRefPropertiesWidget() void XRefPropertiesWidget::setProperties(const QHash &properties) { m_properties = properties; - fillMissingTypes(); updateDisplay(); m_previous_type_index = ui->m_type_cb->currentIndex(); } -/** - @brief XRefPropertiesWidget::fillMissingTypes - Make sure every type the combo box offers has properties of its own. - - A project built before a type existed does not hold that type, and - neither does a settings file written before it. Without an entry the - form would make one up out of thin air, and for a cable that would be - the very default of the coil ("%f-%l%c"), which reads as a stray dash - behind the folio number and two empty numbers after it. -*/ -void XRefPropertiesWidget::fillMissingTypes() -{ - const QHash defaults = XRefProperties::defaultProperties(); - for (int i = 0; i < ui->m_type_cb->count(); ++i) - { - const QString type = ui->m_type_cb->itemData(i).toString(); - if (!m_properties.contains(type)) { - m_properties.insert(type, defaults.value(type)); - } - } -} - /** @brief XRefPropertiesWidget::properties @return the properties edited by this widget @@ -118,9 +88,6 @@ void XRefPropertiesWidget::setReadOnly(bool ro) { ui->m_type_cb->setDisabled(ro); ui->m_display_gb->setDisabled(ro); ui->m_cross_properties_gb->setDisabled(ro); - //The texts stand on their own since they are needed for a cable - //too, and they are as much a setting as the rest. - ui->m_labels_gb->setDisabled(ro); if (!ro && ui->m_display_has_contacts_rb->isChecked()) { ui->m_cross_properties_gb->setDisabled(true); @@ -133,16 +100,10 @@ void XRefPropertiesWidget::setReadOnly(bool ro) { */ void XRefPropertiesWidget::buildUi() { - //Wording of the master label as the .ui wrote it, kept to put it - //back for every type which does not rename it. - m_master_label_text = ui->label_6->text(); ui -> m_type_cb -> addItem(tr("Coil"), "coil"); ui -> m_type_cb -> addItem(tr("Organ of protection"), "protection"); ui -> m_type_cb -> addItem(tr("Switch / button"), "commutator"); ui -> m_type_cb -> addItem(tr("Programmable Logic Controller (PLC)"), "plc"); - //Not an element: the type of the cross references a cable writes - //in its own label when it runs on several folios. - ui -> m_type_cb -> addItem(tr("Cable"), "cable"); ui -> m_snap_to_cb -> addItem(tr("Footer"), "bottom"); ui -> m_snap_to_cb -> addItem(tr("Under the label of the element"), "label"); @@ -191,7 +152,6 @@ void XRefPropertiesWidget::saveProperties(int index) { xrp.setPrefix("switch", ui->m_switch_prefix_le->text()); xrp.setMasterLabel(ui->m_master_le->text()); xrp.setSlaveLabel(ui->m_slave_le->text()); - xrp.setFont(m_current_font); //The boxes cannot show a value below their minimum (the offset's //minimum is its "Default" entry, standing for the stored 0): keep //the stored value unless the box shows something else. @@ -225,10 +185,6 @@ void XRefPropertiesWidget::updateDisplay() QString master = xrp.masterLabel(); ui->m_master_le->setText(master); - //Which font that type's reference is written with: the button - //offers it for a cable alone, but every type carries its own. - m_current_font = xrp.font(); - QString slave = xrp.slaveLabel(); ui->m_slave_le->setText(slave); @@ -280,42 +236,8 @@ void XRefPropertiesWidget::updateDisplay() ui->m_display_has_cross_rb->setVisible(!is_plc); ui->m_show_terminal_name_cb->setVisible(!is_plc); ui->m_show_all_slaves_cb->setVisible(!is_plc); - - //A cable is not an element: it draws no cross of its own and stands - //at no place of the sheet to be pointed at. Its reference is a line - //of the label of the cable itself, written once per other folio the - //cable runs on, so the whole presentation (where a cross is put, how - //it is drawn) and the text of the slave have no meaning for it: what - //is left is the one text which is written into that line. - const bool is_cable = type == QLatin1String("cable"); - ui->m_display_gb->setVisible(!is_cable); - ui->label_7->setVisible(!is_cable); - ui->m_slave_le->setVisible(!is_cable); - //The wording of every type which is not a cable comes back from - //the .ui, so the label cannot drift away from the designer one. - ui->label_6->setText(is_cable ? tr("Text:") : m_master_label_text); - - //Only a cable writes its reference into its own label, so only a - //cable has a text whose font to choose here: the cross references - //of the elements keep the font they have always been drawn with. - ui->m_font_pb->setVisible(is_cable); - if (m_current_font.isEmpty()) - { - ui->m_font_pb->setToolTip(tr("Font and size: those of the cable texts")); - } - else - { - QFont font; - if (QETUtils::fontFromString(font, m_current_font)) { - ui->m_font_pb->setToolTip(tr("Font and size: %1, %2 pt") - .arg(font.family()).arg(font.pointSizeF())); - } - } - - //A cable stacks the lines of its references by itself, so the - //option is only worth showing for the elements. - ui->m_stack_overlapping_cb->setVisible(!is_plc && !is_cable); - ui->m_cross_properties_gb->setVisible(!is_plc && !is_cable); + ui->m_stack_overlapping_cb->setVisible(!is_plc); + ui->m_cross_properties_gb->setVisible(!is_plc); } /** @@ -345,31 +267,3 @@ void XRefPropertiesWidget::enableOffsetSB(int i){ ui->m_offset_sb->setEnabled(true); ui->m_stack_overlapping_cb->setEnabled(ui->m_offset_sb->isEnabled()); } - -/** - @brief XRefPropertiesWidget::chooseXRefFont - Let him pick the font -- family, size and style, all three at once -- - the reference of a cable is written with. - - The font is only kept when he presses OK: cancelling the dialog - leaves the previous one standing. What he picked is written into the - properties by saveProperties(), the way every other field of this - form is, so leaving the type or pressing OK in the settings window - is what really stores it. -*/ -void XRefPropertiesWidget::chooseXRefFont() -{ - QFont initial; - if (m_current_font.isEmpty() || !QETUtils::fontFromString(initial, m_current_font)) { - initial = QETApp::cableTextsFont(); - } - - bool ok = false; - const QFont font = QFontDialog::getFont(&ok, initial, this, - tr("Font of the cross-reference text")); - if (!ok) return; - - m_current_font = QETUtils::fontToString(font); - ui->m_font_pb->setToolTip(tr("Font and size: %1, %2 pt") - .arg(font.family()).arg(font.pointSizeF())); -} diff --git a/sources/ui/xrefpropertieswidget.h b/sources/ui/xrefpropertieswidget.h index 12a1a7099..8bd255b74 100644 --- a/sources/ui/xrefpropertieswidget.h +++ b/sources/ui/xrefpropertieswidget.h @@ -46,9 +46,7 @@ class XRefPropertiesWidget : public QWidget private: void buildUi(); - void fillMissingTypes(); void saveProperties(int index); - void chooseXRefFont(); private slots: void updateDisplay(); @@ -60,10 +58,6 @@ class XRefPropertiesWidget : public QWidget Ui::XRefPropertiesWidget *ui; QHash m_properties; int m_previous_type_index; - ///The font picked for the type shown, as the settings describe it - QString m_current_font; - ///Wording of the master label, as the .ui gives it - QString m_master_label_text; }; #endif // XREFPROPERTIESWIDGET_H diff --git a/sources/ui/xrefpropertieswidget.ui b/sources/ui/xrefpropertieswidget.ui index 5e3ac7f71..06e3568ca 100644 --- a/sources/ui/xrefpropertieswidget.ui +++ b/sources/ui/xrefpropertieswidget.ui @@ -189,110 +189,100 @@ - - - - - - - - - Label cross reference - - + + - - - - - - 0 - 0 - - - - - 48 - 0 - - - - Master - - - - - - - - - - %f-%l%c - - - - - - - Choose the font and size of the cable cross-reference text - - - Font… - - - - - - - - - - - - 0 - 0 - - - - - 48 - 0 - - - - Slave - - - false - - - - - - - (%f-%l%c) - - - - - - - - - Create your own text using the following variables: + + + Label cross reference + + + + + + + + + 0 + 0 + + + + + 48 + 0 + + + + Master + + + + + + + + + + %f-%l%c + + + + + + + + + + + + 0 + 0 + + + + + 48 + 0 + + + + Slave + + + false + + + + + + + (%f-%l%c) + + + + + + + + + Create your own text using the following variables: %f: the sheet number %F: the sheet label %l: the row number %c: the column number %M: the installation %LM: the location - + + + + - - - + + + diff --git a/sources/undocommand/deleteqgraphicsitemcommand.cpp b/sources/undocommand/deleteqgraphicsitemcommand.cpp index 521bee8eb..5de7f90af 100644 --- a/sources/undocommand/deleteqgraphicsitemcommand.cpp +++ b/sources/undocommand/deleteqgraphicsitemcommand.cpp @@ -18,7 +18,6 @@ #include "deleteqgraphicsitemcommand.h" #include "../wiringrules.h" -#include "../cable/cablemanager.h" #include "../diagram.h" #include "../qetproject.h" #include "addgraphicsobjectcommand.h" @@ -338,15 +337,6 @@ void DeleteQGraphicsItemCommand::undo() } } - //Back on the sheet and linked again: the cables which run over - //this report claim their wire on the other side once more. - for (Element *e : m_removed_contents.m_elements) { - if (e && (e->linkType() & Element::AllReport)) { - CableManager::refreshLabels(m_diagram->project()); - break; - } - } - QUndoCommand::undo(); } @@ -400,16 +390,5 @@ void DeleteQGraphicsItemCommand::redo() for(QGraphicsItem *item : m_removed_contents.items()) m_diagram->removeItem(item); - //A folio report carries a cable core over to the next sheet: - //taking one away (putting it back is undo, below) changes which - //wires belong to which cable, so the cable fields are worked - //out again straight away. - for (Element *e : m_removed_contents.m_elements) { - if (e && (e->linkType() & Element::AllReport)) { - CableManager::refreshLabels(m_diagram->project()); - break; - } - } - QUndoCommand::redo(); } diff --git a/sources/undocommand/linkelementcommand.cpp b/sources/undocommand/linkelementcommand.cpp index e5e2f3162..0599eb8ad 100644 --- a/sources/undocommand/linkelementcommand.cpp +++ b/sources/undocommand/linkelementcommand.cpp @@ -17,7 +17,6 @@ */ #include "linkelementcommand.h" -#include "../cable/cablemanager.h" #include "../conductorautonumerotation.h" #include "../diagram.h" #include "../diagramposition.h" @@ -60,23 +59,6 @@ static QString plcCrossRefText(Element *master, Element *slave) xrp.slaveLabel(), seq, master->diagram(), master); } -/** - @brief refreshCableLabelsForReport - A folio report carries a cable core over to the next sheet, so - making or breaking such a link changes which wires belong to which - cable. Work the cable fields out again straight away, so the wire - coming out of the linked arrow shows the cable right after the link - is made -- and drops it right after the link is taken away. - @param element the element the link was made or broken on -*/ -static void refreshCableLabelsForReport(Element *element) -{ - if (!element || !(element->linkType() & Element::AllReport)) return; - if (Diagram *diagram = element->diagram()) { - CableManager::refreshLabels(diagram->project()); - } -} - /** @brief LinkElementCommand::LinkElementCommand Constructor @@ -320,7 +302,6 @@ void LinkElementCommand::undo() } makeLink(m_linked_before); - refreshCableLabelsForReport(m_element); QUndoCommand::undo(); } @@ -348,7 +329,6 @@ void LinkElementCommand::redo() } m_first_redo = false; } - refreshCableLabelsForReport(m_element); QUndoCommand::redo(); } diff --git a/tests/qttest/CMakeLists.txt b/tests/qttest/CMakeLists.txt index d871a96dd..3572ed416 100644 --- a/tests/qttest/CMakeLists.txt +++ b/tests/qttest/CMakeLists.txt @@ -87,15 +87,6 @@ if(Python3_Interpreter_FOUND AND CMAKE_GENERATOR STREQUAL "Ninja") --source ${CMAKE_CURRENT_SOURCE_DIR}/attached_plain_paste_probe.cpp --fixture ${CMAKE_CURRENT_SOURCE_DIR}/fixtures/unlinked_contact_label.qet) set_tests_properties(tst_attachedplainpaste PROPERTIES TIMEOUT 300 RUN_SERIAL TRUE) - # Deleting a cable, undoing the delete twice and then editing anything - # has both cable commands tearing down the same cable. Runs under the - # same objects the application uses, so a second free is caught. - add_test(NAME tst_cableundointegration - COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/run_cable_undo_probe.py - --build ${CMAKE_BINARY_DIR} --binary $ - --source ${CMAKE_CURRENT_SOURCE_DIR}/cable_undo_probe.cpp - --fixture ${CMAKE_CURRENT_SOURCE_DIR}/fixtures/unlinked_contact_label.qet) - set_tests_properties(tst_cableundointegration PROPERTIES TIMEOUT 300 RUN_SERIAL TRUE) endif() if(Python3_Interpreter_FOUND AND CMAKE_GENERATOR STREQUAL "Ninja") add_test(NAME tst_darkimageintegration @@ -263,23 +254,6 @@ add_test(NAME tst_legacyelementtypes COMMAND tst_legacyelementtypes) target_include_directories(tst_legacyelementtypes PRIVATE ${QET_DIR}/sources) target_link_libraries(tst_legacyelementtypes PRIVATE Qt::Test) -# The cable type catalogue is plain data (it reads and writes the CSV file -# holding the cable kinds), so it is compiled here on its own, together with -# the material file helpers it shares and the element information wording. -add_executable( - tst_cabletypelist - tst_cabletypelist.cpp - ${QET_DIR}/sources/cablelist/cabletypelist.cpp - ${QET_DIR}/sources/cablelist/cabletypelist.h - ${QET_DIR}/sources/materiallist/materiallist.cpp - ${QET_DIR}/sources/materiallist/materiallist.h - ${QET_DIR}/sources/qetinformation.cpp - ${QET_DIR}/sources/qetinformation.h) -add_test(NAME tst_cabletypelist COMMAND tst_cabletypelist) -target_include_directories(tst_cabletypelist PRIVATE ${QET_DIR}/sources) -# qetinformation.h uses QRegularExpressionValidator, which lives in QtGui. -target_link_libraries(tst_cabletypelist PRIVATE Qt::Test Qt::Gui) - add_executable( tst_qetpalette tst_qetpalette.cpp diff --git a/tests/qttest/cable_undo_probe.cpp b/tests/qttest/cable_undo_probe.cpp deleted file mode 100644 index b02cc06df..000000000 --- a/tests/qttest/cable_undo_probe.cpp +++ /dev/null @@ -1,105 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -// Cable undo ownership regression, linked against the application's objects. -// -// Deleting a cable and undoing the delete twice leaves the cable out of -// the project while AddCableCommand still holds it, and the next edit -// makes the stack throw both commands away. Whoever of the two runs -// first frees the cable; the other one must not follow it into the freed -// memory. A raw pointer there meant freeing the same cable twice, which -// a normal build hides and AddressSanitizer reports as a heap -// use-after-free in ~AddCableCommand. -#include -#include -#include -#include -#include -#include -#include - -#include "../../sources/diagram.h" -#include "../../sources/qetproject.h" -#include "../../sources/qetmessagebox.h" -#include "../../sources/cable/addcablecommand.h" -#include "../../sources/cable/cable.h" -#include "../../sources/cable/cablepart.h" -#include "../../sources/cable/editcablecommand.h" - -static void check(bool value, const char *message) -{ - if (!value) throw std::runtime_error(message); -} - -// Any edit the user makes afterwards: pushing it is what makes the stack -// discard the commands it still holds undone, and that is where the two -// destructors meet the same cable. -struct AnyEdit : QUndoCommand -{ - void redo() override {} - void undo() override {} -}; - -int main(int argc, char **argv) -{ - QApplication app(argc, argv); - std::freopen(argv[2], "w", stdout); - - QTemporaryDir settings; - QSettings::setDefaultFormat(QSettings::IniFormat); - QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, settings.path()); - QCoreApplication::setOrganizationName("QETCableUndoRegression"); - QETProject::setBackupEnabled(false); - QET::QetMessageBox::setNonInteractive(true); - QFontDatabase::addApplicationFont(":/fonts/LiberationSans-Regular.ttf"); - - try - { - QETProject project(QString::fromLocal8Bit(argv[1])); - check(project.state() == QETProject::Ok, "fixture opens"); - Diagram *scene = project.diagrams().first(); - - // The state the cable tool leaves behind when it has drawn one - // line and the dialog accepted it: a cable holding the section, - // a line on the folio pointing back at that cable. - CablePartData data; - data.uuid = QUuid::createUuid(); - data.diagram = scene->uuid(); - data.p1 = QPointF(100, 100); - data.p2 = QPointF(300, 100); - - //The cable the create dialog hands over: owned by the project, - //which is the only way it may enter the project's list at all. - auto *cable = project.newCable(); - cable->addPart(data); - auto *item = new CablePart(data); - item->setCable(cable); - scene->addItem(item); - - // Draw the line. - scene->undoStack().push(new AddCableCommand(cable, item, scene, true)); - check(project.cables().contains(cable), "drawn cable is in the project"); - - // Select all and delete. - scene->undoStack().push(new RemoveCableCommand(scene, {item})); - check(!project.cables().contains(cable), "delete took the cable out of the project"); - - // Ctrl+Z, Ctrl+Z: the cable is in nobody's hands now, but both - // commands still stand and both think they may have to clean it up. - scene->undoStack().undo(); - scene->undoStack().undo(); - check(!project.cables().contains(cable), "cable orphaned by two undos"); - - // The next edit of any kind: the stack throws the undone commands - // away, and ~RemoveCableCommand frees the cable before - // ~AddCableCommand gets to look at it. - scene->undoStack().push(new AnyEdit); - - check(project.state() == QETProject::Ok, "project survives the edit"); - std::puts("PASS: deleting a cable, undoing twice and editing again frees it exactly once"); - } - catch (const std::exception &error) - { - std::printf("FAIL: %s\n", error.what()); - return 1; - } - return 0; -} diff --git a/tests/qttest/run_cable_undo_probe.py b/tests/qttest/run_cable_undo_probe.py deleted file mode 100755 index 7613ec164..000000000 --- a/tests/qttest/run_cable_undo_probe.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env python3 -"""Link the cable undo integration probe using an existing Ninja app build. - -Reuses app objects without duplicating the app or introducing a core-library -refactor. No application source or existing executable is replaced. The -generated manifest, probe binary, reports and export fixtures stay in build/. -""" -import argparse -import pathlib -import re -import shutil -import subprocess -import sys - -parser = argparse.ArgumentParser() -parser.add_argument('--build', required=True) -parser.add_argument('--binary', required=True) -parser.add_argument('--source', required=True) -parser.add_argument('--fixture', required=True) -args = parser.parse_args() -build = pathlib.Path(args.build).resolve() -binary = pathlib.Path(args.binary).resolve() -source = pathlib.Path(args.source).resolve() -fixture = pathlib.Path(args.fixture).resolve() -manifest = (build / 'build.ninja').read_text(encoding='utf-8') -match = re.search(r'^build (\S*?/sources/main\.cpp\.(?:obj|o)):', manifest, re.MULTILINE) -if not match: - sys.exit('Cannot find the application main object in the Ninja build.') -main_object = match.group(1) -probe_object = main_object.replace('main.cpp.', 'cable_undo_probe.cpp.') -probe_binary = binary.with_name('cable_undo_probe' + binary.suffix) -def escape(path): - return pathlib.Path(path).as_posix().replace('$', '$$').replace(':', '$:').replace(' ', '$ ') -main_source = source.parents[2] / 'sources' / 'main.cpp' -manifest = manifest.replace(main_object, probe_object) -manifest = manifest.replace(escape(main_source), escape(source)) -if binary.suffix: - manifest = manifest.replace(binary.name, probe_binary.name) -else: - old_target = escape(binary.name if binary.parent == build else binary) - new_target = escape(probe_binary.name if probe_binary.parent == build else probe_binary) - manifest = manifest.replace('build ' + old_target + ':', 'build ' + new_target + ':') - old_file = binary.name if binary.parent == build else binary.as_posix() - new_file = probe_binary.name if probe_binary.parent == build else probe_binary.as_posix() - manifest = re.sub(r'^ TARGET_FILE = ' + re.escape(old_file) + r'$', - ' TARGET_FILE = ' + new_file, manifest, flags=re.MULTILINE) -probe_manifest = build / 'cable-undo-probe.ninja' -probe_manifest.write_text(manifest, encoding='utf-8') -ninja = shutil.which('ninja') -if not ninja: - sys.exit('Ninja is required for the integration probe.') -target = probe_binary.name if probe_binary.parent == build else str(probe_binary) -subprocess.run([ninja, '-C', str(build), '-f', probe_manifest.name, target], check=True) -output = build / 'cable-undo' -output.mkdir(exist_ok=True) -report = output / 'results.txt' -result = subprocess.run([str(probe_binary), str(fixture), str(report), str(output)], timeout=90) -if report.exists(): - print(report.read_text(encoding='utf-8', errors='replace')) -sys.exit(result.returncode) diff --git a/tests/qttest/tst_cabletypelist.cpp b/tests/qttest/tst_cabletypelist.cpp deleted file mode 100644 index 65f1cb2b1..000000000 --- a/tests/qttest/tst_cabletypelist.cpp +++ /dev/null @@ -1,205 +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 "cablelist/cabletypelist.h" - -#include -#include -#include - -/** - The cable type catalogue is read back the way it was written, and the - three columns it really uses are the only ones a new file gets: - - - a catalogue carrying the material file's whole shape (ten columns, - two header lines) reads as ten columns, its machine header line is - not mistaken for a cable, and the type comes out of it : this is - the catalogue a user already has on disk; - - a file the program creates itself holds the two header lines and - the three columns, no more; - - a file written by hand with the label line alone still gives the - type, so dropping the machine header never costs a column; - - and a line of cables is never taken for the machine header, even - when it happens to be written in key shaped words. -*/ -class tst_cabletypelist : public QObject -{ - Q_OBJECT - - QTemporaryDir m_dir; - - QString write(const QString &name, const QString &content) - { - const QString path = m_dir.filePath(name); - QFile file(path); - if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { - return {}; - } - file.write(content.toUtf8()); - file.close(); - return path; - } - - bool load(const QString &path, CableTypeListData *data) - { - QString error; - const bool ok = CableTypeList::load(path, data, &error); - if (!ok) { - qWarning("%s", qPrintable(error)); - } - return ok; - } - - private slots: - void tenColumnCatalogueReadsBack() - { - const QString path = write(QStringLiteral("ten.csv"), - QStringLiteral( - "Désignation;Nombre d'âmes;Couleurs des âmes;Section du câble;Couleur du fourreau;Fabricant;Numéro de commande;Fournisseur;Description textuelle;Notes\n" - "designation;cores;core_colors;section;color;manufacturer;manufacturer_reference;supplier;description;notes\n" - "H07V-K 3G1,5;3;wh|bn|bl;1.5;PVC;Nexans;X123;Rexel;courant fort;note\n" - "NYM-J 3G2,5;3;wh|bn|bl;2.5;PVC;;;;;\n")); - QVERIFY(!path.isEmpty()); - - CableTypeListData data; - QVERIFY2(load(path, &data), "the catalogue could not be read"); - - //The machine header line is not one of the cables... - QCOMPARE(data.records.size(), 2); - //... and every column it names is read under that name, even the - //ones this program never gave a meaning to. - QCOMPARE(data.columns, QStringList({ - QStringLiteral("designation"), QStringLiteral("cores"), - QStringLiteral("core_colors"), QStringLiteral("section"), - QStringLiteral("color"), QStringLiteral("manufacturer"), - QStringLiteral("manufacturer_reference"), - QStringLiteral("supplier"), QStringLiteral("description"), - QStringLiteral("notes")})); - - QCOMPARE(CableTypeList::designation(data.records.at(0)), - QStringLiteral("H07V-K 3G1,5")); - QCOMPARE(CableTypeList::coreCount(data.records.at(0)), 3); - QCOMPARE(CableTypeList::coreColors(data.records.at(0)), - QStringList({QStringLiteral("wh"), QStringLiteral("bn"), - QStringLiteral("bl")})); - //A column outside the three the program uses keeps its name and - //its data. - QCOMPARE(data.records.at(0).value(QStringLiteral("section")), - QStringLiteral("1.5")); - QCOMPARE(CableTypeList::designation(data.records.at(1)), - QStringLiteral("NYM-J 3G2,5")); - } - - void newFileHasThreeColumns() - { - const QString path = m_dir.filePath(QStringLiteral("new.csv")); - QVERIFY2(CableTypeList::createFile(path), "the file was not created"); - - CableTypeListData data; - QVERIFY(load(path, &data)); - QCOMPARE(data.columns, QStringList({ - QStringLiteral("designation"), QStringLiteral("cores"), - QStringLiteral("core_colors")})); - QVERIFY(data.records.isEmpty()); - //The label line reads the way the cable form names the type. - QCOMPARE(CableTypeList::translatedHeader(data.columns), QStringList({ - QStringLiteral("Designation"), - QStringLiteral("Number of cores"), - QStringLiteral("Core colors")})); - - CableTypeRecord record; - record.setValue(QStringLiteral("designation"), - QStringLiteral("H07V-K 3G1,5")); - record.setValue(QStringLiteral("cores"), QStringLiteral("3")); - record.setValue(QStringLiteral("core_colors"), - QStringLiteral("wh|bn|bl")); - QVERIFY2(CableTypeList::appendRecord(path, record), - "the record was not appended"); - - CableTypeListData read_back; - QVERIFY(load(path, &read_back)); - QCOMPARE(read_back.columns, data.columns); - QCOMPARE(read_back.records.size(), 1); - QCOMPARE(CableTypeList::designation(read_back.records.first()), - QStringLiteral("H07V-K 3G1,5")); - QCOMPARE(CableTypeList::coreCount(read_back.records.first()), 3); - QCOMPARE(CableTypeList::coreColors(read_back.records.first()), - QStringList({QStringLiteral("wh"), QStringLiteral("bn"), - QStringLiteral("bl")})); - } - - //The folder a cable type file goes into may not exist yet: a fresh - //profile has no documents folder, and a path typed into the settings - //may name one which was never created. The write makes the folder - //rather than failing with a message which says nothing about why. - void aMissingFolderIsCreated() - { - const QString path = m_dir.filePath( - QStringLiteral("not/there/yet/qet_cable_types.csv")); - QVERIFY(!QFile::exists(path)); - - QString error; - QVERIFY2(CableTypeList::createFile(path, &error), qPrintable(error)); - QVERIFY2(QFile::exists(path), "the file was not created"); - - CableTypeListData data; - QVERIFY2(load(path, &data), "the file just created could not be read"); - QVERIFY(data.records.isEmpty()); - } - - void labelLineAloneStillGivesTheType() - { - const QString path = write(QStringLiteral("hand.csv"), - QStringLiteral( - "Désignation;Nombre d'âmes;Couleurs des âmes\n" - "H07V-K 3G1,5;3;wh|bn|bl\n")); - QVERIFY(!path.isEmpty()); - - CableTypeListData data; - QVERIFY2(load(path, &data), "the hand written file could not be read"); - QCOMPARE(data.columns, QStringList({ - QStringLiteral("designation"), QStringLiteral("cores"), - QStringLiteral("core_colors")})); - QCOMPARE(data.records.size(), 1); - QCOMPARE(CableTypeList::designation(data.records.first()), - QStringLiteral("H07V-K 3G1,5")); - QCOMPARE(CableTypeList::coreCount(data.records.first()), 3); - } - - void aCableIsNeverMistakenForTheHeader() - { - //Three cells which all look like column keys, none of which means - //anything here : far more likely to be a cable than a header. - const QString path = write(QStringLiteral("unknown.csv"), - QStringLiteral( - "Désignation;Nombre d'âmes;Couleurs des âmes\n" - "ymj;;wh\n" - "H07V-K 3G1,5;3;wh|bn|bl\n")); - QVERIFY(!path.isEmpty()); - - CableTypeListData data; - QVERIFY2(load(path, &data), "the file could not be read"); - QCOMPARE(data.records.size(), 2); - QCOMPARE(CableTypeList::designation(data.records.first()), - QStringLiteral("ymj")); - QCOMPARE(CableTypeList::designation(data.records.last()), - QStringLiteral("H07V-K 3G1,5")); - } -}; - -QTEST_APPLESS_MAIN(tst_cabletypelist) -#include "tst_cabletypelist.moc"