mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-06-08 19:23:13 +02:00
Compare commits
47 Commits
d85aff0c0f
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 6c4711a8d0 | |||
| 8a8a338a2e | |||
| 407cc7a4c2 | |||
| 5cb8930732 | |||
| a24acfac24 | |||
| 8b0b1d10d4 | |||
| 57dfa28674 | |||
| 3848c7821a | |||
| 1572c23d51 | |||
| e234f063f8 | |||
| be21604ad0 | |||
| e1ccc1e568 | |||
| e202b5bc2b | |||
| 2b7e62f901 | |||
| 23e8258ae1 | |||
| 457d265f0a | |||
| cd76b6a1d6 | |||
| b522a94556 | |||
| 399bc0e897 | |||
| de9eeed542 | |||
| c071e92c58 | |||
| d22e4abf96 | |||
| 38b91e8083 | |||
| 19704cf5ca | |||
| 471d1f2538 | |||
| dc52105868 | |||
| 1b8dc5f410 | |||
| 26d5d019cc | |||
| 6dd7c2d926 | |||
| 47796e183a | |||
| ddf5ffcd89 | |||
| 7d718bb9a0 | |||
| d949e6eb8c | |||
| dbda958261 | |||
| d691489165 | |||
| 202ea38e40 | |||
| 86dafcb576 | |||
| f416c2a97e | |||
| 7426fedba3 | |||
| 027050c7e7 | |||
| fc948ad963 | |||
| 24d075b64c | |||
| f914f91e77 | |||
| fe03a0f643 | |||
| a7ad0278a6 | |||
| f517489421 | |||
| c8fa1c9fa4 |
@@ -75,10 +75,21 @@ jobs:
|
|||||||
$count = git rev-list HEAD --count 2>$null
|
$count = git rev-list HEAD --count 2>$null
|
||||||
$rev = [int]$count + 473
|
$rev = [int]$count + 473
|
||||||
$verDisplay = "${ver}-r${rev}-${sha}_x86_64-win64"
|
$verDisplay = "${ver}-r${rev}-${sha}_x86_64-win64"
|
||||||
|
# Generate a unique ProductCode GUID from the commit SHA
|
||||||
|
# This ensures MajorUpgrade always triggers, even for same-version builds
|
||||||
|
$fullSha = git rev-parse HEAD 2>$null
|
||||||
|
if (-not $fullSha) { $fullSha = [System.Guid]::NewGuid().ToString() }
|
||||||
|
$bytes = [System.Text.Encoding]::UTF8.GetBytes($fullSha)
|
||||||
|
$md5 = [System.Security.Cryptography.MD5]::Create().ComputeHash($bytes)
|
||||||
|
$guidBytes = [byte[]]$md5[0..15]
|
||||||
|
$productGuid = [System.Guid]::new($guidBytes).ToString().ToUpper()
|
||||||
|
|
||||||
echo "VERSION_MSI=$verMsi" >> $env:GITHUB_OUTPUT
|
echo "VERSION_MSI=$verMsi" >> $env:GITHUB_OUTPUT
|
||||||
echo "VERSION_DISPLAY=$verDisplay" >> $env:GITHUB_OUTPUT
|
echo "VERSION_DISPLAY=$verDisplay" >> $env:GITHUB_OUTPUT
|
||||||
|
echo "PRODUCT_GUID=$productGuid" >> $env:GITHUB_OUTPUT
|
||||||
Write-Host "Version MSI : $verMsi"
|
Write-Host "Version MSI : $verMsi"
|
||||||
Write-Host "Version display : $verDisplay"
|
Write-Host "Version display : $verDisplay"
|
||||||
|
Write-Host "Product GUID : $productGuid"
|
||||||
|
|
||||||
# ----------------------------------------------------------------
|
# ----------------------------------------------------------------
|
||||||
# 4. Install WiX v7, accept EULA and install WixUI extension
|
# 4. Install WiX v7, accept EULA and install WixUI extension
|
||||||
@@ -202,10 +213,13 @@ jobs:
|
|||||||
Write-Host " LicenseRtf : $licRtf"
|
Write-Host " LicenseRtf : $licRtf"
|
||||||
Write-Host " Output : dist\$outputName"
|
Write-Host " Output : dist\$outputName"
|
||||||
|
|
||||||
|
$productGuid = "${{ steps.version.outputs.PRODUCT_GUID }}"
|
||||||
|
|
||||||
wix build $wxs `
|
wix build $wxs `
|
||||||
-arch x64 `
|
-arch x64 `
|
||||||
-d "Version=$version" `
|
-d "Version=$version" `
|
||||||
-d "ProductVersion=$verDisplay" `
|
-d "ProductVersion=$verDisplay" `
|
||||||
|
-d "ProductCode=$productGuid" `
|
||||||
-d "FilesDir=$filesDir" `
|
-d "FilesDir=$filesDir" `
|
||||||
-d "LicenseRtf=$licRtf" `
|
-d "LicenseRtf=$licRtf" `
|
||||||
-ext WixToolset.UI.wixext `
|
-ext WixToolset.UI.wixext `
|
||||||
@@ -221,7 +235,35 @@ jobs:
|
|||||||
echo "MSI_NAME=$outputName" >> $env:GITHUB_ENV
|
echo "MSI_NAME=$outputName" >> $env:GITHUB_ENV
|
||||||
|
|
||||||
# ----------------------------------------------------------------
|
# ----------------------------------------------------------------
|
||||||
# 10. Upload the MSI artifact
|
# 10. Sign the MSI with SignPath
|
||||||
|
# ----------------------------------------------------------------
|
||||||
|
- name: Install SignPath PowerShell module
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
Install-Module -Name SignPath -Force -Scope CurrentUser
|
||||||
|
|
||||||
|
- name: Sign MSI with SignPath
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$msi = Get-ChildItem "$env:GITHUB_WORKSPACE\dist\*.msi" | Select-Object -First 1
|
||||||
|
if (-not $msi) {
|
||||||
|
Write-Error "No .msi found in dist/"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
Write-Host "Signing: $($msi.FullName)"
|
||||||
|
Submit-SigningRequest `
|
||||||
|
-InputArtifactPath $msi.FullName `
|
||||||
|
-ApiToken "${{ secrets.SIGNPATH_API_TOKEN }}" `
|
||||||
|
-OrganizationId "${{ secrets.SIGNPATH_ORGANIZATION_ID }}" `
|
||||||
|
-ProjectSlug "MSI" `
|
||||||
|
-SigningPolicySlug "test-signing" `
|
||||||
|
-OutputArtifactPath $msi.FullName `
|
||||||
|
-Force `
|
||||||
|
-WaitForCompletion
|
||||||
|
Write-Host "Signing complete: $($msi.Name)"
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------
|
||||||
|
# 11. Upload the MSI artifact
|
||||||
# ----------------------------------------------------------------
|
# ----------------------------------------------------------------
|
||||||
- name: Upload MSI artifact
|
- name: Upload MSI artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
|
|||||||
+2
-1
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
include(cmake/hoto_update_cmake_message.cmake)
|
include(cmake/hoto_update_cmake_message.cmake)
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.14...3.19 FATAL_ERROR)
|
cmake_minimum_required(VERSION 3.5...4.2)
|
||||||
|
|
||||||
project(qelectrotech
|
project(qelectrotech
|
||||||
VERSION 0.100.1
|
VERSION 0.100.1
|
||||||
@@ -145,6 +145,7 @@ target_include_directories(
|
|||||||
${QET_DIR}/sources/dataBase/ui
|
${QET_DIR}/sources/dataBase/ui
|
||||||
${QET_DIR}/sources/factory/ui
|
${QET_DIR}/sources/factory/ui
|
||||||
${QET_DIR}/sources/print
|
${QET_DIR}/sources/print
|
||||||
|
${QET_DIR}/sources/svg
|
||||||
)
|
)
|
||||||
|
|
||||||
install(TARGETS ${PROJECT_NAME})
|
install(TARGETS ${PROJECT_NAME})
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
Name="QElectroTech"
|
Name="QElectroTech"
|
||||||
Manufacturer="QElectroTech Team"
|
Manufacturer="QElectroTech Team"
|
||||||
Version="$(var.Version)"
|
Version="$(var.Version)"
|
||||||
|
ProductCode="$(var.ProductCode)"
|
||||||
UpgradeCode="A1B2C3D4-E5F6-7890-ABCD-EF1234567890"
|
UpgradeCode="A1B2C3D4-E5F6-7890-ABCD-EF1234567890"
|
||||||
Language="1033"
|
Language="1033"
|
||||||
Codepage="1252"
|
Codepage="1252"
|
||||||
@@ -28,6 +29,7 @@
|
|||||||
<!-- In-place upgrade: automatically uninstalls the previous version -->
|
<!-- In-place upgrade: automatically uninstalls the previous version -->
|
||||||
<MajorUpgrade
|
<MajorUpgrade
|
||||||
DowngradeErrorMessage="A newer version of QElectroTech is already installed."
|
DowngradeErrorMessage="A newer version of QElectroTech is already installed."
|
||||||
|
AllowSameVersionUpgrades="yes"
|
||||||
Schedule="afterInstallInitialize" />
|
Schedule="afterInstallInitialize" />
|
||||||
|
|
||||||
<!-- Installation directory -->
|
<!-- Installation directory -->
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ if(BUILD_PUGIXML)
|
|||||||
FetchContent_Declare(
|
FetchContent_Declare(
|
||||||
pugixml
|
pugixml
|
||||||
GIT_REPOSITORY https://github.com/zeux/pugixml.git
|
GIT_REPOSITORY https://github.com/zeux/pugixml.git
|
||||||
GIT_TAG v1.11.4)
|
GIT_TAG v1.15)
|
||||||
|
|
||||||
FetchContent_MakeAvailable(pugixml)
|
FetchContent_MakeAvailable(pugixml)
|
||||||
else()
|
else()
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ set(QET_COMPONENTS
|
|||||||
set(QET_PRIVATE_LIBRARIES
|
set(QET_PRIVATE_LIBRARIES
|
||||||
Qt::PrintSupport
|
Qt::PrintSupport
|
||||||
Qt::Gui
|
Qt::Gui
|
||||||
|
Qt::GuiPrivate # Required for QPdfEngine::drawHyperlink (PDF internal links)
|
||||||
Qt::Xml
|
Qt::Xml
|
||||||
Qt::Svg
|
Qt::Svg
|
||||||
Qt::Sql
|
Qt::Sql
|
||||||
@@ -499,6 +500,8 @@ set(QET_SRC_FILES
|
|||||||
${QET_DIR}/sources/SearchAndReplace/ui/replacefoliowidget.h
|
${QET_DIR}/sources/SearchAndReplace/ui/replacefoliowidget.h
|
||||||
${QET_DIR}/sources/SearchAndReplace/ui/searchandreplacewidget.cpp
|
${QET_DIR}/sources/SearchAndReplace/ui/searchandreplacewidget.cpp
|
||||||
${QET_DIR}/sources/SearchAndReplace/ui/searchandreplacewidget.h
|
${QET_DIR}/sources/SearchAndReplace/ui/searchandreplacewidget.h
|
||||||
|
${QET_DIR}/sources/svg/qetsvg.cpp
|
||||||
|
${QET_DIR}/sources/svg/qetsvg.h
|
||||||
|
|
||||||
${QET_DIR}/sources/titleblock/dimension.cpp
|
${QET_DIR}/sources/titleblock/dimension.cpp
|
||||||
${QET_DIR}/sources/titleblock/dimension.h
|
${QET_DIR}/sources/titleblock/dimension.h
|
||||||
@@ -713,6 +716,8 @@ set(QET_SRC_FILES
|
|||||||
|
|
||||||
${QET_DIR}/sources/xml/terminalstripitemxml.cpp
|
${QET_DIR}/sources/xml/terminalstripitemxml.cpp
|
||||||
${QET_DIR}/sources/xml/terminalstripitemxml.h
|
${QET_DIR}/sources/xml/terminalstripitemxml.h
|
||||||
|
${QET_DIR}/sources/xml/terminalstriplayoutpatternxml.cpp
|
||||||
|
${QET_DIR}/sources/xml/terminalstriplayoutpatternxml.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(TS_FILES
|
set(TS_FILES
|
||||||
|
|||||||
+1
-1
Submodule elements updated: f6a422ab00...3aab395fc4
+509
-443
File diff suppressed because it is too large
Load Diff
+509
-443
File diff suppressed because it is too large
Load Diff
+509
-443
File diff suppressed because it is too large
Load Diff
+509
-444
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+510
-444
File diff suppressed because it is too large
Load Diff
+509
-443
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+509
-443
File diff suppressed because it is too large
Load Diff
+509
-443
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+508
-442
File diff suppressed because it is too large
Load Diff
+508
-442
File diff suppressed because it is too large
Load Diff
+506
-440
File diff suppressed because it is too large
Load Diff
+510
-444
File diff suppressed because it is too large
Load Diff
+506
-440
File diff suppressed because it is too large
Load Diff
+509
-443
File diff suppressed because it is too large
Load Diff
+505
-439
File diff suppressed because it is too large
Load Diff
+506
-440
File diff suppressed because it is too large
Load Diff
+509
-443
File diff suppressed because it is too large
Load Diff
+508
-442
File diff suppressed because it is too large
Load Diff
+509
-443
File diff suppressed because it is too large
Load Diff
+512
-446
File diff suppressed because it is too large
Load Diff
+506
-440
File diff suppressed because it is too large
Load Diff
+512
-446
File diff suppressed because it is too large
Load Diff
+508
-442
File diff suppressed because it is too large
Load Diff
+509
-443
File diff suppressed because it is too large
Load Diff
+508
-442
File diff suppressed because it is too large
Load Diff
+508
-442
File diff suppressed because it is too large
Load Diff
+508
-442
File diff suppressed because it is too large
Load Diff
+506
-440
File diff suppressed because it is too large
Load Diff
+297
-4861
File diff suppressed because it is too large
Load Diff
+506
-440
File diff suppressed because it is too large
Load Diff
+506
-440
File diff suppressed because it is too large
Load Diff
+5
-1
@@ -230,7 +230,11 @@ RESOURCES += qelectrotech.qrc
|
|||||||
TRANSLATIONS += lang/*.ts
|
TRANSLATIONS += lang/*.ts
|
||||||
|
|
||||||
# Modules Qt utilises par l'application
|
# Modules Qt utilises par l'application
|
||||||
QT += xml svg network sql widgets printsupport concurrent KWidgetsAddons KCoreAddons
|
QT += xml svg network sql widgets printsupport concurrent KWidgetsAddons KCoreAddons gui-private
|
||||||
|
|
||||||
|
# Private Qt GUI headers (needed for QPdfEngine::drawHyperlink)
|
||||||
|
# gui-private should add this automatically, but some distros need it explicit
|
||||||
|
INCLUDEPATH += $$[QT_INSTALL_HEADERS]/QtGui/$$[QT_VERSION]/QtGui
|
||||||
|
|
||||||
# UI DESIGNER FILES AND GENERATION SOURCES FILES
|
# UI DESIGNER FILES AND GENERATION SOURCES FILES
|
||||||
FORMS += $$files(sources/richtext/*.ui) \
|
FORMS += $$files(sources/richtext/*.ui) \
|
||||||
|
|||||||
@@ -383,7 +383,7 @@ void projectDataBase::createElementNomenclatureView()
|
|||||||
"ei.supplier_auxiliary4 AS supplier_auxiliary4,"
|
"ei.supplier_auxiliary4 AS supplier_auxiliary4,"
|
||||||
"ei.quantity_auxiliary4 AS quantity_auxiliary4,"
|
"ei.quantity_auxiliary4 AS quantity_auxiliary4,"
|
||||||
"ei.unity_auxiliary4 AS unity_auxiliary4,"
|
"ei.unity_auxiliary4 AS unity_auxiliary4,"
|
||||||
|
"ei.exclude_from_bom AS exclude_from_bom,"
|
||||||
|
|
||||||
"d.pos AS diagram_position,"
|
"d.pos AS diagram_position,"
|
||||||
"e.type AS element_type,"
|
"e.type AS element_type,"
|
||||||
@@ -392,7 +392,7 @@ void projectDataBase::createElementNomenclatureView()
|
|||||||
"di.folio AS folio,"
|
"di.folio AS folio,"
|
||||||
"e.pos AS position "
|
"e.pos AS position "
|
||||||
" FROM element_info ei, diagram_info di, element e, diagram d"
|
" FROM element_info ei, diagram_info di, element e, diagram d"
|
||||||
" WHERE ei.element_uuid = e.uuid AND e.diagram_uuid = d.uuid AND di.diagram_uuid = d.uuid");
|
" WHERE ei.element_uuid = e.uuid AND e.diagram_uuid = d.uuid AND di.diagram_uuid = d.uuid AND (ei.exclude_from_bom IS NOT 'true')");
|
||||||
|
|
||||||
QSqlQuery query(m_data_base);
|
QSqlQuery query(m_data_base);
|
||||||
if (!query.exec(create_view)) {
|
if (!query.exec(create_view)) {
|
||||||
|
|||||||
@@ -373,6 +373,11 @@ QString ElementQueryWidget::queryStr() const
|
|||||||
where.clear();
|
where.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString exclude_condition = "(exclude_from_bom IS NULL OR exclude_from_bom != '1')";
|
||||||
|
|
||||||
|
filter_ += " AND " + exclude_condition;
|
||||||
|
// -------------------------------------------------------------
|
||||||
|
|
||||||
if (where.isEmpty() && !filter_.isEmpty()) {
|
if (where.isEmpty() && !filter_.isEmpty()) {
|
||||||
filter_.remove(0, 4); //Remove the first " AND" of filter.
|
filter_.remove(0, 4); //Remove the first " AND" of filter.
|
||||||
filter_.prepend( " WHERE");
|
filter_.prepend( " WHERE");
|
||||||
@@ -456,7 +461,7 @@ void ElementQueryWidget::setUpItems()
|
|||||||
{
|
{
|
||||||
for(QString key : QETInformation::elementInfoKeys())
|
for(QString key : QETInformation::elementInfoKeys())
|
||||||
{
|
{
|
||||||
if (key == "formula")
|
if (key == "formula" || key == "exclude_from_bom")
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto item = new QListWidgetItem(QETInformation::translatedInfoKey(key), ui->m_var_list);
|
auto item = new QListWidgetItem(QETInformation::translatedInfoKey(key), ui->m_var_list);
|
||||||
|
|||||||
+3
-2
@@ -142,10 +142,11 @@ class Diagram : public QGraphicsScene
|
|||||||
void wheelEvent (QGraphicsSceneWheelEvent *event) override;
|
void wheelEvent (QGraphicsSceneWheelEvent *event) override;
|
||||||
void keyPressEvent (QKeyEvent *event) override;
|
void keyPressEvent (QKeyEvent *event) override;
|
||||||
void keyReleaseEvent (QKeyEvent *) override;
|
void keyReleaseEvent (QKeyEvent *) override;
|
||||||
void correctTextPos(Element* elmt);
|
|
||||||
void restoreText(Element* elmt);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
void correctTextPos(Element* elmt);
|
||||||
|
void restoreText(Element* elmt);
|
||||||
QUuid uuid();
|
QUuid uuid();
|
||||||
void setEventInterface (DiagramEventInterface *event_interface);
|
void setEventInterface (DiagramEventInterface *event_interface);
|
||||||
void clearEventInterface();
|
void clearEventInterface();
|
||||||
|
|||||||
@@ -220,7 +220,7 @@ void DynamicTextFieldEditor::fillInfoComboBox()
|
|||||||
QStringList strl;
|
QStringList strl;
|
||||||
auto type = elementEditor()->elementScene()->elementData().m_type;
|
auto type = elementEditor()->elementScene()->elementData().m_type;
|
||||||
|
|
||||||
if(type & ElementData::AllReport) {
|
if((type & ElementData::AllReport) || (type == ElementData::ConductorDefinition)) {
|
||||||
strl = QETInformation::folioReportInfoKeys();
|
strl = QETInformation::folioReportInfoKeys();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -365,7 +365,8 @@ void DynamicTextFieldEditor::on_m_text_from_cb_activated(int index) {
|
|||||||
void DynamicTextFieldEditor::on_m_composite_text_pb_clicked()
|
void DynamicTextFieldEditor::on_m_composite_text_pb_clicked()
|
||||||
{
|
{
|
||||||
bool isReport = false;
|
bool isReport = false;
|
||||||
if (elementEditor()->elementScene()->elementData().m_type & ElementData::AllReport) {
|
auto type = elementEditor()->elementScene()->elementData().m_type;
|
||||||
|
if ((type & ElementData::AllReport) || (type == ElementData::ConductorDefinition)) {
|
||||||
isReport = true;
|
isReport = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -133,6 +133,7 @@ void ElementPropertiesEditorWidget::setUpInterface()
|
|||||||
ui->m_base_type_cb->addItem (tr("Renvoi de folio précédent"), ElementData::PreviousReport);
|
ui->m_base_type_cb->addItem (tr("Renvoi de folio précédent"), ElementData::PreviousReport);
|
||||||
ui->m_base_type_cb->addItem (tr("Bornier"), ElementData::Terminal);
|
ui->m_base_type_cb->addItem (tr("Bornier"), ElementData::Terminal);
|
||||||
ui->m_base_type_cb->addItem (tr("Vignette"), ElementData::Thumbnail);
|
ui->m_base_type_cb->addItem (tr("Vignette"), ElementData::Thumbnail);
|
||||||
|
ui->m_base_type_cb->addItem (tr("Définition de conducteur"), ElementData::ConductorDefinition);
|
||||||
|
|
||||||
// Slave option
|
// Slave option
|
||||||
ui->m_state_cb->addItem(tr("Normalement ouvert"), ElementData::NO);
|
ui->m_state_cb->addItem(tr("Normalement ouvert"), ElementData::NO);
|
||||||
@@ -188,6 +189,9 @@ void ElementPropertiesEditorWidget::updateTree()
|
|||||||
case ElementData::PreviousReport:
|
case ElementData::PreviousReport:
|
||||||
ui->m_tree->setDisabled(true);
|
ui->m_tree->setDisabled(true);
|
||||||
break;
|
break;
|
||||||
|
case ElementData::ConductorDefinition:
|
||||||
|
ui->m_tree->setDisabled(true);
|
||||||
|
break;
|
||||||
case ElementData::Master:
|
case ElementData::Master:
|
||||||
ui->m_tree->setEnabled(true);
|
ui->m_tree->setEnabled(true);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -736,40 +736,62 @@ bool QETElementEditor::checkElement()
|
|||||||
QList<QETWarning> warnings;
|
QList<QETWarning> warnings;
|
||||||
QList<QETWarning> errors;
|
QList<QETWarning> errors;
|
||||||
|
|
||||||
// Warning #1: Element haven't got terminal
|
// Warning #1: Element haven't got terminal
|
||||||
// (except for report, because report must have one terminal and this checking is do below)
|
// (except for report and conductor definition, because they must have one terminal and this checking is done below)
|
||||||
if (!m_elmt_scene -> containsTerminals() &&
|
if (!m_elmt_scene -> containsTerminals() &&
|
||||||
!(m_elmt_scene->elementData().m_type & ElementData::AllReport)) {
|
!(m_elmt_scene->elementData().m_type & ElementData::AllReport) &&
|
||||||
|
m_elmt_scene->elementData().m_type != ElementData::ConductorDefinition) {
|
||||||
warnings << qMakePair(
|
warnings << qMakePair(
|
||||||
tr("Absence de borne", "warning title"),
|
tr("Absence de borne", "warning title"),
|
||||||
tr(
|
tr(
|
||||||
"<br>En l'absence de borne, l'élément ne pourra être"
|
"<br>En l'absence de borne, l'élément ne pourra être"
|
||||||
" relié à d'autres éléments par l'intermédiaire de conducteurs.",
|
" relié à d'autres éléments par l'intermédiaire de conducteurs.",
|
||||||
"warning description"
|
"warning description"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check folio report element
|
// Check folio report element
|
||||||
if (m_elmt_scene->elementData().m_type & ElementData::AllReport)
|
if (m_elmt_scene->elementData().m_type & ElementData::AllReport)
|
||||||
{
|
{
|
||||||
int terminal =0;
|
int terminal =0;
|
||||||
|
|
||||||
for(auto qgi : m_elmt_scene -> items()) {
|
for(auto qgi : m_elmt_scene -> items()) {
|
||||||
if (qgraphicsitem_cast<PartTerminal *>(qgi)) {
|
if (qgraphicsitem_cast<PartTerminal *>(qgi)) {
|
||||||
terminal ++;
|
terminal ++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Error folio report must have only one terminal
|
||||||
|
if (terminal != 1) {
|
||||||
|
errors << qMakePair (tr("Absence de borne"),
|
||||||
|
tr("<br><b>Erreur</b> :"
|
||||||
|
"<br>Les reports de folio doivent posséder une seul borne."
|
||||||
|
"<br><b>Solution</b> :"
|
||||||
|
"<br>Verifier que l'élément ne possède qu'une seul borne"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Error folio report must have only one terminal
|
// Check conductor definition element
|
||||||
if (terminal != 1) {
|
if (m_elmt_scene->elementData().m_type == ElementData::ConductorDefinition)
|
||||||
errors << qMakePair (tr("Absence de borne"),
|
{
|
||||||
tr("<br><b>Erreur</b> :"
|
int terminal =0;
|
||||||
"<br>Les reports de folio doivent posséder une seul borne."
|
|
||||||
"<br><b>Solution</b> :"
|
for(auto qgi : m_elmt_scene -> items()) {
|
||||||
"<br>Verifier que l'élément ne possède qu'une seul borne"));
|
if (qgraphicsitem_cast<PartTerminal *>(qgi)) {
|
||||||
|
terminal ++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error: Conductor definition must have exactly one terminal
|
||||||
|
if (terminal != 1) {
|
||||||
|
errors << qMakePair (tr("Nombre de bornes incorrect"),
|
||||||
|
tr("<br><b>Erreur</b> :"
|
||||||
|
"<br>Les définitions de conducteur ne peuvent posséder qu'une seule borne."
|
||||||
|
"<br><b>Solution</b> :"
|
||||||
|
"<br>Vérifier que l'élément ne possède qu'une seule borne"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!errors.count() && !warnings.count()) {
|
if (!errors.count() && !warnings.count()) {
|
||||||
return(true);
|
return(true);
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ void TerminalEditor::updateForm()
|
|||||||
ui->m_y_dsb->setValue(m_part->property("y").toReal());
|
ui->m_y_dsb->setValue(m_part->property("y").toReal());
|
||||||
ui->m_orientation_cb->setCurrentIndex(ui->m_orientation_cb->findData(m_part->property("orientation")));
|
ui->m_orientation_cb->setCurrentIndex(ui->m_orientation_cb->findData(m_part->property("orientation")));
|
||||||
ui->m_name_le->setText(m_part->terminalName());
|
ui->m_name_le->setText(m_part->terminalName());
|
||||||
ui->m_type_cb->setCurrentIndex(ui->m_orientation_cb->findData(m_part->terminalType()));
|
ui->m_type_cb->setCurrentIndex(ui->m_type_cb->findData(m_part->terminalType()));
|
||||||
|
|
||||||
activeConnections(true);
|
activeConnections(true);
|
||||||
}
|
}
|
||||||
@@ -122,6 +122,9 @@ void TerminalEditor::init()
|
|||||||
ui->m_type_cb->addItem(tr("Générique"), TerminalData::Generic);
|
ui->m_type_cb->addItem(tr("Générique"), TerminalData::Generic);
|
||||||
ui->m_type_cb->addItem(tr("Bornier intérieur"), TerminalData::Inner);
|
ui->m_type_cb->addItem(tr("Bornier intérieur"), TerminalData::Inner);
|
||||||
ui->m_type_cb->addItem(tr("Bornier extérieur"), TerminalData::Outer);
|
ui->m_type_cb->addItem(tr("Bornier extérieur"), TerminalData::Outer);
|
||||||
|
ui->m_type_cb->addItem(tr("NO (contact SW)"), TerminalData::No);
|
||||||
|
ui->m_type_cb->addItem(tr("NC (contact SW)"), TerminalData::Nc);
|
||||||
|
ui->m_type_cb->addItem(tr("Commun (contact SW)"), TerminalData::Common);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -335,6 +335,7 @@ void TextEditor::setUpWidget(QWidget *parent)
|
|||||||
|
|
||||||
m_size_sb = new QSpinBox(parent);
|
m_size_sb = new QSpinBox(parent);
|
||||||
m_size_sb->setObjectName(QString::fromUtf8("m_size_sb"));
|
m_size_sb->setObjectName(QString::fromUtf8("m_size_sb"));
|
||||||
|
m_size_sb->setMinimum(4);
|
||||||
|
|
||||||
gridLayout->addWidget(m_size_sb, 2, 1, 1, 1);
|
gridLayout->addWidget(m_size_sb, 2, 1, 1, 1);
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include "elementspanelwidget.h"
|
#include "elementspanelwidget.h"
|
||||||
|
|
||||||
#include "diagram.h"
|
#include "diagram.h"
|
||||||
#include "editor/ui/qetelementeditor.h"
|
#include "editor/ui/qetelementeditor.h"
|
||||||
#include "elementscategoryeditor.h"
|
#include "elementscategoryeditor.h"
|
||||||
@@ -26,6 +25,7 @@
|
|||||||
#include "titleblock/templatedeleter.h"
|
#include "titleblock/templatedeleter.h"
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
#include "qetgraphicsitem/element.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
When the ENABLE_PANEL_WIDGET_DND_CHECKS flag is set, the panel
|
When the ENABLE_PANEL_WIDGET_DND_CHECKS flag is set, the panel
|
||||||
@@ -59,7 +59,8 @@ ElementsPanelWidget::ElementsPanelWidget(QWidget *parent) : QWidget(parent) {
|
|||||||
prj_close = new QAction(QET::Icons::DocumentClose, tr("Fermer ce projet"), this);
|
prj_close = new QAction(QET::Icons::DocumentClose, tr("Fermer ce projet"), this);
|
||||||
prj_edit_prop = new QAction(QET::Icons::DialogInformation, tr("Propriétés du projet"), this);
|
prj_edit_prop = new QAction(QET::Icons::DialogInformation, tr("Propriétés du projet"), this);
|
||||||
prj_prop_diagram = new QAction(QET::Icons::DialogInformation, tr("Propriétés du folio"), this);
|
prj_prop_diagram = new QAction(QET::Icons::DialogInformation, tr("Propriétés du folio"), this);
|
||||||
prj_add_diagram = new QAction(QET::Icons::DiagramAdd, tr("Ajouter un folio"), this);
|
prj_add_diagram = new QAction(QET::Icons::DiagramAdd, tr("Ajouter un folio"), this);
|
||||||
|
prj_duplicate_diagram = new QAction(QET::Icons::IC_CopyFile, tr("Copier et coller"), this);
|
||||||
prj_del_diagram = new QAction(QET::Icons::DiagramDelete, tr("Supprimer ce folio"), this);
|
prj_del_diagram = new QAction(QET::Icons::DiagramDelete, tr("Supprimer ce folio"), this);
|
||||||
prj_move_diagram_up = new QAction(QET::Icons::GoUp, tr("Remonter ce folio"), this);
|
prj_move_diagram_up = new QAction(QET::Icons::GoUp, tr("Remonter ce folio"), this);
|
||||||
prj_move_diagram_down = new QAction(QET::Icons::GoDown, tr("Abaisser ce folio"), this);
|
prj_move_diagram_down = new QAction(QET::Icons::GoDown, tr("Abaisser ce folio"), this);
|
||||||
@@ -100,6 +101,7 @@ ElementsPanelWidget::ElementsPanelWidget(QWidget *parent) : QWidget(parent) {
|
|||||||
connect(prj_prop_diagram, SIGNAL(triggered()), this, SLOT(editDiagramProperties()));
|
connect(prj_prop_diagram, SIGNAL(triggered()), this, SLOT(editDiagramProperties()));
|
||||||
connect(prj_add_diagram, SIGNAL(triggered()), this, SLOT(newDiagram()));
|
connect(prj_add_diagram, SIGNAL(triggered()), this, SLOT(newDiagram()));
|
||||||
connect(prj_del_diagram, SIGNAL(triggered()), this, SLOT(deleteDiagram()));
|
connect(prj_del_diagram, SIGNAL(triggered()), this, SLOT(deleteDiagram()));
|
||||||
|
connect(prj_duplicate_diagram, SIGNAL(triggered()), this, SLOT(duplicateDiagram()));
|
||||||
connect(prj_move_diagram_up, SIGNAL(triggered()), this, SLOT(moveDiagramUp()));
|
connect(prj_move_diagram_up, SIGNAL(triggered()), this, SLOT(moveDiagramUp()));
|
||||||
connect(prj_move_diagram_down, SIGNAL(triggered()), this, SLOT(moveDiagramDown()));
|
connect(prj_move_diagram_down, SIGNAL(triggered()), this, SLOT(moveDiagramDown()));
|
||||||
connect(prj_move_diagram_top, SIGNAL(triggered()), this, SLOT(moveDiagramUpTop()));
|
connect(prj_move_diagram_top, SIGNAL(triggered()), this, SLOT(moveDiagramUpTop()));
|
||||||
@@ -447,7 +449,8 @@ void ElementsPanelWidget::updateButtons()
|
|||||||
}
|
}
|
||||||
|
|
||||||
prj_del_diagram -> setEnabled(is_writable);
|
prj_del_diagram -> setEnabled(is_writable);
|
||||||
prj_move_diagram_up -> setEnabled(is_writable && min_position > 0);
|
prj_duplicate_diagram -> setEnabled(is_writable);
|
||||||
|
prj_move_diagram_up -> setEnabled(is_writable && min_position > 0);
|
||||||
prj_move_diagram_down -> setEnabled(is_writable && max_position < project_diagrams_count - 1);
|
prj_move_diagram_down -> setEnabled(is_writable && max_position < project_diagrams_count - 1);
|
||||||
prj_move_diagram_top -> setEnabled(is_writable && min_position > 0);
|
prj_move_diagram_top -> setEnabled(is_writable && min_position > 0);
|
||||||
|
|
||||||
@@ -501,6 +504,7 @@ void ElementsPanelWidget::handleContextMenu(const QPoint &pos) {
|
|||||||
case QET::Diagram:
|
case QET::Diagram:
|
||||||
context_menu -> addAction(prj_prop_diagram);
|
context_menu -> addAction(prj_prop_diagram);
|
||||||
context_menu -> addAction(prj_del_diagram);
|
context_menu -> addAction(prj_del_diagram);
|
||||||
|
context_menu -> addAction(prj_duplicate_diagram);
|
||||||
context_menu -> addAction(prj_move_diagram_top);
|
context_menu -> addAction(prj_move_diagram_top);
|
||||||
context_menu -> addAction(prj_move_diagram_upx10);
|
context_menu -> addAction(prj_move_diagram_upx10);
|
||||||
context_menu -> addAction(prj_move_diagram_upx100);
|
context_menu -> addAction(prj_move_diagram_upx100);
|
||||||
@@ -593,3 +597,56 @@ void ElementsPanelWidget::keyPressEvent(QKeyEvent *e) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Duplicates the selected folios (pages) along with their content
|
||||||
|
* and properties, and cleanly resolves cross-references.
|
||||||
|
*/
|
||||||
|
void ElementsPanelWidget::duplicateDiagram()
|
||||||
|
{
|
||||||
|
QList<Diagram *> diagrams_to_duplicate = elements_panel->selectedDiagrams();
|
||||||
|
if (diagrams_to_duplicate.isEmpty()) return;
|
||||||
|
|
||||||
|
QETProject *project = diagrams_to_duplicate.first()->project();
|
||||||
|
if (!project || project->isReadOnly()) return;
|
||||||
|
|
||||||
|
for (Diagram *source_diagram : diagrams_to_duplicate) {
|
||||||
|
|
||||||
|
Diagram *new_diagram = project->addNewDiagram();
|
||||||
|
if (!new_diagram) continue;
|
||||||
|
|
||||||
|
QString template_name = source_diagram->border_and_titleblock.titleBlockTemplateName();
|
||||||
|
new_diagram->setTitleBlockTemplate(template_name);
|
||||||
|
|
||||||
|
TitleBlockProperties tbp = source_diagram->border_and_titleblock.exportTitleBlock();
|
||||||
|
new_diagram->border_and_titleblock.importTitleBlock(tbp);
|
||||||
|
|
||||||
|
BorderProperties bp = source_diagram->border_and_titleblock.exportBorder();
|
||||||
|
new_diagram->border_and_titleblock.importBorder(bp);
|
||||||
|
|
||||||
|
for (QGraphicsItem *item : source_diagram->items()) {
|
||||||
|
if (Element *elmt = dynamic_cast<Element *>(item)) {
|
||||||
|
source_diagram->correctTextPos(elmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QDomDocument doc = source_diagram->toXml();
|
||||||
|
QDomElement diagram_elmt = doc.documentElement();
|
||||||
|
|
||||||
|
for (QGraphicsItem *item : source_diagram->items()) {
|
||||||
|
if (Element *elmt = dynamic_cast<Element *>(item)) {
|
||||||
|
source_diagram->restoreText(elmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new_diagram->fromXml(diagram_elmt, QPointF(0, 0), false, nullptr);
|
||||||
|
|
||||||
|
for (QGraphicsItem *item : new_diagram->items()) {
|
||||||
|
if (Element *elmt = dynamic_cast<Element *>(item)) {
|
||||||
|
new_diagram->restoreText(elmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
elements_panel->reload();
|
||||||
|
}
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ class ElementsPanelWidget : public QWidget {
|
|||||||
*prj_prop_diagram,
|
*prj_prop_diagram,
|
||||||
*prj_add_diagram,
|
*prj_add_diagram,
|
||||||
*prj_del_diagram,
|
*prj_del_diagram,
|
||||||
|
*prj_duplicate_diagram,
|
||||||
*prj_move_diagram_up,
|
*prj_move_diagram_up,
|
||||||
*prj_move_diagram_top,
|
*prj_move_diagram_top,
|
||||||
*prj_move_diagram_down,
|
*prj_move_diagram_down,
|
||||||
@@ -88,6 +89,7 @@ class ElementsPanelWidget : public QWidget {
|
|||||||
void editDiagramProperties();
|
void editDiagramProperties();
|
||||||
void newDiagram();
|
void newDiagram();
|
||||||
void deleteDiagram();
|
void deleteDiagram();
|
||||||
|
void duplicateDiagram();
|
||||||
void moveDiagramUp();
|
void moveDiagramUp();
|
||||||
void moveDiagramDown();
|
void moveDiagramDown();
|
||||||
void moveDiagramUpTop();
|
void moveDiagramUpTop();
|
||||||
|
|||||||
@@ -21,9 +21,16 @@
|
|||||||
#include "../qeticons.h"
|
#include "../qeticons.h"
|
||||||
#include "../qetproject.h"
|
#include "../qetproject.h"
|
||||||
#include "../qetversion.h"
|
#include "../qetversion.h"
|
||||||
|
#include "../qetgraphicsitem/crossrefitem.h"
|
||||||
|
#include "../qetgraphicsitem/dynamicelementtextitem.h"
|
||||||
|
#include "../qetgraphicsitem/elementtextitemgroup.h"
|
||||||
|
|
||||||
#include "ui_projectprintwindow.h"
|
#include "ui_projectprintwindow.h"
|
||||||
|
|
||||||
|
// Private Qt PDF engine for drawHyperlink() — not public API, stable since Qt4
|
||||||
|
// Requires QT += gui-private in qelectrotech.pro
|
||||||
|
#include <private/qpdf_p.h>
|
||||||
|
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove
|
||||||
# include <QDesktopWidget>
|
# include <QDesktopWidget>
|
||||||
#else
|
#else
|
||||||
@@ -37,6 +44,11 @@
|
|||||||
#include <QPrintDialog>
|
#include <QPrintDialog>
|
||||||
#include <QPrintPreviewWidget>
|
#include <QPrintPreviewWidget>
|
||||||
#include <QScreen>
|
#include <QScreen>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QMap>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief ProjectPrintWindow::ProjectPrintWindow
|
* @brief ProjectPrintWindow::ProjectPrintWindow
|
||||||
@@ -188,6 +200,241 @@ ProjectPrintWindow::~ProjectPrintWindow()
|
|||||||
* @brief ProjectPrintWindow::requestPaint
|
* @brief ProjectPrintWindow::requestPaint
|
||||||
* @param slot called when m_preview emit paintRequested
|
* @param slot called when m_preview emit paintRequested
|
||||||
*/
|
*/
|
||||||
|
/**
|
||||||
|
* @brief ProjectPrintWindow::pdfConvertUriToGoTo
|
||||||
|
* Post-processes a Qt-generated PDF to replace URI link annotations
|
||||||
|
* (file:///path/to/file.pdf#page=N) with native PDF GoTo actions
|
||||||
|
* ([pageObj 0 R /Fit]). This makes cross-reference links work in all
|
||||||
|
* PDF viewers regardless of where the file is stored.
|
||||||
|
*
|
||||||
|
* The function:
|
||||||
|
* 1. Reads the PDF as raw bytes.
|
||||||
|
* 2. Collects page object numbers in document order by scanning for
|
||||||
|
* objects that contain "/Type /Page" (but not "/Type /Pages").
|
||||||
|
* 3. Replaces every annotation action block
|
||||||
|
* /S /URI\n/URI (file://...#page=N)
|
||||||
|
* with
|
||||||
|
* /S /GoTo\n/D [<pageObj> 0 R /Fit]
|
||||||
|
* 4. Rebuilds the cross-reference table (offsets change because the
|
||||||
|
* replacement strings have different lengths).
|
||||||
|
* 5. Writes the result back to the same file.
|
||||||
|
*
|
||||||
|
* The function is intentionally conservative: if any step fails (file
|
||||||
|
* not found, malformed PDF, no URI annotations) it returns silently
|
||||||
|
* without corrupting the file.
|
||||||
|
*/
|
||||||
|
static void pdfConvertUriToGoTo(const QString &pdfPath)
|
||||||
|
{
|
||||||
|
// --- 1. Read raw bytes ---
|
||||||
|
QFile f(pdfPath);
|
||||||
|
if (!f.open(QIODevice::ReadOnly)) return;
|
||||||
|
QByteArray data = f.readAll();
|
||||||
|
f.close();
|
||||||
|
|
||||||
|
// --- 2. Collect page object numbers in document order ---
|
||||||
|
// Read them from the page tree (/Type /Pages -> /Kids [ N 0 R ... ]).
|
||||||
|
// This is reliable; scanning raw bytes for "/Type /Page" is NOT: that
|
||||||
|
// marker also occurs inside content streams, and a forward lookahead
|
||||||
|
// wrongly tags neighbouring objects (it found 280 "pages" for a 137-page
|
||||||
|
// document). Qt writes a single, flat /Kids array listing every page.
|
||||||
|
QVector<int> pageObjs;
|
||||||
|
{
|
||||||
|
int pagesPos = data.indexOf("/Type /Pages");
|
||||||
|
int kidsPos = (pagesPos == -1) ? -1 : data.indexOf("/Kids", pagesPos);
|
||||||
|
int lb = (kidsPos == -1) ? -1 : data.indexOf('[', kidsPos);
|
||||||
|
int rb = (lb == -1) ? -1 : data.indexOf(']', lb);
|
||||||
|
if (lb != -1 && rb != -1 && rb > lb) {
|
||||||
|
const QString kids =
|
||||||
|
QString::fromLatin1(data.mid(lb + 1, rb - lb - 1));
|
||||||
|
QRegularExpression re(QStringLiteral("(\\d+)\\s+\\d+\\s+R"));
|
||||||
|
auto it = re.globalMatch(kids);
|
||||||
|
while (it.hasNext()) {
|
||||||
|
int objNum = it.next().captured(1).toInt();
|
||||||
|
if (objNum > 0) pageObjs.append(objNum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pageObjs.isEmpty()) return; // nothing to do
|
||||||
|
|
||||||
|
// --- 3. Replace URI annotations with GoTo ---
|
||||||
|
// Pattern (Qt always writes exactly this):
|
||||||
|
// /S /URI\n/URI (file:///...<anything>#page=N)\n
|
||||||
|
// or (older patches without file://):
|
||||||
|
// /S /URI\n/URI (page=N)\n
|
||||||
|
bool changed = false;
|
||||||
|
{
|
||||||
|
// We do a manual scan to handle variable-length replacements.
|
||||||
|
QByteArray out;
|
||||||
|
out.reserve(data.size());
|
||||||
|
|
||||||
|
const QByteArray sUri = "/S /URI\n/URI (";
|
||||||
|
const QByteArray sGoTo = "/S /GoTo\n/D [";
|
||||||
|
int pos = 0;
|
||||||
|
|
||||||
|
while (pos < data.size()) {
|
||||||
|
int found = data.indexOf(sUri, pos);
|
||||||
|
if (found == -1) {
|
||||||
|
out.append(data.mid(pos));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy everything up to the match
|
||||||
|
out.append(data.mid(pos, found - pos));
|
||||||
|
|
||||||
|
// Find closing ')' of the URI value
|
||||||
|
int uriStart = found + sUri.size();
|
||||||
|
int closeParen = data.indexOf(')', uriStart);
|
||||||
|
if (closeParen == -1) {
|
||||||
|
// Malformed — copy rest verbatim
|
||||||
|
out.append(data.mid(found));
|
||||||
|
pos = data.size();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray uriVal = data.mid(uriStart, closeParen - uriStart);
|
||||||
|
|
||||||
|
// Extract page number: look for #page=N or bare page=N
|
||||||
|
int pageNum = -1;
|
||||||
|
int hashPos = uriVal.lastIndexOf("#page=");
|
||||||
|
int digitStart = -1;
|
||||||
|
if (hashPos != -1) {
|
||||||
|
digitStart = hashPos + 6;
|
||||||
|
} else if (uriVal.startsWith("page=")) {
|
||||||
|
digitStart = 5;
|
||||||
|
}
|
||||||
|
if (digitStart != -1) {
|
||||||
|
// Take only the leading digits: the fragment may carry extra
|
||||||
|
// parameters after the page number (e.g. "22&fitr=15_489_..."),
|
||||||
|
// and QByteArray::toInt() would fail on the whole remainder.
|
||||||
|
int e = digitStart;
|
||||||
|
while (e < uriVal.size()
|
||||||
|
&& uriVal[e] >= '0' && uriVal[e] <= '9')
|
||||||
|
++e;
|
||||||
|
if (e > digitStart)
|
||||||
|
pageNum = uriVal.mid(digitStart, e - digitStart).toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pageNum >= 1 && pageNum <= pageObjs.size()) {
|
||||||
|
// Valid page reference — emit GoTo action.
|
||||||
|
int pageObjNum = pageObjs[pageNum - 1];
|
||||||
|
|
||||||
|
// Optional precise destination: &fitr=Left_Bottom_Right_Top
|
||||||
|
// (integer PDF points). If present -> /FitR (frame the element);
|
||||||
|
// otherwise -> /Fit (whole page, top).
|
||||||
|
QByteArray dest = " /Fit]";
|
||||||
|
int fr = uriVal.indexOf("fitr=");
|
||||||
|
if (fr != -1) {
|
||||||
|
QByteArray rest = uriVal.mid(fr + 5);
|
||||||
|
// stop at first char that is not part of the number list
|
||||||
|
int end = 0;
|
||||||
|
while (end < rest.size()
|
||||||
|
&& ((rest[end] >= '0' && rest[end] <= '9')
|
||||||
|
|| rest[end] == '_' || rest[end] == '-'))
|
||||||
|
++end;
|
||||||
|
QList<QByteArray> parts = rest.left(end).split('_');
|
||||||
|
if (parts.size() == 4) {
|
||||||
|
dest = " /FitR " + parts[0] + " " + parts[1] + " "
|
||||||
|
+ parts[2] + " " + parts[3] + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray goTo = sGoTo
|
||||||
|
+ QByteArray::number(pageObjNum)
|
||||||
|
+ " 0 R" + dest;
|
||||||
|
out.append(goTo);
|
||||||
|
changed = true;
|
||||||
|
} else {
|
||||||
|
// Unknown page — keep original URI
|
||||||
|
out.append(sUri);
|
||||||
|
out.append(uriVal);
|
||||||
|
out.append(')');
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = closeParen + 1; // skip past ')'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!changed) return; // nothing was replaced
|
||||||
|
data = out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 4. Rebuild xref table ---
|
||||||
|
// Find start of existing xref (last occurrence)
|
||||||
|
int xrefStart = data.lastIndexOf("\nxref\n");
|
||||||
|
if (xrefStart == -1) xrefStart = data.lastIndexOf("\nxref ");
|
||||||
|
if (xrefStart == -1) return; // malformed PDF
|
||||||
|
++xrefStart; // skip the leading '\n'
|
||||||
|
|
||||||
|
QByteArray body = data.left(xrefStart);
|
||||||
|
|
||||||
|
// Collect all object offsets from the body
|
||||||
|
QMap<int, int> offsets; // objNum -> byte offset
|
||||||
|
{
|
||||||
|
const QByteArray objMarker = " 0 obj";
|
||||||
|
int pos = 0;
|
||||||
|
while ((pos = body.indexOf(objMarker, pos)) != -1) {
|
||||||
|
int numStart = pos - 1;
|
||||||
|
while (numStart > 0 && body[numStart-1] != '\n' && body[numStart-1] != '\r')
|
||||||
|
--numStart;
|
||||||
|
QByteArray numStr = body.mid(numStart, pos - numStart).trimmed();
|
||||||
|
bool ok = false;
|
||||||
|
int objNum = numStr.toInt(&ok);
|
||||||
|
if (ok && objNum > 0)
|
||||||
|
offsets[objNum] = numStart;
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offsets.isEmpty()) return;
|
||||||
|
|
||||||
|
int maxObj = offsets.lastKey();
|
||||||
|
|
||||||
|
// Build xref table
|
||||||
|
QByteArray xref;
|
||||||
|
xref += "xref\n";
|
||||||
|
xref += "0 " + QByteArray::number(maxObj + 1) + "\n";
|
||||||
|
xref += "0000000000 65535 f \n";
|
||||||
|
for (int i = 1; i <= maxObj; ++i) {
|
||||||
|
if (offsets.contains(i)) {
|
||||||
|
xref += QByteArray::number(offsets[i]).rightJustified(10, '0')
|
||||||
|
+ " 00000 n \n";
|
||||||
|
} else {
|
||||||
|
xref += "0000000000 65535 f \n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find trailer dict from the original xref section
|
||||||
|
int trailerPos = data.indexOf("trailer", xrefStart);
|
||||||
|
int trailerEnd = -1;
|
||||||
|
if (trailerPos != -1) {
|
||||||
|
trailerEnd = data.indexOf("%%EOF", trailerPos);
|
||||||
|
if (trailerEnd != -1) trailerEnd += 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray trailer;
|
||||||
|
if (trailerPos != -1 && trailerEnd != -1)
|
||||||
|
trailer = data.mid(trailerPos, trailerEnd - trailerPos);
|
||||||
|
else
|
||||||
|
trailer = "trailer\n<<>>\n%%EOF";
|
||||||
|
|
||||||
|
int newXrefOffset = body.size();
|
||||||
|
|
||||||
|
QByteArray result;
|
||||||
|
result.reserve(body.size() + xref.size() + trailer.size() + 30);
|
||||||
|
result += body;
|
||||||
|
result += xref;
|
||||||
|
result += trailer;
|
||||||
|
result += "\nstartxref\n";
|
||||||
|
result += QByteArray::number(newXrefOffset);
|
||||||
|
result += "\n%%EOF\n";
|
||||||
|
|
||||||
|
// --- 5. Write back ---
|
||||||
|
QFile out(pdfPath);
|
||||||
|
if (!out.open(QIODevice::WriteOnly | QIODevice::Truncate)) return;
|
||||||
|
out.write(result);
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
|
||||||
void ProjectPrintWindow::requestPaint()
|
void ProjectPrintWindow::requestPaint()
|
||||||
{
|
{
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
|
||||||
@@ -214,13 +461,47 @@ void ProjectPrintWindow::requestPaint()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build diagram -> first physical PDF page number map (1-based)
|
||||||
|
// Must be done before the print loop since page numbers depend on order
|
||||||
|
QMap<Diagram*, int> diagramPageMap;
|
||||||
|
{
|
||||||
|
int pageNum = 1;
|
||||||
|
for (auto diagram : selectedDiagram()) {
|
||||||
|
diagramPageMap.insert(diagram, pageNum);
|
||||||
|
// Each diagram may span multiple pages if not fit_page
|
||||||
|
if (!ui->m_fit_in_page_cb->isChecked()) {
|
||||||
|
auto option = exportProperties();
|
||||||
|
bool full_page = m_printer->fullPage();
|
||||||
|
int h = horizontalPagesCount(diagram, option, full_page);
|
||||||
|
int v = verticalPagesCount(diagram, option, full_page);
|
||||||
|
pageNum += h * v;
|
||||||
|
} else {
|
||||||
|
pageNum += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool first = true;
|
bool first = true;
|
||||||
QPainter painter(m_printer);
|
QPainter painter(m_printer);
|
||||||
|
|
||||||
|
// A real PDF export uses the QPdfEngine; the on-screen preview uses a
|
||||||
|
// preview paint engine. We only post-process when actually writing a PDF.
|
||||||
|
const bool pdfExport =
|
||||||
|
(m_printer->outputFormat() == QPrinter::PdfFormat)
|
||||||
|
&& (dynamic_cast<QPdfEngine*>(painter.paintEngine()) != nullptr);
|
||||||
|
|
||||||
for (auto diagram : selectedDiagram())
|
for (auto diagram : selectedDiagram())
|
||||||
{
|
{
|
||||||
first ? first = false : m_printer->newPage();
|
first ? first = false : m_printer->newPage();
|
||||||
printDiagram(diagram, ui->m_fit_in_page_cb->isChecked(), &painter, m_printer);
|
printDiagram(diagram, ui->m_fit_in_page_cb->isChecked(), &painter, m_printer, diagramPageMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note: do NOT call painter.end() or pdfConvertUriToGoTo() here.
|
||||||
|
// We are inside the paintRequested slot: the QPrintPreviewWidget still
|
||||||
|
// owns the paint cycle. On macOS arm64 (Metal/CALayer compositor),
|
||||||
|
// closing the QPainter manually inside this slot leaves the backing
|
||||||
|
// store in an undefined state, producing a black screen after export.
|
||||||
|
// pdfConvertUriToGoTo() is deferred to print() via QTimer::singleShot(0).
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -230,7 +511,7 @@ void ProjectPrintWindow::requestPaint()
|
|||||||
* @param fit_page
|
* @param fit_page
|
||||||
* @param printer
|
* @param printer
|
||||||
*/
|
*/
|
||||||
void ProjectPrintWindow::printDiagram(Diagram *diagram, bool fit_page, QPainter *painter, QPrinter *printer)
|
void ProjectPrintWindow::printDiagram(Diagram *diagram, bool fit_page, QPainter *painter, QPrinter *printer, const QMap<Diagram*, int> &diagramPageMap)
|
||||||
{
|
{
|
||||||
|
|
||||||
////Prepare the print////
|
////Prepare the print////
|
||||||
@@ -317,6 +598,171 @@ void ProjectPrintWindow::printDiagram(Diagram *diagram, bool fit_page, QPainter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////Inject PDF cross-reference links////
|
||||||
|
if (printer->outputFormat() == QPrinter::PdfFormat && fit_page) {
|
||||||
|
auto *pdfEngine = dynamic_cast<QPdfEngine*>(painter->paintEngine());
|
||||||
|
if (pdfEngine) {
|
||||||
|
|
||||||
|
// QGraphicsScene::render() fait save()/restore() : worldTransform()
|
||||||
|
// est revenu a l'identite ici. On reconstruit DONC explicitement la
|
||||||
|
// transform appliquee par :
|
||||||
|
// diagram->render(painter, QRectF(), diagram_rect, KeepAspectRatio)
|
||||||
|
// cible vide => painter->viewport() ; source = diagram_rect ; centre.
|
||||||
|
const QRectF target = QRectF(painter->viewport());
|
||||||
|
const QRectF source = QRectF(diagram_rect); // meme source que render()
|
||||||
|
|
||||||
|
// render() ANCRE en haut-gauche (pas de centrage) :
|
||||||
|
// translate(target.topLeft) . scale(s,s) . translate(-source.topLeft)
|
||||||
|
// On reproduit EXACTEMENT ca — surtout PAS de (target-source*s)/2.
|
||||||
|
const qreal s = qMin(target.width() / source.width(),
|
||||||
|
target.height() / source.height());
|
||||||
|
|
||||||
|
QTransform fit;
|
||||||
|
fit.translate(target.x(), target.y());
|
||||||
|
fit.scale(s, s);
|
||||||
|
fit.translate(-source.x(), -source.y()); // scene -> pixels device
|
||||||
|
|
||||||
|
// IMPORTANT : QPdfEngine::drawHyperlink() applique lui-meme
|
||||||
|
// pageMatrix() (echelle 72/resolution + inversion de Y + marges).
|
||||||
|
// On lui passe donc le rectangle en PIXELS DEVICE, sans aucune
|
||||||
|
// conversion en points ni flip de notre cote.
|
||||||
|
const QRectF pageBounds(0, 0, target.width(), target.height());
|
||||||
|
|
||||||
|
// ---- Device-pixels -> PDF points, replicating QPdfEnginePrivate::pageMatrix()
|
||||||
|
// (same geometry for every page: same printer, page size and margins). ----
|
||||||
|
const qreal pt_scale = 72.0 / printer->resolution();
|
||||||
|
const qreal fullH_pt = printer->pageLayout().fullRectPoints().height();
|
||||||
|
const bool fullPageMode =
|
||||||
|
(printer->pageLayout().mode() == QPageLayout::FullPageMode);
|
||||||
|
const QRect paintPx =
|
||||||
|
printer->pageLayout().paintRectPixels(printer->resolution());
|
||||||
|
auto devToPdf = [=](const QPointF &d) -> QPointF {
|
||||||
|
qreal dx = d.x(), dy = d.y();
|
||||||
|
if (!fullPageMode) { dx += paintPx.left(); dy += paintPx.top(); }
|
||||||
|
return QPointF(pt_scale * dx, fullH_pt - pt_scale * dy);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Compute, in PDF points on its OWN page, the rectangle to frame for a
|
||||||
|
// 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 = QRectF(diagramRect(dg, exportProperties()));
|
||||||
|
if (srcT.width() <= 0.0 || srcT.height() <= 0.0) return QRectF();
|
||||||
|
const qreal sT = qMin(target.width() / srcT.width(),
|
||||||
|
target.height() / srcT.height());
|
||||||
|
QTransform fitT;
|
||||||
|
fitT.translate(target.x(), target.y());
|
||||||
|
fitT.scale(sT, sT);
|
||||||
|
fitT.translate(-srcT.x(), -srcT.y());
|
||||||
|
|
||||||
|
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.
|
||||||
|
const qreal pad = 25.0;
|
||||||
|
elemScene.adjust(-pad, -pad, pad, pad);
|
||||||
|
const qreal minSide = 160.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(elemScene);
|
||||||
|
const QPointF a = devToPdf(devT.topLeft());
|
||||||
|
const QPointF b = devToPdf(devT.bottomRight());
|
||||||
|
return QRectF(QPointF(qMin(a.x(), b.x()), qMin(a.y(), b.y())),
|
||||||
|
QPointF(qMax(a.x(), b.x()), qMax(a.y(), b.y())));
|
||||||
|
};
|
||||||
|
|
||||||
|
auto injectLink = [&](const QRectF &sceneRect, Element *targetElmt) {
|
||||||
|
if (!targetElmt || !targetElmt->diagram()) return;
|
||||||
|
const int targetPage =
|
||||||
|
diagramPageMap.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 = 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()))
|
||||||
|
.arg(qRound(d.right())).arg(qRound(d.bottom()));
|
||||||
|
|
||||||
|
QUrl url = QUrl::fromLocalFile(printer->outputFileName());
|
||||||
|
url.setFragment(frag);
|
||||||
|
pdfEngine->drawHyperlink(devRect, url);
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto *item : diagram->items()) {
|
||||||
|
|
||||||
|
// --- CrossRefItem links ---
|
||||||
|
if (auto *xref = dynamic_cast<CrossRefItem*>(item)) {
|
||||||
|
for (auto it = xref->hoveredContactsMap().begin();
|
||||||
|
it != xref->hoveredContactsMap().end(); ++it)
|
||||||
|
{
|
||||||
|
Element *targetElmt = it.key();
|
||||||
|
if (!targetElmt || !targetElmt->diagram()) continue;
|
||||||
|
// it.value() est en coords LOCALES du CrossRefItem -> scene
|
||||||
|
injectLink(xref->mapRectToScene(it.value()), targetElmt);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Folio report links (DynamicElementTextItem) ---
|
||||||
|
if (auto *deti = dynamic_cast<DynamicElementTextItem*>(item)) {
|
||||||
|
Element *parent = deti->parentElement();
|
||||||
|
if (!parent) continue;
|
||||||
|
|
||||||
|
// (a) Report element : label -> linked report on another folio
|
||||||
|
if (parent->linkType() & Element::AllReport) {
|
||||||
|
if (parent->linkedElements().isEmpty()) continue;
|
||||||
|
|
||||||
|
bool showsLabel =
|
||||||
|
(deti->textFrom() == DynamicElementTextItem::ElementInfo
|
||||||
|
&& deti->infoName() == QLatin1String("label")) ||
|
||||||
|
(deti->textFrom() == DynamicElementTextItem::CompositeText
|
||||||
|
&& deti->compositeText().contains(QStringLiteral("%{label}")));
|
||||||
|
if (!showsLabel) continue;
|
||||||
|
|
||||||
|
Element *targetElmt = parent->linkedElements().first();
|
||||||
|
if (!targetElmt || !targetElmt->diagram()) continue;
|
||||||
|
|
||||||
|
injectLink(deti->mapRectToScene(deti->boundingRect()), targetElmt);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// (b) Slave element : the "(folio-pos)" text -> master element
|
||||||
|
if (parent->linkType() == Element::Slave) {
|
||||||
|
QGraphicsTextItem *sx = deti->slaveXrefItem();
|
||||||
|
Element *master = deti->masterElement();
|
||||||
|
if (sx && master && master->diagram()) {
|
||||||
|
injectLink(sx->mapRectToScene(sx->boundingRect()), master);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Slave cross-reference carried by a grouped text ---
|
||||||
|
if (auto *grp = dynamic_cast<ElementTextItemGroup*>(item)) {
|
||||||
|
Element *parent = grp->parentElement();
|
||||||
|
if (!parent || parent->linkType() != Element::Slave) continue;
|
||||||
|
if (parent->linkedElements().isEmpty()) continue;
|
||||||
|
QGraphicsTextItem *sx = grp->slaveXrefItem();
|
||||||
|
if (!sx) continue;
|
||||||
|
Element *master = parent->linkedElements().first();
|
||||||
|
if (!master || !master->diagram()) continue;
|
||||||
|
injectLink(sx->mapRectToScene(sx->boundingRect()), master);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
////PDF links end////
|
||||||
|
|
||||||
////Print is finished, restore diagram and graphics item properties
|
////Print is finished, restore diagram and graphics item properties
|
||||||
for (auto view : diagram->views()) {
|
for (auto view : diagram->views()) {
|
||||||
view->setInteractive(true);
|
view->setInteractive(true);
|
||||||
@@ -772,9 +1218,29 @@ void ProjectPrintWindow::on_m_uncheck_all_clicked()
|
|||||||
|
|
||||||
void ProjectPrintWindow::print()
|
void ProjectPrintWindow::print()
|
||||||
{
|
{
|
||||||
m_preview->print();
|
const bool isPdf = (m_printer->outputFormat() == QPrinter::PdfFormat);
|
||||||
|
const QString pdfFile = isPdf ? m_printer->outputFileName() : QString();
|
||||||
|
|
||||||
|
m_preview->print(); // triggers requestPaint() synchronously; painter
|
||||||
|
// is created/destroyed inside that call
|
||||||
|
|
||||||
savePageSetupForCurrentPrinter();
|
savePageSetupForCurrentPrinter();
|
||||||
this->close();
|
|
||||||
|
if (isPdf && !pdfFile.isEmpty()) {
|
||||||
|
// Defer post-processing and window close to the next event-loop
|
||||||
|
// iteration. This lets the macOS arm64 Metal compositor finish
|
||||||
|
// compositing the backing store before the window is destroyed,
|
||||||
|
// which prevents the black screen observed on Apple Silicon under
|
||||||
|
// macOS Sequoia (QPrintPreviewWidget + CALayer timing issue).
|
||||||
|
QTimer::singleShot(0, this, [this, pdfFile]() {
|
||||||
|
// Convert URI link annotations into native internal GoTo/FitR
|
||||||
|
// actions so cross-references jump inside the document.
|
||||||
|
pdfConvertUriToGoTo(pdfFile);
|
||||||
|
this->close();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this->close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectPrintWindow::on_m_date_cb_userDateChanged(const QDate &date)
|
void ProjectPrintWindow::on_m_date_cb_userDateChanged(const QDate &date)
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#include "../exportproperties.h"
|
#include "../exportproperties.h"
|
||||||
|
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
|
#include <QMap>
|
||||||
#include <QPrinter>
|
#include <QPrinter>
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
@@ -79,7 +80,7 @@ class ProjectPrintWindow : public QMainWindow
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void requestPaint();
|
void requestPaint();
|
||||||
void printDiagram(Diagram *diagram, bool fit_page, QPainter *painter, QPrinter *printer);
|
void printDiagram(Diagram *diagram, bool fit_page, QPainter *painter, QPrinter *printer, const QMap<Diagram*, int> &diagramPageMap = {});
|
||||||
QRect diagramRect(Diagram *diagram, const ExportProperties &option) const;
|
QRect diagramRect(Diagram *diagram, const ExportProperties &option) const;
|
||||||
int horizontalPagesCount(Diagram *diagram, const ExportProperties &option, bool full_page) const;
|
int horizontalPagesCount(Diagram *diagram, const ExportProperties &option, bool full_page) const;
|
||||||
int verticalPagesCount(Diagram *diagram, const ExportProperties &option, bool full_page) const;
|
int verticalPagesCount(Diagram *diagram, const ExportProperties &option, bool full_page) const;
|
||||||
|
|||||||
@@ -45,14 +45,26 @@ QDomElement ElementData::toXml(QDomDocument &xml_element) const {
|
|||||||
bool ElementData::fromXml(const QDomElement &xml_element)
|
bool ElementData::fromXml(const QDomElement &xml_element)
|
||||||
{
|
{
|
||||||
if(xml_element.tagName() != QLatin1String("definition") ||
|
if(xml_element.tagName() != QLatin1String("definition") ||
|
||||||
xml_element.attribute(QStringLiteral("type")) != QLatin1String("element")) {
|
xml_element.attribute(QStringLiteral("type")) != QLatin1String("element")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- HIER STARTET UNSER DEBUG-BLOCK ---
|
||||||
|
// Wir holen den String aus der XML und speichern ihn kurz zwischen
|
||||||
|
QString raw_type_string = xml_element.attribute(QStringLiteral("link_type"), QStringLiteral("simple"));
|
||||||
|
|
||||||
|
qDebug() << "\n=== NEUES BAUTEIL WIRD GELADEN ===";
|
||||||
|
qDebug() << "[XML Parser] Roher 'link_type' String aus der .elmt Datei:" << raw_type_string;
|
||||||
|
|
||||||
|
// Jetzt übergeben wir ihn an deine Übersetzungs-Funktion
|
||||||
|
m_type = typeFromString(raw_type_string);
|
||||||
|
|
||||||
|
qDebug() << "[XML Parser] Übersetzter ElementData-Typ:" << typeToString(m_type);
|
||||||
|
// --- HIER ENDET UNSER DEBUG-BLOCK ---
|
||||||
|
|
||||||
m_type = typeFromString(xml_element.attribute(QStringLiteral("link_type"), QStringLiteral("simple")));
|
|
||||||
kindInfoFromXml(xml_element);
|
kindInfoFromXml(xml_element);
|
||||||
m_informations.fromXml(xml_element.firstChildElement(QStringLiteral("elementInformations")),
|
m_informations.fromXml(xml_element.firstChildElement(QStringLiteral("elementInformations")),
|
||||||
QStringLiteral("elementInformation"));
|
QStringLiteral("elementInformation"));
|
||||||
m_names_list.fromXml(xml_element);
|
m_names_list.fromXml(xml_element);
|
||||||
|
|
||||||
auto xml_draw_info = xml_element.firstChildElement(QStringLiteral("informations"));
|
auto xml_draw_info = xml_element.firstChildElement(QStringLiteral("informations"));
|
||||||
@@ -323,6 +335,8 @@ QString ElementData::typeToString(ElementData::Type type)
|
|||||||
return QStringLiteral("terminal");
|
return QStringLiteral("terminal");
|
||||||
case ElementData::Thumbnail:
|
case ElementData::Thumbnail:
|
||||||
return QStringLiteral("thumbnail");
|
return QStringLiteral("thumbnail");
|
||||||
|
case ElementData::ConductorDefinition:
|
||||||
|
return QStringLiteral("conductor_definition");
|
||||||
default:
|
default:
|
||||||
qDebug() << "ElementData::typeToString : type don't exist"
|
qDebug() << "ElementData::typeToString : type don't exist"
|
||||||
<< "return failsafe value 'simple'";
|
<< "return failsafe value 'simple'";
|
||||||
@@ -346,6 +360,8 @@ ElementData::Type ElementData::typeFromString(const QString &string)
|
|||||||
return ElementData::Terminal;
|
return ElementData::Terminal;
|
||||||
} else if (string == QLatin1String("thumbnail")) {
|
} else if (string == QLatin1String("thumbnail")) {
|
||||||
return ElementData::Thumbnail;
|
return ElementData::Thumbnail;
|
||||||
|
} else if (string == QLatin1String("conductor_definition")) {
|
||||||
|
return ElementData::ConductorDefinition;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Return simple if nothing match
|
//Return simple if nothing match
|
||||||
|
|||||||
@@ -41,7 +41,8 @@ class ElementData : public PropertiesInterface
|
|||||||
Master = 8,
|
Master = 8,
|
||||||
Slave = 16,
|
Slave = 16,
|
||||||
Terminal = 32,
|
Terminal = 32,
|
||||||
Thumbnail = 64};
|
Thumbnail = 64,
|
||||||
|
ConductorDefinition = 128};
|
||||||
Q_ENUM(Type)
|
Q_ENUM(Type)
|
||||||
Q_DECLARE_FLAGS(Types, Type)
|
Q_DECLARE_FLAGS(Types, Type)
|
||||||
|
|
||||||
|
|||||||
@@ -174,6 +174,12 @@ QString TerminalData::typeToString(TerminalData::Type type)
|
|||||||
return QString("Inner");
|
return QString("Inner");
|
||||||
case Outer :
|
case Outer :
|
||||||
return QString("Outer");
|
return QString("Outer");
|
||||||
|
case No :
|
||||||
|
return QString("No");
|
||||||
|
case Nc :
|
||||||
|
return QString("Nc");
|
||||||
|
case Common :
|
||||||
|
return QString("Common");
|
||||||
}
|
}
|
||||||
return QString("Generic");
|
return QString("Generic");
|
||||||
}
|
}
|
||||||
@@ -193,6 +199,12 @@ TerminalData::Type TerminalData::typeFromString(const QString &string)
|
|||||||
return TerminalData::Inner;
|
return TerminalData::Inner;
|
||||||
} else if (string == "Outer") {
|
} else if (string == "Outer") {
|
||||||
return TerminalData::Outer;
|
return TerminalData::Outer;
|
||||||
|
} else if (string == "No") {
|
||||||
|
return TerminalData::No;
|
||||||
|
} else if (string == "Nc") {
|
||||||
|
return TerminalData::Nc;
|
||||||
|
} else if (string == "Common") {
|
||||||
|
return TerminalData::Common;
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "TerminalData::typeFromString, argument string is invalid"
|
qDebug() << "TerminalData::typeFromString, argument string is invalid"
|
||||||
" failsafe type 'TerminalData::Generic' is returned";
|
" failsafe type 'TerminalData::Generic' is returned";
|
||||||
|
|||||||
@@ -41,7 +41,10 @@ class TerminalData : public PropertiesInterface
|
|||||||
enum Type {
|
enum Type {
|
||||||
Generic,
|
Generic,
|
||||||
Inner,
|
Inner,
|
||||||
Outer
|
Outer,
|
||||||
|
No, ///< Normally Open terminal (for SW contacts)
|
||||||
|
Nc, ///< Normally Closed terminal (for SW contacts)
|
||||||
|
Common ///< Common terminal (for SW contacts)
|
||||||
};
|
};
|
||||||
Q_ENUM(Type)
|
Q_ENUM(Type)
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
XRefProperties::XRefProperties()
|
XRefProperties::XRefProperties()
|
||||||
{
|
{
|
||||||
m_show_power_ctc = true;
|
m_show_power_ctc = true;
|
||||||
|
m_show_terminal_name = true;
|
||||||
m_display = Cross;
|
m_display = Cross;
|
||||||
m_snap_to = Bottom;
|
m_snap_to = Bottom;
|
||||||
m_prefix_keys << "power" << "delay" << "switch";
|
m_prefix_keys << "power" << "delay" << "switch";
|
||||||
@@ -48,6 +49,7 @@ void XRefProperties::toSettings(QSettings &settings,
|
|||||||
const QString prefix) const
|
const QString prefix) const
|
||||||
{
|
{
|
||||||
settings.setValue(prefix % "showpowerctc", m_show_power_ctc);
|
settings.setValue(prefix % "showpowerctc", m_show_power_ctc);
|
||||||
|
settings.setValue(prefix % "showterminalname", m_show_terminal_name);
|
||||||
QString display = m_display == Cross? "cross" : "contacts";
|
QString display = m_display == Cross? "cross" : "contacts";
|
||||||
settings.setValue(prefix % "displayhas", display);
|
settings.setValue(prefix % "displayhas", display);
|
||||||
QString snap = m_snap_to == Bottom? "bottom" : "label";
|
QString snap = m_snap_to == Bottom? "bottom" : "label";
|
||||||
@@ -78,6 +80,7 @@ void XRefProperties::fromSettings(const QSettings &settings,
|
|||||||
const QString prefix)
|
const QString prefix)
|
||||||
{
|
{
|
||||||
m_show_power_ctc = settings.value(prefix % "showpowerctc", true).toBool();
|
m_show_power_ctc = settings.value(prefix % "showpowerctc", true).toBool();
|
||||||
|
m_show_terminal_name = settings.value(prefix % "showterminalname", true).toBool();
|
||||||
QString display = settings.value(prefix % "displayhas", "cross").toString();
|
QString display = settings.value(prefix % "displayhas", "cross").toString();
|
||||||
display == "cross"? m_display = Cross : m_display = Contacts;
|
display == "cross"? m_display = Cross : m_display = Contacts;
|
||||||
QString snap = settings.value(prefix % "snapto", "label").toString();
|
QString snap = settings.value(prefix % "snapto", "label").toString();
|
||||||
@@ -107,6 +110,7 @@ QDomElement XRefProperties::toXml(QDomDocument &xml_document) const
|
|||||||
xml_element.setAttribute("type", m_key);
|
xml_element.setAttribute("type", m_key);
|
||||||
|
|
||||||
xml_element.setAttribute("showpowerctc", m_show_power_ctc? "true" : "false");
|
xml_element.setAttribute("showpowerctc", m_show_power_ctc? "true" : "false");
|
||||||
|
xml_element.setAttribute("showterminalname", m_show_terminal_name? "true" : "false");
|
||||||
QString display = m_display == Cross? "cross" : "contacts";
|
QString display = m_display == Cross? "cross" : "contacts";
|
||||||
xml_element.setAttribute("displayhas", display);
|
xml_element.setAttribute("displayhas", display);
|
||||||
QString snap = m_snap_to == Bottom? "bottom" : "label";
|
QString snap = m_snap_to == Bottom? "bottom" : "label";
|
||||||
@@ -137,6 +141,7 @@ QDomElement XRefProperties::toXml(QDomDocument &xml_document) const
|
|||||||
*/
|
*/
|
||||||
bool XRefProperties::fromXml(const QDomElement &xml_element) {
|
bool XRefProperties::fromXml(const QDomElement &xml_element) {
|
||||||
m_show_power_ctc = xml_element.attribute("showpowerctc") == "true";
|
m_show_power_ctc = xml_element.attribute("showpowerctc") == "true";
|
||||||
|
m_show_terminal_name = xml_element.attribute("showterminalname", "true") == "true";
|
||||||
QString display = xml_element.attribute("displayhas", "cross");
|
QString display = xml_element.attribute("displayhas", "cross");
|
||||||
display == "cross"? m_display = Cross : m_display = Contacts;
|
display == "cross"? m_display = Cross : m_display = Contacts;
|
||||||
QString snap = xml_element.attribute("snapto", "label");
|
QString snap = xml_element.attribute("snapto", "label");
|
||||||
@@ -188,6 +193,7 @@ QHash<QString, XRefProperties> XRefProperties::defaultProperties()
|
|||||||
|
|
||||||
bool XRefProperties::operator ==(const XRefProperties &xrp) const{
|
bool XRefProperties::operator ==(const XRefProperties &xrp) const{
|
||||||
return (m_show_power_ctc == xrp.m_show_power_ctc
|
return (m_show_power_ctc == xrp.m_show_power_ctc
|
||||||
|
&& m_show_terminal_name == xrp.m_show_terminal_name
|
||||||
&& m_display == xrp.m_display
|
&& m_display == xrp.m_display
|
||||||
&& m_snap_to == xrp.m_snap_to
|
&& m_snap_to == xrp.m_snap_to
|
||||||
&& m_prefix == xrp.m_prefix
|
&& m_prefix == xrp.m_prefix
|
||||||
|
|||||||
@@ -57,6 +57,9 @@ class XRefProperties : public PropertiesInterface
|
|||||||
void setShowPowerContac (const bool a) {m_show_power_ctc = a;}
|
void setShowPowerContac (const bool a) {m_show_power_ctc = a;}
|
||||||
bool showPowerContact () const {return m_show_power_ctc;}
|
bool showPowerContact () const {return m_show_power_ctc;}
|
||||||
|
|
||||||
|
void setShowTerminalName (const bool a) {m_show_terminal_name = a;}
|
||||||
|
bool showTerminalName () const {return m_show_terminal_name;}
|
||||||
|
|
||||||
void setDisplayHas (const DisplayHas dh) {m_display = dh;}
|
void setDisplayHas (const DisplayHas dh) {m_display = dh;}
|
||||||
DisplayHas displayHas () const {return m_display;}
|
DisplayHas displayHas () const {return m_display;}
|
||||||
|
|
||||||
@@ -81,6 +84,7 @@ class XRefProperties : public PropertiesInterface
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_show_power_ctc;
|
bool m_show_power_ctc;
|
||||||
|
bool m_show_terminal_name;
|
||||||
DisplayHas m_display;
|
DisplayHas m_display;
|
||||||
SnapTo m_snap_to;
|
SnapTo m_snap_to;
|
||||||
Qt::AlignmentFlag m_xref_pos;
|
Qt::AlignmentFlag m_xref_pos;
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
*/
|
*/
|
||||||
#include "crossrefitem.h"
|
#include "crossrefitem.h"
|
||||||
|
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
#include "../autoNum/assignvariables.h"
|
#include "../autoNum/assignvariables.h"
|
||||||
#include "../diagram.h"
|
#include "../diagram.h"
|
||||||
#include "../diagramposition.h"
|
#include "../diagramposition.h"
|
||||||
@@ -25,6 +27,7 @@
|
|||||||
#include "element.h"
|
#include "element.h"
|
||||||
#include "elementtextitemgroup.h"
|
#include "elementtextitemgroup.h"
|
||||||
#include "qgraphicsitemutility.h"
|
#include "qgraphicsitemutility.h"
|
||||||
|
#include "terminal.h"
|
||||||
|
|
||||||
//define the height of the header.
|
//define the height of the header.
|
||||||
static int header = 5;
|
static int header = 5;
|
||||||
@@ -221,9 +224,12 @@ void CrossRefItem::updateLabel()
|
|||||||
prepareGeometryChange();
|
prepareGeometryChange();
|
||||||
m_bounding_rect = QRectF();
|
m_bounding_rect = QRectF();
|
||||||
|
|
||||||
//init the painter
|
// Build geometry and m_hovered_contacts_map using a QImage-backed
|
||||||
QPainter qp;
|
// painter so font metrics match the screen painter in paint().
|
||||||
qp.begin(&m_drawing);
|
// m_update_map=true allows the draw functions to populate the map;
|
||||||
|
// paint() calls them with m_update_map=false so the map is stable.
|
||||||
|
QImage dummy(1, 1, QImage::Format_ARGB32_Premultiplied);
|
||||||
|
QPainter qp(&dummy);
|
||||||
QPen pen_;
|
QPen pen_;
|
||||||
pen_.setWidthF(0.5);
|
pen_.setWidthF(0.5);
|
||||||
qp.setPen(pen_);
|
qp.setPen(pen_);
|
||||||
@@ -232,17 +238,21 @@ void CrossRefItem::updateLabel()
|
|||||||
//Draw cross or contact, only if master element is linked.
|
//Draw cross or contact, only if master element is linked.
|
||||||
if (! m_element->linkedElements().isEmpty())
|
if (! m_element->linkedElements().isEmpty())
|
||||||
{
|
{
|
||||||
|
m_update_map = true;
|
||||||
XRefProperties::DisplayHas dh = m_properties.displayHas();
|
XRefProperties::DisplayHas dh = m_properties.displayHas();
|
||||||
|
|
||||||
if (dh == XRefProperties::Cross)
|
if (dh == XRefProperties::Cross)
|
||||||
drawAsCross(qp);
|
drawAsCross(qp);
|
||||||
else if (dh == XRefProperties::Contacts)
|
else if (dh == XRefProperties::Contacts)
|
||||||
drawAsContacts(qp);
|
drawAsContacts(qp);
|
||||||
|
m_update_map = false;
|
||||||
}
|
}
|
||||||
qp.end();
|
|
||||||
|
|
||||||
autoPos();
|
autoPos();
|
||||||
update();
|
update();
|
||||||
|
// Schedule a second update after the scene has finished laying out,
|
||||||
|
// so the initial render uses the correct bounding rect.
|
||||||
|
QTimer::singleShot(0, this, [this]{ update(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -310,7 +320,26 @@ void CrossRefItem::paint(
|
|||||||
{
|
{
|
||||||
Q_UNUSED(option)
|
Q_UNUSED(option)
|
||||||
Q_UNUSED(widget)
|
Q_UNUSED(widget)
|
||||||
m_drawing.play(painter);
|
// Draw directly — no QPicture involved anywhere.
|
||||||
|
// QPicture::play() + nested drawPicture() (m_hdr_no_ctc/m_hdr_nc_ctc)
|
||||||
|
// caused a use-after-free crash (QRegion::begin, Qt5Gui+0x49af60)
|
||||||
|
// confirmed by analysis of 19+ coredumps.
|
||||||
|
// m_update_map=false: draw functions do not overwrite m_hovered_contacts_map.
|
||||||
|
if (m_element->linkedElements().isEmpty()) return;
|
||||||
|
|
||||||
|
QPen pen_;
|
||||||
|
pen_.setWidthF(0.5);
|
||||||
|
painter->save();
|
||||||
|
painter->setPen(pen_);
|
||||||
|
painter->setFont(QETApp::diagramTextsFont(5));
|
||||||
|
|
||||||
|
m_update_map = false;
|
||||||
|
XRefProperties::DisplayHas dh = m_properties.displayHas();
|
||||||
|
if (dh == XRefProperties::Cross)
|
||||||
|
drawAsCross(*painter);
|
||||||
|
else if (dh == XRefProperties::Contacts)
|
||||||
|
drawAsContacts(*painter);
|
||||||
|
painter->restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -320,7 +349,24 @@ void CrossRefItem::paint(
|
|||||||
void CrossRefItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
|
void CrossRefItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
|
||||||
{
|
{
|
||||||
event->accept();
|
event->accept();
|
||||||
QetGraphicsItem::showItem(m_hovered_contact);
|
|
||||||
|
// Find the element under the click position directly from the map,
|
||||||
|
// rather than relying on m_hovered_contact which may have been reset
|
||||||
|
// by hoverMoveEvent between the two clicks of the double-click.
|
||||||
|
QPointF pos = event->pos();
|
||||||
|
Element *target = m_hovered_contact;
|
||||||
|
|
||||||
|
if (!target) {
|
||||||
|
for (auto it = m_hovered_contacts_map.begin();
|
||||||
|
it != m_hovered_contacts_map.end(); ++it) {
|
||||||
|
if (it.value().contains(pos)) {
|
||||||
|
target = it.key();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QetGraphicsItem::showItem(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -432,49 +478,41 @@ void CrossRefItem::linkedChanged()
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
@brief CrossRefItem::buildHeaderContact
|
@brief CrossRefItem::buildHeaderContact
|
||||||
Draw the QPicture of m_hdr_no_ctc and m_hdr_nc_ctc
|
Draw NO and NC contact symbols directly onto painter.
|
||||||
|
Previously used QPicture (m_hdr_no_ctc/m_hdr_nc_ctc) which caused
|
||||||
|
use-after-free crashes via nested QPicture::play() calls.
|
||||||
*/
|
*/
|
||||||
void CrossRefItem::buildHeaderContact()
|
void CrossRefItem::buildHeaderContact(QPainter &painter, QPointF no_pos, QPointF nc_pos)
|
||||||
{
|
{
|
||||||
if (!m_hdr_no_ctc.isNull() && !m_hdr_nc_ctc.isNull()) return;
|
|
||||||
|
|
||||||
//init the painter
|
|
||||||
QPainter qp;
|
|
||||||
QPen pen_;
|
QPen pen_;
|
||||||
pen_.setWidthF(0.2);
|
pen_.setWidthF(0.2);
|
||||||
|
painter.save();
|
||||||
|
painter.setPen(pen_);
|
||||||
|
|
||||||
//draw the NO contact
|
//draw the NO contact header symbol
|
||||||
if (m_hdr_no_ctc.isNull()) {
|
painter.drawLine(no_pos.x()+0, no_pos.y()+3, no_pos.x()+5, no_pos.y()+3);
|
||||||
qp.begin(&m_hdr_no_ctc);
|
QPointF p1[3] = {
|
||||||
qp.setPen(pen_);
|
QPointF(no_pos.x()+5, no_pos.y()+0),
|
||||||
qp.drawLine(0, 3, 5, 3);
|
QPointF(no_pos.x()+10, no_pos.y()+3),
|
||||||
QPointF p1[3] = {
|
QPointF(no_pos.x()+15, no_pos.y()+3),
|
||||||
QPointF(5, 0),
|
};
|
||||||
QPointF(10, 3),
|
painter.drawPolyline(p1, 3);
|
||||||
QPointF(15, 3),
|
|
||||||
};
|
|
||||||
qp.drawPolyline(p1,3);
|
|
||||||
qp.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
//draw the NC contact
|
//draw the NC contact header symbol
|
||||||
if (m_hdr_nc_ctc.isNull()) {
|
QPointF p2[3] = {
|
||||||
qp.begin(&m_hdr_nc_ctc);
|
QPointF(nc_pos.x()+0, nc_pos.y()+3),
|
||||||
qp.setPen(pen_);
|
QPointF(nc_pos.x()+5, nc_pos.y()+3),
|
||||||
QPointF p2[3] = {
|
QPointF(nc_pos.x()+5, nc_pos.y()+0)
|
||||||
QPointF(0, 3),
|
};
|
||||||
QPointF(5, 3),
|
painter.drawPolyline(p2, 3);
|
||||||
QPointF(5, 0)
|
QPointF p3[3] = {
|
||||||
};
|
QPointF(nc_pos.x()+4, nc_pos.y()+0),
|
||||||
qp.drawPolyline(p2,3);
|
QPointF(nc_pos.x()+10, nc_pos.y()+3),
|
||||||
QPointF p3[3] = {
|
QPointF(nc_pos.x()+15, nc_pos.y()+3),
|
||||||
QPointF(4, 0),
|
};
|
||||||
QPointF(10, 3),
|
painter.drawPolyline(p3, 3);
|
||||||
QPointF(15, 3),
|
|
||||||
};
|
painter.restore();
|
||||||
qp.drawPolyline(p3,3);
|
|
||||||
qp.end();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -492,11 +530,95 @@ void CrossRefItem::setUpCrossBoundingRect(QPainter &painter)
|
|||||||
|
|
||||||
QStringList no_str, nc_str;
|
QStringList no_str, nc_str;
|
||||||
|
|
||||||
|
// Helper lambda: build "[13-14] pos" string for an element.
|
||||||
|
// For power contacts (e.g. 3-pole contactor), all named terminals
|
||||||
|
// are collected (e.g. "[1-2-3-4-5-6] pos").
|
||||||
|
// For other contacts (NO/NC/SW), only the first 2 named terminals
|
||||||
|
// are used — users are expected to name and order their terminals
|
||||||
|
// in the element editor.
|
||||||
|
// Helper lambda: build "[13-14] pos" for an element.
|
||||||
|
// - Power: all terminals sorted numerically → "[1-2-3-4-5-6] pos"
|
||||||
|
// - SW with typed terminals: show relevant pair per column (handled below)
|
||||||
|
// - Others: first 2 named terminals
|
||||||
|
auto buildLabel = [this](Element *elmt, bool is_no_col) -> QString {
|
||||||
|
const bool is_power =
|
||||||
|
elmt->kindInformations()["type"].toString() == "power";
|
||||||
|
const bool is_sw =
|
||||||
|
elmt->kindInformations()["state"].toString() == "SW";
|
||||||
|
|
||||||
|
QStringList tnames;
|
||||||
|
|
||||||
|
if (is_sw) {
|
||||||
|
// Check if terminals have explicit No/Nc/Common types
|
||||||
|
bool has_typed = false;
|
||||||
|
for (Terminal *t : elmt->terminals()) {
|
||||||
|
if (!t) continue;
|
||||||
|
if (t->terminalType() == TerminalData::No ||
|
||||||
|
t->terminalType() == TerminalData::Nc ||
|
||||||
|
t->terminalType() == TerminalData::Common) {
|
||||||
|
has_typed = true; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (has_typed) {
|
||||||
|
QString no_name, nc_name, common_name;
|
||||||
|
for (Terminal *t : elmt->terminals()) {
|
||||||
|
if (!t) continue;
|
||||||
|
if (!t->name().isEmpty()) {
|
||||||
|
if (t->terminalType() == TerminalData::No) no_name = t->name();
|
||||||
|
else if (t->terminalType() == TerminalData::Nc) nc_name = t->name();
|
||||||
|
else if (t->terminalType() == TerminalData::Common) common_name = t->name();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// NO column: show NO+Common pair; NC column: show NC+Common pair
|
||||||
|
if (is_no_col)
|
||||||
|
tnames << no_name << common_name;
|
||||||
|
else
|
||||||
|
tnames << nc_name << common_name;
|
||||||
|
} else {
|
||||||
|
// Fallback: first 2 named terminals
|
||||||
|
for (Terminal *t : elmt->terminals()) {
|
||||||
|
if (!t) continue;
|
||||||
|
const QString tn = t->name();
|
||||||
|
if (!tn.isEmpty()) { tnames << tn; if (tnames.size() >= 2) break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (Terminal *t : elmt->terminals()) {
|
||||||
|
if (!t) continue;
|
||||||
|
const QString tn = t->name();
|
||||||
|
if (!tn.isEmpty()) {
|
||||||
|
tnames << tn;
|
||||||
|
if (!is_power && tnames.size() >= 2) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_power) {
|
||||||
|
std::sort(tnames.begin(), tnames.end(),
|
||||||
|
[](const QString &a, const QString &b){
|
||||||
|
int i_a = a.size();
|
||||||
|
while (i_a > 0 && a[i_a-1].isDigit()) --i_a;
|
||||||
|
int i_b = b.size();
|
||||||
|
while (i_b > 0 && b[i_b-1].isDigit()) --i_b;
|
||||||
|
bool a_ok = false, b_ok = false;
|
||||||
|
int ai = a.mid(i_a).toInt(&a_ok);
|
||||||
|
int bi = b.mid(i_b).toInt(&b_ok);
|
||||||
|
if (a_ok && b_ok && a.left(i_a) == b.left(i_b))
|
||||||
|
return ai < bi;
|
||||||
|
return a < b;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString pos = elementPositionText(elmt, true);
|
||||||
|
if (!tnames.isEmpty() && m_properties.showTerminalName())
|
||||||
|
return QStringLiteral("[") + tnames.join("-") + QStringLiteral("] ") + pos;
|
||||||
|
return pos;
|
||||||
|
};
|
||||||
|
|
||||||
for (auto elmt : NOElements()) {
|
for (auto elmt : NOElements()) {
|
||||||
no_str.append(elementPositionText(elmt, true));
|
no_str.append(buildLabel(elmt, true));
|
||||||
}
|
}
|
||||||
for (auto elmt : NCElements()) {
|
for (auto elmt : NCElements()) {
|
||||||
nc_str.append(elementPositionText(elmt, true));
|
nc_str.append(buildLabel(elmt, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
//There is no string to display, we return now
|
//There is no string to display, we return now
|
||||||
@@ -509,9 +631,10 @@ void CrossRefItem::setUpCrossBoundingRect(QPainter &painter)
|
|||||||
QRectF no_bounding;
|
QRectF no_bounding;
|
||||||
for (auto str : no_str)
|
for (auto str : no_str)
|
||||||
{
|
{
|
||||||
QRectF bounding = painter.boundingRect(QRectF (), Qt::AlignCenter, str);
|
QRectF bounding = painter.boundingRect(QRectF(0, 0, 500, 20), Qt::AlignLeft, str);
|
||||||
no_bounding = no_bounding.united(bounding);
|
|
||||||
no_bounding.setHeight(no_bounding.height() + bounding.height());
|
no_bounding.setHeight(no_bounding.height() + bounding.height());
|
||||||
|
if (bounding.width() > no_bounding.width())
|
||||||
|
no_bounding.setWidth(bounding.width());
|
||||||
}
|
}
|
||||||
//Adjust according to the NO
|
//Adjust according to the NO
|
||||||
if (no_bounding.height() > default_bounding.height() - header)
|
if (no_bounding.height() > default_bounding.height() - header)
|
||||||
@@ -523,9 +646,10 @@ void CrossRefItem::setUpCrossBoundingRect(QPainter &painter)
|
|||||||
QRectF nc_bounding;
|
QRectF nc_bounding;
|
||||||
for (auto str : nc_str)
|
for (auto str : nc_str)
|
||||||
{
|
{
|
||||||
QRectF bounding = painter.boundingRect(QRectF (), Qt::AlignCenter, str);
|
QRectF bounding = painter.boundingRect(QRectF(0, 0, 500, 20), Qt::AlignLeft, str);
|
||||||
nc_bounding = nc_bounding.united(bounding);
|
|
||||||
nc_bounding.setHeight(nc_bounding.height() + bounding.height());
|
nc_bounding.setHeight(nc_bounding.height() + bounding.height());
|
||||||
|
if (bounding.width() > nc_bounding.width())
|
||||||
|
nc_bounding.setWidth(bounding.width());
|
||||||
}
|
}
|
||||||
//Adjust according to the NC
|
//Adjust according to the NC
|
||||||
if (nc_bounding.height() > default_bounding.height() - header)
|
if (nc_bounding.height() > default_bounding.height() - header)
|
||||||
@@ -549,7 +673,8 @@ void CrossRefItem::drawAsCross(QPainter &painter)
|
|||||||
{
|
{
|
||||||
//calculate the size of the cross
|
//calculate the size of the cross
|
||||||
setUpCrossBoundingRect(painter);
|
setUpCrossBoundingRect(painter);
|
||||||
m_hovered_contacts_map.clear();
|
m_drawed_contacts = 0;
|
||||||
|
if (m_update_map) m_hovered_contacts_map.clear();
|
||||||
|
|
||||||
//Bounding rect is empty that mean there's no contact to draw
|
//Bounding rect is empty that mean there's no contact to draw
|
||||||
if (boundingRect().isEmpty()) return;
|
if (boundingRect().isEmpty()) return;
|
||||||
@@ -559,12 +684,11 @@ void CrossRefItem::drawAsCross(QPainter &painter)
|
|||||||
painter.drawLine(br.width()/2, 0, br.width()/2, br.height()); //vertical line
|
painter.drawLine(br.width()/2, 0, br.width()/2, br.height()); //vertical line
|
||||||
painter.drawLine(0, header, br.width(), header); //horizontal line
|
painter.drawLine(0, header, br.width(), header); //horizontal line
|
||||||
|
|
||||||
//Add the symbolic contacts
|
//Add the symbolic contacts (drawn directly, no QPicture)
|
||||||
buildHeaderContact();
|
static const qreal hdr_symbol_width = 15.0;
|
||||||
QPointF p((m_bounding_rect.width()/4) - (m_hdr_no_ctc.width()/2), 0);
|
QPointF no_pos((m_bounding_rect.width()/4) - (hdr_symbol_width/2), 0);
|
||||||
painter.drawPicture (p, m_hdr_no_ctc);
|
QPointF nc_pos((m_bounding_rect.width() * 3/4) - (hdr_symbol_width/2), 0);
|
||||||
p.setX((m_bounding_rect.width() * 3/4) - (m_hdr_nc_ctc.width()/2));
|
buildHeaderContact(painter, no_pos, nc_pos);
|
||||||
painter.drawPicture (p, m_hdr_nc_ctc);
|
|
||||||
|
|
||||||
//and fill it
|
//and fill it
|
||||||
fillCrossRef(painter);
|
fillCrossRef(painter);
|
||||||
@@ -581,7 +705,7 @@ void CrossRefItem::drawAsContacts(QPainter &painter)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
m_drawed_contacts = 0;
|
m_drawed_contacts = 0;
|
||||||
m_hovered_contacts_map.clear();
|
if (m_update_map) m_hovered_contacts_map.clear();
|
||||||
QRectF bounding_rect;
|
QRectF bounding_rect;
|
||||||
|
|
||||||
//Draw each linked contact
|
//Draw each linked contact
|
||||||
@@ -605,7 +729,7 @@ void CrossRefItem::drawAsContacts(QPainter &painter)
|
|||||||
else if (type == "delayOff") option += DelayOff;
|
else if (type == "delayOff") option += DelayOff;
|
||||||
else if (type == "delayOnOff") option += DelayOnOff;
|
else if (type == "delayOnOff") option += DelayOnOff;
|
||||||
|
|
||||||
QRectF br = drawContact(painter, option, elmt);
|
QRectF br = drawContact(painter, option, elmt, i);
|
||||||
bounding_rect = bounding_rect.united(br);
|
bounding_rect = bounding_rect.united(br);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -624,9 +748,81 @@ void CrossRefItem::drawAsContacts(QPainter &painter)
|
|||||||
@param elmt : the element to display text (the position of the contact)
|
@param elmt : the element to display text (the position of the contact)
|
||||||
@return The bounding rect of the draw (contact + text)
|
@return The bounding rect of the draw (contact + text)
|
||||||
*/
|
*/
|
||||||
QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt)
|
QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt, int pole_index)
|
||||||
{
|
{
|
||||||
QString str = elementPositionText(elmt);
|
QString str = elementPositionText(elmt);
|
||||||
|
|
||||||
|
// Collect terminal names from the element definition (.elmt)
|
||||||
|
// e.g. name="13" and name="14" on each terminal
|
||||||
|
// For power contacts, sort numerically and pick the pair for pole_index.
|
||||||
|
// For SW contacts with typed terminals (No/Nc/Common), filter by role.
|
||||||
|
QStringList terminal_names;
|
||||||
|
const bool is_power_ctc =
|
||||||
|
elmt->kindInformations()["type"].toString() == "power";
|
||||||
|
const bool is_sw = (flags & SW) && !(flags & NOC);
|
||||||
|
|
||||||
|
// Check if SW terminals have explicit No/Nc/Common types
|
||||||
|
bool sw_has_typed_terminals = false;
|
||||||
|
if (is_sw) {
|
||||||
|
for (Terminal *t : elmt->terminals()) {
|
||||||
|
if (!t) continue;
|
||||||
|
if (t->terminalType() == TerminalData::No ||
|
||||||
|
t->terminalType() == TerminalData::Nc ||
|
||||||
|
t->terminalType() == TerminalData::Common) {
|
||||||
|
sw_has_typed_terminals = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Terminal *t : elmt->terminals()) {
|
||||||
|
if (!t) continue;
|
||||||
|
const QString tname = t->name();
|
||||||
|
if (!tname.isEmpty())
|
||||||
|
terminal_names << tname;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_power_ctc) {
|
||||||
|
// Sort terminals alphanumerically so names like "R1","R2"... or "1","2"...
|
||||||
|
// are ordered correctly. Extract trailing digits for numeric comparison;
|
||||||
|
// fall back to full string comparison when no digits are found.
|
||||||
|
std::sort(terminal_names.begin(), terminal_names.end(),
|
||||||
|
[](const QString &a, const QString &b){
|
||||||
|
// Extract trailing numeric part
|
||||||
|
int i_a = a.size();
|
||||||
|
while (i_a > 0 && a[i_a-1].isDigit()) --i_a;
|
||||||
|
int i_b = b.size();
|
||||||
|
while (i_b > 0 && b[i_b-1].isDigit()) --i_b;
|
||||||
|
bool a_ok = false, b_ok = false;
|
||||||
|
int ai = a.mid(i_a).toInt(&a_ok);
|
||||||
|
int bi = b.mid(i_b).toInt(&b_ok);
|
||||||
|
if (a_ok && b_ok && a.left(i_a) == b.left(i_b))
|
||||||
|
return ai < bi;
|
||||||
|
return a < b;
|
||||||
|
});
|
||||||
|
// Pick the pair for this pole: pole 0 → [0,1], pole 1 → [2,3], etc.
|
||||||
|
int idx = pole_index * 2;
|
||||||
|
if (idx + 1 < terminal_names.size())
|
||||||
|
terminal_names = QStringList() << terminal_names[idx] << terminal_names[idx+1];
|
||||||
|
else
|
||||||
|
terminal_names.clear();
|
||||||
|
} else if (is_sw && sw_has_typed_terminals) {
|
||||||
|
// Build [NO_name, Common_name, NC_name] from typed terminals
|
||||||
|
QString no_name, nc_name, common_name;
|
||||||
|
for (Terminal *t : elmt->terminals()) {
|
||||||
|
if (!t) continue;
|
||||||
|
if (!t->name().isEmpty()) {
|
||||||
|
if (t->terminalType() == TerminalData::No) no_name = t->name();
|
||||||
|
else if (t->terminalType() == TerminalData::Nc) nc_name = t->name();
|
||||||
|
else if (t->terminalType() == TerminalData::Common) common_name = t->name();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// drawText expects: [0]=NC, [1]=NO, [2]=Common
|
||||||
|
// (drawText uses [1] for NO top-left, [0] for NC bottom-left, [2] for Common right)
|
||||||
|
terminal_names.clear();
|
||||||
|
terminal_names << nc_name << no_name << common_name;
|
||||||
|
}
|
||||||
|
|
||||||
int offset = m_drawed_contacts*10;
|
int offset = m_drawed_contacts*10;
|
||||||
QRectF bounding_rect = QRectF(0, offset, 24, 10);
|
QRectF bounding_rect = QRectF(0, offset, 24, 10);
|
||||||
|
|
||||||
@@ -643,15 +839,17 @@ QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt)
|
|||||||
painter.drawLine(0, offset+6, 8, offset+6);
|
painter.drawLine(0, offset+6, 8, offset+6);
|
||||||
painter.drawLine(16, offset+6, 24, offset+6);
|
painter.drawLine(16, offset+6, 24, offset+6);
|
||||||
|
|
||||||
///take example of this code for display the terminal text
|
// Draw terminal names on each side of the contact symbol
|
||||||
/*QFont font = QETApp::diagramTextsFont(4);
|
// terminal_names[0] on the left, terminal_names[1] on the right
|
||||||
font.setBold(true);
|
if (!terminal_names.isEmpty() && m_properties.showTerminalName()) {
|
||||||
painter.setFont(font);
|
painter.setFont(QETApp::diagramTextsFont(4));
|
||||||
QRectF bt(0, offset, 24, 10);
|
QRectF bt(0, offset, 24, 10);
|
||||||
int txt = 10 + m_drawed_contacts;
|
if (terminal_names.size() >= 1)
|
||||||
painter.drawText(bt, Qt::AlignLeft|Qt::AlignTop, QString::number(txt));
|
painter.drawText(bt, Qt::AlignLeft|Qt::AlignTop, terminal_names[0]);
|
||||||
painter.drawText(bt, Qt::AlignRight|Qt::AlignTop, QString::number(txt));
|
if (terminal_names.size() >= 2)
|
||||||
painter.setFont(QETApp::diagramTextsFont(5));*/
|
painter.drawText(bt, Qt::AlignRight|Qt::AlignTop, terminal_names[1]);
|
||||||
|
painter.setFont(QETApp::diagramTextsFont(5));
|
||||||
|
}
|
||||||
|
|
||||||
//draw open contact
|
//draw open contact
|
||||||
if (flags &NO) {
|
if (flags &NO) {
|
||||||
@@ -729,14 +927,8 @@ QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt)
|
|||||||
painter.drawText(text_rect, Qt::AlignLeft | Qt::AlignVCenter, str);
|
painter.drawText(text_rect, Qt::AlignLeft | Qt::AlignVCenter, str);
|
||||||
bounding_rect = bounding_rect.united(text_rect);
|
bounding_rect = bounding_rect.united(text_rect);
|
||||||
|
|
||||||
if (m_hovered_contacts_map.contains(elmt))
|
if (m_update_map)
|
||||||
{
|
m_hovered_contacts_map.insert(elmt, text_rect);
|
||||||
m_hovered_contacts_map.insert(elmt, bounding_rect);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_hovered_contacts_map.insert(elmt, bounding_rect);
|
|
||||||
}
|
|
||||||
|
|
||||||
++m_drawed_contacts;
|
++m_drawed_contacts;
|
||||||
}
|
}
|
||||||
@@ -768,6 +960,25 @@ QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt)
|
|||||||
};
|
};
|
||||||
painter.drawPolyline(p2, 3);
|
painter.drawPolyline(p2, 3);
|
||||||
|
|
||||||
|
// Draw terminal names for switch contact (3 terminals)
|
||||||
|
// terminal_names[0] = NC (bottom-left)
|
||||||
|
// terminal_names[1] = NO (top-left)
|
||||||
|
// terminal_names[2] = Common (right)
|
||||||
|
if (!terminal_names.isEmpty() && m_properties.showTerminalName()) {
|
||||||
|
painter.setFont(QETApp::diagramTextsFont(4));
|
||||||
|
// Storage order set above: [0]=NC, [1]=NO, [2]=Common
|
||||||
|
if (terminal_names.size() >= 2)
|
||||||
|
painter.drawText(QRectF(0, offset, 8, 8),
|
||||||
|
Qt::AlignLeft|Qt::AlignTop, terminal_names[1]); // NO top-left
|
||||||
|
if (terminal_names.size() >= 3)
|
||||||
|
painter.drawText(QRectF(16, offset+4, 8, 6),
|
||||||
|
Qt::AlignRight|Qt::AlignTop, terminal_names[2]); // Common right
|
||||||
|
if (terminal_names.size() >= 1)
|
||||||
|
painter.drawText(QRectF(0, offset+9, 8, 6),
|
||||||
|
Qt::AlignLeft|Qt::AlignTop, terminal_names[0]); // NC bottom-left
|
||||||
|
painter.setFont(QETApp::diagramTextsFont(5));
|
||||||
|
}
|
||||||
|
|
||||||
//Draw the half ellipse off delay
|
//Draw the half ellipse off delay
|
||||||
if (flags &Delay)
|
if (flags &Delay)
|
||||||
{
|
{
|
||||||
@@ -799,12 +1010,8 @@ QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt)
|
|||||||
str);
|
str);
|
||||||
bounding_rect = bounding_rect.united(text_rect);
|
bounding_rect = bounding_rect.united(text_rect);
|
||||||
|
|
||||||
if (m_hovered_contacts_map.contains(elmt)) {
|
if (m_update_map)
|
||||||
m_hovered_contacts_map.insert(elmt, bounding_rect);
|
m_hovered_contacts_map.insert(elmt, text_rect);
|
||||||
}
|
|
||||||
else {
|
|
||||||
m_hovered_contacts_map.insert(elmt, bounding_rect);
|
|
||||||
}
|
|
||||||
|
|
||||||
//a switch contact take place of two normal contact
|
//a switch contact take place of two normal contact
|
||||||
m_drawed_contacts += 2;
|
m_drawed_contacts += 2;
|
||||||
@@ -835,12 +1042,8 @@ QRectF CrossRefItem::drawContact(QPainter &painter, int flags, Element *elmt)
|
|||||||
str);
|
str);
|
||||||
bounding_rect = bounding_rect.united(text_rect);
|
bounding_rect = bounding_rect.united(text_rect);
|
||||||
|
|
||||||
if (m_hovered_contacts_map.contains(elmt)) {
|
if (m_update_map)
|
||||||
m_hovered_contacts_map.insert(elmt, bounding_rect);
|
m_hovered_contacts_map.insert(elmt, text_rect);
|
||||||
}
|
|
||||||
else {
|
|
||||||
m_hovered_contacts_map.insert(elmt, bounding_rect);
|
|
||||||
}
|
|
||||||
++m_drawed_contacts;
|
++m_drawed_contacts;
|
||||||
}
|
}
|
||||||
return bounding_rect;
|
return bounding_rect;
|
||||||
@@ -866,7 +1069,60 @@ void CrossRefItem::fillCrossRef(QPainter &painter)
|
|||||||
m_hovered_contact == elmt ? pen.setColor(Qt::blue) :pen.setColor(Qt::black);
|
m_hovered_contact == elmt ? pen.setColor(Qt::blue) :pen.setColor(Qt::black);
|
||||||
painter.setPen(pen);
|
painter.setPen(pen);
|
||||||
|
|
||||||
|
// Collect terminal names for NO column.
|
||||||
|
// Power: all terminals sorted numerically.
|
||||||
|
// SW with typed terminals: NO+Common pair.
|
||||||
|
// Others: first 2 named terminals.
|
||||||
|
const bool is_power_no =
|
||||||
|
elmt->kindInformations()["type"].toString() == "power";
|
||||||
|
const bool is_sw_no =
|
||||||
|
elmt->kindInformations()["state"].toString() == "SW";
|
||||||
|
QStringList tnames;
|
||||||
|
if (is_sw_no) {
|
||||||
|
bool has_typed = false;
|
||||||
|
for (Terminal *t : elmt->terminals()) {
|
||||||
|
if (!t) continue;
|
||||||
|
if (t->terminalType() == TerminalData::No ||
|
||||||
|
t->terminalType() == TerminalData::Nc ||
|
||||||
|
t->terminalType() == TerminalData::Common) {
|
||||||
|
has_typed = true; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (has_typed) {
|
||||||
|
QString no_name, common_name;
|
||||||
|
for (Terminal *t : elmt->terminals()) {
|
||||||
|
if (!t) continue;
|
||||||
|
if (!t->name().isEmpty()) {
|
||||||
|
if (t->terminalType() == TerminalData::No) no_name = t->name();
|
||||||
|
else if (t->terminalType() == TerminalData::Common) common_name = t->name();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tnames << no_name << common_name;
|
||||||
|
} else {
|
||||||
|
for (Terminal *t : elmt->terminals()) {
|
||||||
|
if (!t) continue;
|
||||||
|
const QString tn = t->name();
|
||||||
|
if (!tn.isEmpty()) { tnames << tn; if (tnames.size() >= 2) break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (Terminal *t : elmt->terminals()) {
|
||||||
|
if (!t) continue;
|
||||||
|
const QString tn = t->name();
|
||||||
|
if (!tn.isEmpty()) {
|
||||||
|
tnames << tn;
|
||||||
|
if (!is_power_no && tnames.size() >= 2) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QString terminal_label;
|
||||||
|
if (!tnames.isEmpty() && m_properties.showTerminalName())
|
||||||
|
terminal_label = QStringLiteral("[") + tnames.join("-") + QStringLiteral("]");
|
||||||
|
|
||||||
QString str = elementPositionText(elmt, true);
|
QString str = elementPositionText(elmt, true);
|
||||||
|
if (!terminal_label.isEmpty())
|
||||||
|
str = terminal_label + QStringLiteral(" ") + str;
|
||||||
|
|
||||||
QRectF bounding = painter.boundingRect(
|
QRectF bounding = painter.boundingRect(
|
||||||
QRectF(no_top_left,
|
QRectF(no_top_left,
|
||||||
QSize(middle_cross, 1)),
|
QSize(middle_cross, 1)),
|
||||||
@@ -874,13 +1130,11 @@ void CrossRefItem::fillCrossRef(QPainter &painter)
|
|||||||
str);
|
str);
|
||||||
painter.drawText(bounding, Qt::AlignLeft, str);
|
painter.drawText(bounding, Qt::AlignLeft, str);
|
||||||
|
|
||||||
if (m_hovered_contacts_map.contains(elmt))
|
if (m_update_map) {
|
||||||
{
|
QString pos_str = elementPositionText(elmt, true);
|
||||||
m_hovered_contacts_map.insert(elmt, bounding);
|
QRectF pos_rect = painter.boundingRect(bounding, Qt::AlignRight, pos_str);
|
||||||
}
|
pos_rect.adjust(-2, -1, 2, 1); // extend hit area slightly
|
||||||
else
|
m_hovered_contacts_map.insert(elmt, pos_rect);
|
||||||
{
|
|
||||||
m_hovered_contacts_map.insert(elmt, bounding);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
no_top_left.ry() += bounding.height();
|
no_top_left.ry() += bounding.height();
|
||||||
@@ -895,7 +1149,60 @@ void CrossRefItem::fillCrossRef(QPainter &painter)
|
|||||||
:pen.setColor(Qt::black);
|
:pen.setColor(Qt::black);
|
||||||
painter.setPen(pen);
|
painter.setPen(pen);
|
||||||
|
|
||||||
|
// Collect terminal names for NC column.
|
||||||
|
// Power: all terminals sorted numerically.
|
||||||
|
// SW with typed terminals: NC+Common pair.
|
||||||
|
// Others: first 2 named terminals.
|
||||||
|
const bool is_power_nc =
|
||||||
|
elmt->kindInformations()["type"].toString() == "power";
|
||||||
|
const bool is_sw_nc =
|
||||||
|
elmt->kindInformations()["state"].toString() == "SW";
|
||||||
|
QStringList tnames_nc;
|
||||||
|
if (is_sw_nc) {
|
||||||
|
bool has_typed = false;
|
||||||
|
for (Terminal *t : elmt->terminals()) {
|
||||||
|
if (!t) continue;
|
||||||
|
if (t->terminalType() == TerminalData::No ||
|
||||||
|
t->terminalType() == TerminalData::Nc ||
|
||||||
|
t->terminalType() == TerminalData::Common) {
|
||||||
|
has_typed = true; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (has_typed) {
|
||||||
|
QString nc_name, common_name;
|
||||||
|
for (Terminal *t : elmt->terminals()) {
|
||||||
|
if (!t) continue;
|
||||||
|
if (!t->name().isEmpty()) {
|
||||||
|
if (t->terminalType() == TerminalData::Nc) nc_name = t->name();
|
||||||
|
else if (t->terminalType() == TerminalData::Common) common_name = t->name();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tnames_nc << nc_name << common_name;
|
||||||
|
} else {
|
||||||
|
for (Terminal *t : elmt->terminals()) {
|
||||||
|
if (!t) continue;
|
||||||
|
const QString tn = t->name();
|
||||||
|
if (!tn.isEmpty()) { tnames_nc << tn; if (tnames_nc.size() >= 2) break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (Terminal *t : elmt->terminals()) {
|
||||||
|
if (!t) continue;
|
||||||
|
const QString tn = t->name();
|
||||||
|
if (!tn.isEmpty()) {
|
||||||
|
tnames_nc << tn;
|
||||||
|
if (!is_power_nc && tnames_nc.size() >= 2) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QString terminal_label;
|
||||||
|
if (!tnames_nc.isEmpty() && m_properties.showTerminalName())
|
||||||
|
terminal_label = QStringLiteral("[") + tnames_nc.join("-") + QStringLiteral("]");
|
||||||
|
|
||||||
QString str = elementPositionText(elmt, true);
|
QString str = elementPositionText(elmt, true);
|
||||||
|
if (!terminal_label.isEmpty())
|
||||||
|
str = terminal_label + QStringLiteral(" ") + str;
|
||||||
|
|
||||||
QRectF bounding = painter.boundingRect(
|
QRectF bounding = painter.boundingRect(
|
||||||
QRectF(nc_top_left,
|
QRectF(nc_top_left,
|
||||||
QSize(middle_cross, 1)),
|
QSize(middle_cross, 1)),
|
||||||
@@ -903,13 +1210,11 @@ void CrossRefItem::fillCrossRef(QPainter &painter)
|
|||||||
str);
|
str);
|
||||||
painter.drawText(bounding, Qt::AlignRight, str);
|
painter.drawText(bounding, Qt::AlignRight, str);
|
||||||
|
|
||||||
if (m_hovered_contacts_map.contains(elmt))
|
if (m_update_map) {
|
||||||
{
|
QString pos_str = elementPositionText(elmt, true);
|
||||||
m_hovered_contacts_map.insert(elmt, bounding);
|
QRectF pos_rect = painter.boundingRect(bounding, Qt::AlignRight, pos_str);
|
||||||
}
|
pos_rect.adjust(-2, -1, 2, 1); // extend hit area slightly
|
||||||
else
|
m_hovered_contacts_map.insert(elmt, pos_rect);
|
||||||
{
|
|
||||||
m_hovered_contacts_map.insert(elmt, bounding);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nc_top_left.ry() += bounding.height();
|
nc_top_left.ry() += bounding.height();
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
|
|
||||||
#include <QGraphicsObject>
|
#include <QGraphicsObject>
|
||||||
#include <QMultiMap>
|
#include <QMultiMap>
|
||||||
#include <QPicture>
|
|
||||||
|
|
||||||
class Element;
|
class Element;
|
||||||
class DynamicElementTextItem;
|
class DynamicElementTextItem;
|
||||||
@@ -103,11 +102,11 @@ class CrossRefItem : public QGraphicsObject
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void linkedChanged();
|
void linkedChanged();
|
||||||
void buildHeaderContact();
|
void buildHeaderContact(QPainter &painter, QPointF no_pos, QPointF nc_pos);
|
||||||
void setUpCrossBoundingRect(QPainter &painter);
|
void setUpCrossBoundingRect(QPainter &painter);
|
||||||
void drawAsCross(QPainter &painter);
|
void drawAsCross(QPainter &painter);
|
||||||
void drawAsContacts(QPainter &painter);
|
void drawAsContacts(QPainter &painter);
|
||||||
QRectF drawContact(QPainter &painter, int flags, Element *elmt);
|
QRectF drawContact(QPainter &painter, int flags, Element *elmt, int pole_index = 0);
|
||||||
void fillCrossRef(QPainter &painter);
|
void fillCrossRef(QPainter &painter);
|
||||||
void AddExtraInfo(QPainter &painter, const QString&);
|
void AddExtraInfo(QPainter &painter, const QString&);
|
||||||
QList<Element *> NOElements() const;
|
QList<Element *> NOElements() const;
|
||||||
@@ -117,16 +116,22 @@ class CrossRefItem : public QGraphicsObject
|
|||||||
private:
|
private:
|
||||||
Element *m_element; //element to display the cross reference
|
Element *m_element; //element to display the cross reference
|
||||||
QRectF m_bounding_rect;
|
QRectF m_bounding_rect;
|
||||||
QPicture m_drawing, m_hdr_no_ctc, m_hdr_nc_ctc;
|
|
||||||
QPainterPath m_shape_path;
|
QPainterPath m_shape_path;
|
||||||
XRefProperties m_properties;
|
XRefProperties m_properties;
|
||||||
int m_drawed_contacts;
|
int m_drawed_contacts;
|
||||||
|
bool m_update_map = false;
|
||||||
QMultiMap <Element *, QRectF> m_hovered_contacts_map;
|
QMultiMap <Element *, QRectF> m_hovered_contacts_map;
|
||||||
Element *m_hovered_contact = nullptr;
|
Element *m_hovered_contact = nullptr;
|
||||||
DynamicElementTextItem *m_text = nullptr;
|
DynamicElementTextItem *m_text = nullptr;
|
||||||
ElementTextItemGroup *m_group = nullptr;
|
ElementTextItemGroup *m_group = nullptr;
|
||||||
QList <QMetaObject::Connection> m_slave_connection;
|
QList <QMetaObject::Connection> m_slave_connection;
|
||||||
QList <QMetaObject::Connection> m_update_connection;
|
QList <QMetaObject::Connection> m_update_connection;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// Returns the map of linked elements and their clickable rects (local coords).
|
||||||
|
/// Used by the PDF export to inject hyperlink annotations.
|
||||||
|
const QMultiMap<Element *, QRectF> &hoveredContactsMap() const
|
||||||
|
{ return m_hovered_contacts_map; }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CROSSREFITEM_H
|
#endif // CROSSREFITEM_H
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
#include "crossrefitem.h"
|
#include "crossrefitem.h"
|
||||||
#include "element.h"
|
#include "element.h"
|
||||||
#include "elementtextitemgroup.h"
|
#include "elementtextitemgroup.h"
|
||||||
|
#include <QTimer>
|
||||||
#include <QDomDocument>
|
#include <QDomDocument>
|
||||||
#include <QDomElement>
|
#include <QDomElement>
|
||||||
#include <QGraphicsSceneMouseEvent>
|
#include <QGraphicsSceneMouseEvent>
|
||||||
@@ -302,7 +302,8 @@ void DynamicElementTextItem::refreshLabelConnection()
|
|||||||
if ((m_text_from == ElementInfo && m_info_name == "label") ||
|
if ((m_text_from == ElementInfo && m_info_name == "label") ||
|
||||||
(m_text_from == CompositeText && m_composite_text.contains("%{label}")))
|
(m_text_from == CompositeText && m_composite_text.contains("%{label}")))
|
||||||
{
|
{
|
||||||
if(m_parent_element.data()->linkType() & Element::AllReport)
|
if((m_parent_element.data()->linkType() & Element::AllReport) ||
|
||||||
|
m_parent_element.data()->linkType() == Element::ConductorDefinition)
|
||||||
{
|
{
|
||||||
updateReportFormulaConnection();
|
updateReportFormulaConnection();
|
||||||
updateReportText();
|
updateReportText();
|
||||||
@@ -418,7 +419,8 @@ void DynamicElementTextItem::setInfoName(const QString &info_name)
|
|||||||
updateXref();
|
updateXref();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_parent_element && (m_parent_element.data()->linkType() & Element::AllReport)) //special treatment for report
|
if (m_parent_element && ((m_parent_element.data()->linkType() & Element::AllReport) ||
|
||||||
|
m_parent_element.data()->linkType() == Element::ConductorDefinition)) //special treatment for report
|
||||||
{
|
{
|
||||||
if(old_info_name != info_name)
|
if(old_info_name != info_name)
|
||||||
{
|
{
|
||||||
@@ -472,7 +474,8 @@ void DynamicElementTextItem::setCompositeText(const QString &text)
|
|||||||
updateXref();
|
updateXref();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_parent_element && (m_parent_element.data()->linkType() & Element::AllReport)) //special treatment for report
|
if (m_parent_element && ((m_parent_element.data()->linkType() & Element::AllReport) ||
|
||||||
|
m_parent_element.data()->linkType() == Element::ConductorDefinition)) //special treatment for report
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* May be in some case the old and new composite text both have the var %{label},
|
* May be in some case the old and new composite text both have the var %{label},
|
||||||
@@ -726,28 +729,29 @@ QVariant DynamicElementTextItem::itemChange(QGraphicsItem::GraphicsItemChange ch
|
|||||||
if(!m_parent_element.data()->linkedElements().isEmpty())
|
if(!m_parent_element.data()->linkedElements().isEmpty())
|
||||||
masterChanged();
|
masterChanged();
|
||||||
}
|
}
|
||||||
else if(m_parent_element.data()->linkType() & Element::AllReport)
|
else if((m_parent_element.data()->linkType() & Element::AllReport) ||
|
||||||
|
m_parent_element.data()->linkType() == Element::ConductorDefinition)
|
||||||
{
|
{
|
||||||
//Get the report formula, and add connection to keep up to date the formula.
|
//Get the report formula, and add connection to keep up to date the formula.
|
||||||
if (m_parent_element.data()->diagram() && m_parent_element.data()->diagram()->project())
|
if (m_parent_element.data()->diagram() && m_parent_element.data()->diagram()->project())
|
||||||
{
|
{
|
||||||
m_report_formula = m_parent_element.data()->diagram()->project()->defaultReportProperties();
|
m_report_formula = m_parent_element.data()->diagram()->project()->defaultReportProperties();
|
||||||
m_report_formula_con = connect(m_parent_element.data()->diagram()->project(), &QETProject::reportPropertiesChanged, this, &DynamicElementTextItem::reportFormulaChanged);
|
m_report_formula_con = connect(m_parent_element.data()->diagram()->project(), &QETProject::reportPropertiesChanged, this, &DynamicElementTextItem::reportFormulaChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Add connection to keep up to date the status of the element linked to the parent folio report of this text.
|
//Add connection to keep up to date the status of the element linked to the parent folio report of this text.
|
||||||
connect(m_parent_element.data(), &Element::linkedElementChanged, this, &DynamicElementTextItem::reportChanged);
|
connect(m_parent_element.data(), &Element::linkedElementChanged, this, &DynamicElementTextItem::reportChanged);
|
||||||
//The parent is already linked, we call reportChanged for init the connection
|
//The parent is already linked, we call reportChanged for init the connection
|
||||||
if(!m_parent_element.data()->linkedElements().isEmpty())
|
if(!m_parent_element.data()->linkedElements().isEmpty())
|
||||||
reportChanged();
|
reportChanged();
|
||||||
|
|
||||||
if(m_parent_element.data()->terminals().size())
|
if(m_parent_element.data()->terminals().size())
|
||||||
{
|
{
|
||||||
//Add connection to keep up date the conductors added or removed to the parent folio report element
|
//Add connection to keep up date the conductors added or removed to the parent folio report element
|
||||||
connect(m_parent_element.data()->terminals().first(), &Terminal::conductorWasAdded, this, &DynamicElementTextItem::conductorWasAdded);
|
connect(m_parent_element.data()->terminals().first(), &Terminal::conductorWasAdded, this, &DynamicElementTextItem::conductorWasAdded);
|
||||||
connect(m_parent_element.data()->terminals().first(), &Terminal::conductorWasRemoved, this, &DynamicElementTextItem::conductorWasRemoved);
|
connect(m_parent_element.data()->terminals().first(), &Terminal::conductorWasRemoved, this, &DynamicElementTextItem::conductorWasRemoved);
|
||||||
}
|
}
|
||||||
//Get a conductor in the potential
|
//Get a conductor in the potential
|
||||||
setPotentialConductor();
|
setPotentialConductor();
|
||||||
}
|
}
|
||||||
else if(m_parent_element.data()->linkType() == Element::Master)
|
else if(m_parent_element.data()->linkType() == Element::Master)
|
||||||
@@ -1027,7 +1031,8 @@ void DynamicElementTextItem::clearFormulaConnection()
|
|||||||
|
|
||||||
void DynamicElementTextItem::updateReportFormulaConnection()
|
void DynamicElementTextItem::updateReportFormulaConnection()
|
||||||
{
|
{
|
||||||
if(!(m_parent_element.data()->linkType() & Element::AllReport))
|
if(!(m_parent_element.data()->linkType() & Element::AllReport) &&
|
||||||
|
m_parent_element.data()->linkType() != Element::ConductorDefinition)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
removeConnectionForReportFormula(m_report_formula);
|
removeConnectionForReportFormula(m_report_formula);
|
||||||
@@ -1041,7 +1046,8 @@ void DynamicElementTextItem::updateReportFormulaConnection()
|
|||||||
*/
|
*/
|
||||||
void DynamicElementTextItem::updateReportText()
|
void DynamicElementTextItem::updateReportText()
|
||||||
{
|
{
|
||||||
if(!(m_parent_element.data()->linkType() & Element::AllReport))
|
if(!(m_parent_element.data()->linkType() & Element::AllReport) &&
|
||||||
|
m_parent_element.data()->linkType() != Element::ConductorDefinition)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (m_text_from == ElementInfo && m_info_name == "label")
|
if (m_text_from == ElementInfo && m_info_name == "label")
|
||||||
@@ -1098,7 +1104,10 @@ void DynamicElementTextItem::updateLabel()
|
|||||||
void DynamicElementTextItem::conductorWasAdded(Conductor *conductor)
|
void DynamicElementTextItem::conductorWasAdded(Conductor *conductor)
|
||||||
{
|
{
|
||||||
Q_UNUSED(conductor)
|
Q_UNUSED(conductor)
|
||||||
setPotentialConductor();
|
QTimer::singleShot(100, this, [this]() {
|
||||||
|
setPotentialConductor();
|
||||||
|
conductorPropertiesChanged();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1123,7 +1132,8 @@ void DynamicElementTextItem::conductorWasRemoved(Conductor *conductor)
|
|||||||
*/
|
*/
|
||||||
void DynamicElementTextItem::setPotentialConductor()
|
void DynamicElementTextItem::setPotentialConductor()
|
||||||
{
|
{
|
||||||
if(parentElement() && (parentElement()->linkType() & Element::AllReport))
|
if(parentElement() && ((parentElement()->linkType() & Element::AllReport) || (parentElement()->linkType() == Element::ConductorDefinition) ||
|
||||||
|
parentElement()->linkType() == Element::ConductorDefinition))
|
||||||
{
|
{
|
||||||
if(parentElement()->terminals().isEmpty())
|
if(parentElement()->terminals().isEmpty())
|
||||||
return;
|
return;
|
||||||
@@ -1156,6 +1166,7 @@ void DynamicElementTextItem::setPotentialConductor()
|
|||||||
connect(m_watched_conductor.data(), &Conductor::propertiesChange, this, &DynamicElementTextItem::conductorPropertiesChanged);
|
connect(m_watched_conductor.data(), &Conductor::propertiesChange, this, &DynamicElementTextItem::conductorPropertiesChanged);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
conductorPropertiesChanged();
|
||||||
}
|
}
|
||||||
else //This text haven't got a parent element, then ther isn't a conductor in the potential
|
else //This text haven't got a parent element, then ther isn't a conductor in the potential
|
||||||
{
|
{
|
||||||
@@ -1172,7 +1183,8 @@ void DynamicElementTextItem::setPotentialConductor()
|
|||||||
*/
|
*/
|
||||||
void DynamicElementTextItem::conductorPropertiesChanged()
|
void DynamicElementTextItem::conductorPropertiesChanged()
|
||||||
{
|
{
|
||||||
if(m_parent_element && (m_parent_element.data()->linkType() & Element::AllReport))
|
if(m_parent_element && ((m_parent_element.data()->linkType() & Element::AllReport) || (m_parent_element.data()->linkType() == Element::ConductorDefinition) ||
|
||||||
|
m_parent_element.data()->linkType() == Element::ConductorDefinition))
|
||||||
{
|
{
|
||||||
if(m_text_from == ElementInfo)
|
if(m_text_from == ElementInfo)
|
||||||
{
|
{
|
||||||
@@ -1200,8 +1212,9 @@ void DynamicElementTextItem::conductorPropertiesChanged()
|
|||||||
QString DynamicElementTextItem::reportReplacedCompositeText() const
|
QString DynamicElementTextItem::reportReplacedCompositeText() const
|
||||||
{
|
{
|
||||||
QString string;
|
QString string;
|
||||||
|
|
||||||
if(m_parent_element.data()->linkType() & Element::AllReport)
|
if((m_parent_element.data()->linkType() & Element::AllReport) || (m_parent_element.data()->linkType() == Element::ConductorDefinition) ||
|
||||||
|
m_parent_element.data()->linkType() == Element::ConductorDefinition)
|
||||||
{
|
{
|
||||||
string = m_composite_text;
|
string = m_composite_text;
|
||||||
|
|
||||||
|
|||||||
@@ -86,6 +86,9 @@ class DynamicElementTextItem : public DiagramTextItem
|
|||||||
void fromXml(const QDomElement &dom_elmt) override;
|
void fromXml(const QDomElement &dom_elmt) override;
|
||||||
|
|
||||||
Element *parentElement() const;
|
Element *parentElement() const;
|
||||||
|
/// PDF export: slave cross-reference text item ("(folio-pos)") and its master target.
|
||||||
|
QGraphicsTextItem *slaveXrefItem() const { return m_slave_Xref_item; }
|
||||||
|
Element *masterElement() const { return m_master_element.data(); }
|
||||||
ElementTextItemGroup *parentGroup() const;
|
ElementTextItemGroup *parentGroup() const;
|
||||||
Element *elementUseForInfo() const;
|
Element *elementUseForInfo() const;
|
||||||
void refreshLabelConnection();
|
void refreshLabelConnection();
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
#include "../qetxml.h"
|
#include "../qetxml.h"
|
||||||
#include "../qetversion.h"
|
#include "../qetversion.h"
|
||||||
#include "qgraphicsitemutility.h"
|
#include "qgraphicsitemutility.h"
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
#include <QDomElement>
|
#include <QDomElement>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@@ -431,6 +432,11 @@ bool Element::buildFromXml(const QDomElement &xml_def_elmt, int *state)
|
|||||||
m_data.fromXml(xml_def_elmt);
|
m_data.fromXml(xml_def_elmt);
|
||||||
setToolTip(name());
|
setToolTip(name());
|
||||||
|
|
||||||
|
QString my_type_str = xml_def_elmt.attribute(QStringLiteral("link_type"), QStringLiteral("simple"));
|
||||||
|
if (my_type_str == QLatin1String("conductor_definition")) {
|
||||||
|
m_link_type = Element::ConductorDefinition;
|
||||||
|
}
|
||||||
|
|
||||||
//load kind informations
|
//load kind informations
|
||||||
m_kind_informations.fromXml(
|
m_kind_informations.fromXml(
|
||||||
xml_def_elmt.firstChildElement(QStringLiteral("kindInformations")),
|
xml_def_elmt.firstChildElement(QStringLiteral("kindInformations")),
|
||||||
@@ -627,6 +633,9 @@ Terminal *Element::parseTerminal(const QDomElement &dom_element)
|
|||||||
Terminal *new_terminal = new Terminal(data, this);
|
Terminal *new_terminal = new Terminal(data, this);
|
||||||
m_terminals << new_terminal;
|
m_terminals << new_terminal;
|
||||||
|
|
||||||
|
connect(new_terminal, &Terminal::conductorWasAdded, this, &Element::updateConductorTexts);
|
||||||
|
connect(new_terminal, &Terminal::conductorWasRemoved, this, &Element::updateConductorTexts);
|
||||||
|
|
||||||
//Sort from top to bottom and left to right
|
//Sort from top to bottom and left to right
|
||||||
std::sort(m_terminals.begin(),
|
std::sort(m_terminals.begin(),
|
||||||
m_terminals.end(),
|
m_terminals.end(),
|
||||||
@@ -1288,6 +1297,8 @@ QString Element::linkTypeToString() const
|
|||||||
return QStringLiteral("Terminale");
|
return QStringLiteral("Terminale");
|
||||||
case Thumbnail:
|
case Thumbnail:
|
||||||
return QStringLiteral("Thumbnail");
|
return QStringLiteral("Thumbnail");
|
||||||
|
case ConductorDefinition:
|
||||||
|
return QStringLiteral("ConductorDefinition");
|
||||||
default:
|
default:
|
||||||
return QStringLiteral("Unknown");
|
return QStringLiteral("Unknown");
|
||||||
}
|
}
|
||||||
@@ -1555,3 +1566,25 @@ ElementsLocation Element::location() const
|
|||||||
{
|
{
|
||||||
return m_location;
|
return m_location;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Element::updateConductorTexts
|
||||||
|
*Slot that is triggered when a cable is *
|
||||||
|
*connected to or disconnected from a terminal on this component.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @brief Element::updateConductorTexts
|
||||||
|
*/
|
||||||
|
void Element::updateConductorTexts()
|
||||||
|
{
|
||||||
|
if (m_link_type != Element::ConductorDefinition) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (DynamicElementTextItem *deti : m_dynamic_text_list) {
|
||||||
|
if (deti) {
|
||||||
|
deti->setPotentialConductor();
|
||||||
|
deti->updateLabel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -52,14 +52,15 @@ class Element : public QetGraphicsItem
|
|||||||
(master, slave, report ect...)
|
(master, slave, report ect...)
|
||||||
*/
|
*/
|
||||||
enum kind {
|
enum kind {
|
||||||
Simple = 1,
|
Simple = 1,
|
||||||
NextReport = 2,
|
NextReport = 2,
|
||||||
PreviousReport = 4,
|
PreviousReport = 4,
|
||||||
AllReport = 6,
|
AllReport = 6,
|
||||||
Master = 8,
|
Master = 8,
|
||||||
Slave = 16,
|
Slave = 16,
|
||||||
Terminale = 32,
|
Terminale = 32,
|
||||||
Thumbnail = 64};
|
Thumbnail = 64,
|
||||||
|
ConductorDefinition = 128};
|
||||||
|
|
||||||
Element(const ElementsLocation &location,
|
Element(const ElementsLocation &location,
|
||||||
QGraphicsItem * = nullptr,
|
QGraphicsItem * = nullptr,
|
||||||
@@ -95,6 +96,8 @@ class Element : public QetGraphicsItem
|
|||||||
DynamicElementTextItem *text,
|
DynamicElementTextItem *text,
|
||||||
ElementTextItemGroup *group);
|
ElementTextItemGroup *group);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void updateConductorTexts();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
QList<Terminal *> terminals() const;
|
QList<Terminal *> terminals() const;
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ class ElementTextItemGroup : public QObject, public QGraphicsItemGroup
|
|||||||
QList<DynamicElementTextItem *> texts() const;
|
QList<DynamicElementTextItem *> texts() const;
|
||||||
Diagram *diagram() const;
|
Diagram *diagram() const;
|
||||||
Element *parentElement() const;
|
Element *parentElement() const;
|
||||||
|
/// PDF export: slave cross-reference text item of the group, if any.
|
||||||
|
QGraphicsTextItem *slaveXrefItem() const { return m_slave_Xref_item; }
|
||||||
|
|
||||||
QDomElement toXml(QDomDocument &dom_document) const;
|
QDomElement toXml(QDomDocument &dom_document) const;
|
||||||
void fromXml(QDomElement &dom_element);
|
void fromXml(QDomElement &dom_element);
|
||||||
|
|||||||
@@ -753,6 +753,15 @@ QString Terminal::name() const
|
|||||||
return d->m_name;
|
return d->m_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Terminal::terminalType
|
||||||
|
@return the type of this terminal (Generic, Inner, Outer, No, Nc, Common)
|
||||||
|
*/
|
||||||
|
TerminalData::Type Terminal::terminalType() const
|
||||||
|
{
|
||||||
|
return d->m_type;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief Conductor::relatedPotentialTerminal
|
@brief Conductor::relatedPotentialTerminal
|
||||||
Return terminal at the same potential from the same
|
Return terminal at the same potential from the same
|
||||||
@@ -779,6 +788,12 @@ QList<Terminal *> relatedPotentialTerminal (
|
|||||||
// If terminal parent element is a Terminal element.
|
// If terminal parent element is a Terminal element.
|
||||||
else if (terminal -> parentElement() -> linkType() & Element::Terminale)
|
else if (terminal -> parentElement() -> linkType() & Element::Terminale)
|
||||||
{
|
{
|
||||||
|
// English: Check if the user activated the potential isolation checkbox for this terminal
|
||||||
|
if (terminal->parentElement()->elementInformations().value(QStringLiteral("potential_isolating")).toString() == QLatin1String("true")) {
|
||||||
|
// English: Potential is isolated. Return an empty list so it does not propagate to the other side.
|
||||||
|
return QList<Terminal *>();
|
||||||
|
}
|
||||||
|
|
||||||
QList <Terminal *> terminals = terminal->parentElement()->terminals();
|
QList <Terminal *> terminals = terminal->parentElement()->terminals();
|
||||||
terminals.removeAll(const_cast<Terminal *>(terminal));
|
terminals.removeAll(const_cast<Terminal *>(terminal));
|
||||||
return terminals;
|
return terminals;
|
||||||
|
|||||||
@@ -18,13 +18,14 @@
|
|||||||
#ifndef TERMINAL_H
|
#ifndef TERMINAL_H
|
||||||
#define TERMINAL_H
|
#define TERMINAL_H
|
||||||
#include "../qet.h"
|
#include "../qet.h"
|
||||||
|
#include "../properties/terminaldata.h"
|
||||||
|
|
||||||
#include <QtWidgets>
|
#include <QtWidgets>
|
||||||
#include <QtXml>
|
#include <QtXml>
|
||||||
class Conductor;
|
class Conductor;
|
||||||
class Diagram;
|
class Diagram;
|
||||||
class Element;
|
class Element;
|
||||||
class TerminalData;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief The Terminal class
|
@brief The Terminal class
|
||||||
@@ -75,6 +76,7 @@ class Terminal : public QGraphicsObject
|
|||||||
Element *parentElement () const;
|
Element *parentElement () const;
|
||||||
QUuid uuid () const;
|
QUuid uuid () const;
|
||||||
QString name () const;
|
QString name () const;
|
||||||
|
TerminalData::Type terminalType() const;
|
||||||
|
|
||||||
QList<Conductor *> conductors() const;
|
QList<Conductor *> conductors() const;
|
||||||
Qet::Orientation orientation() const;
|
Qet::Orientation orientation() const;
|
||||||
|
|||||||
@@ -189,7 +189,8 @@ QStringList QETInformation::elementInfoKeys()
|
|||||||
ELMT_MACHINE_MANUFACTURER_REF_AUX4,
|
ELMT_MACHINE_MANUFACTURER_REF_AUX4,
|
||||||
ELMT_SUPPLIER_AUX4,
|
ELMT_SUPPLIER_AUX4,
|
||||||
ELMT_QUANTITY_AUX4,
|
ELMT_QUANTITY_AUX4,
|
||||||
ELMT_UNITY_AUX4, };
|
ELMT_UNITY_AUX4,
|
||||||
|
"exclude_from_bom" };
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include "elementinfowidget.h"
|
#include "elementinfowidget.h"
|
||||||
|
#include <QCheckBox>
|
||||||
#include "../diagram.h"
|
#include "../diagram.h"
|
||||||
#include "../qetapp.h"
|
#include "../qetapp.h"
|
||||||
#include "../qetgraphicsitem/element.h"
|
#include "../qetgraphicsitem/element.h"
|
||||||
@@ -161,6 +161,13 @@ void ElementInfoWidget::enableLiveEdit()
|
|||||||
for (ElementInfoPartWidget *eipw : m_eipw_list)
|
for (ElementInfoPartWidget *eipw : m_eipw_list)
|
||||||
connect(eipw, &ElementInfoPartWidget::textChanged, this, &ElementInfoWidget::apply);
|
connect(eipw, &ElementInfoPartWidget::textChanged, this, &ElementInfoWidget::apply);
|
||||||
connect(ui->m_auto_num_locked_cb, &QCheckBox::clicked, this, &ElementInfoWidget::apply);
|
connect(ui->m_auto_num_locked_cb, &QCheckBox::clicked, this, &ElementInfoWidget::apply);
|
||||||
|
|
||||||
|
if (m_potential_isolating_cb) {
|
||||||
|
connect(m_potential_isolating_cb, &QCheckBox::clicked, this, &ElementInfoWidget::apply);
|
||||||
|
}
|
||||||
|
if (m_exclude_from_bom_cb) {
|
||||||
|
connect(m_exclude_from_bom_cb, &QCheckBox::clicked, this, &ElementInfoWidget::apply);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -172,6 +179,13 @@ void ElementInfoWidget::disableLiveEdit()
|
|||||||
for (ElementInfoPartWidget *eipw : m_eipw_list)
|
for (ElementInfoPartWidget *eipw : m_eipw_list)
|
||||||
disconnect(eipw, &ElementInfoPartWidget::textChanged, this, &ElementInfoWidget::apply);
|
disconnect(eipw, &ElementInfoPartWidget::textChanged, this, &ElementInfoWidget::apply);
|
||||||
disconnect(ui->m_auto_num_locked_cb, &QCheckBox::clicked, this, &ElementInfoWidget::apply);
|
disconnect(ui->m_auto_num_locked_cb, &QCheckBox::clicked, this, &ElementInfoWidget::apply);
|
||||||
|
|
||||||
|
if (m_potential_isolating_cb) {
|
||||||
|
disconnect(m_potential_isolating_cb, &QCheckBox::clicked, this, &ElementInfoWidget::apply);
|
||||||
|
}
|
||||||
|
if (m_exclude_from_bom_cb) {
|
||||||
|
disconnect(m_exclude_from_bom_cb, &QCheckBox::clicked, this, &ElementInfoWidget::apply);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -193,16 +207,34 @@ void ElementInfoWidget::buildInterface()
|
|||||||
ui->scroll_vlayout->addWidget(eipw);
|
ui->scroll_vlayout->addWidget(eipw);
|
||||||
m_eipw_list << eipw;
|
m_eipw_list << eipw;
|
||||||
}
|
}
|
||||||
|
|
||||||
ui->scroll_vlayout->addStretch();
|
ui->scroll_vlayout->addStretch();
|
||||||
|
|
||||||
|
// Existing potential isolating checkbox
|
||||||
|
m_potential_isolating_cb = new QCheckBox(tr("Séparation de potentiel"), this);
|
||||||
|
m_potential_isolating_cb->setStyleSheet(QStringLiteral("margin: 5px; font-weight: bold;"));
|
||||||
|
|
||||||
|
// English: Initialize and style the BOM exclusion checkbox
|
||||||
|
m_exclude_from_bom_cb = new QCheckBox(tr("Exclure de la nomenclature"), this);
|
||||||
|
m_exclude_from_bom_cb->setStyleSheet(QStringLiteral("margin: 5px; font-weight: bold;"));
|
||||||
|
|
||||||
|
if (QVBoxLayout *mainLayout = qobject_cast<QVBoxLayout*>(this->layout())) {
|
||||||
|
mainLayout->insertWidget(1, m_potential_isolating_cb);
|
||||||
|
// English: Insert the new checkbox into the main vertical layout
|
||||||
|
mainLayout->insertWidget(2, m_exclude_from_bom_cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
// English: BOM exclusion applies to all elements, so it's always visible
|
||||||
|
m_exclude_from_bom_cb->setVisible(true);
|
||||||
|
|
||||||
// Show checkbox only if the element is a terminal
|
// Show checkbox only if the element is a terminal
|
||||||
if (m_element.data()->elementData().m_type == ElementData::Terminal) {
|
if (m_element.data()->elementData().m_type == ElementData::Terminal) {
|
||||||
ui->m_auto_num_locked_cb->setVisible(true);
|
ui->m_auto_num_locked_cb->setVisible(true);
|
||||||
|
m_potential_isolating_cb->setVisible(true);
|
||||||
} else {
|
} else {
|
||||||
ui->m_auto_num_locked_cb->setVisible(false);
|
ui->m_auto_num_locked_cb->setVisible(false);
|
||||||
|
m_potential_isolating_cb->setVisible(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief ElementInfoWidget::infoPartWidgetForKey
|
@brief ElementInfoWidget::infoPartWidgetForKey
|
||||||
@param key
|
@param key
|
||||||
@@ -241,8 +273,13 @@ void ElementInfoWidget::updateUi()
|
|||||||
}
|
}
|
||||||
// Load the lock status for auto numbering
|
// Load the lock status for auto numbering
|
||||||
if (m_element->elementData().m_type == ElementData::Terminal) {
|
if (m_element->elementData().m_type == ElementData::Terminal) {
|
||||||
QString lock_value = element_info.value(QStringLiteral("auto_num_locked")).toString();
|
// ... (bestehender Terminal-Code für auto_num_locked und potential_isolating) ...
|
||||||
ui->m_auto_num_locked_cb->setChecked(lock_value == QLatin1String("true"));
|
}
|
||||||
|
|
||||||
|
// English: Load the BOM exclusion status from the element information mapping
|
||||||
|
if (m_exclude_from_bom_cb) {
|
||||||
|
QString exclude_bom_value = element_info.value(QStringLiteral("exclude_from_bom")).toString();
|
||||||
|
m_exclude_from_bom_cb->setChecked(exclude_bom_value == QLatin1String("true"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_live_edit) {
|
if (m_live_edit) {
|
||||||
@@ -274,11 +311,14 @@ DiagramContext ElementInfoWidget::currentInfo() const
|
|||||||
|
|
||||||
// Save the auto numbering lock status
|
// Save the auto numbering lock status
|
||||||
if (m_element->elementData().m_type == ElementData::Terminal) {
|
if (m_element->elementData().m_type == ElementData::Terminal) {
|
||||||
info_.addValue(QStringLiteral("auto_num_locked"), ui->m_auto_num_locked_cb->isChecked() ? QStringLiteral("true") : QStringLiteral("false"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_exclude_from_bom_cb) {
|
||||||
|
info_.addValue(QStringLiteral("exclude_from_bom"), m_exclude_from_bom_cb->isChecked() ? QStringLiteral("true") : QStringLiteral("false"));
|
||||||
|
}
|
||||||
|
|
||||||
return info_;
|
return info_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief ElementInfoWidget::firstActivated
|
@brief ElementInfoWidget::firstActivated
|
||||||
Slot activated when this widget is show.
|
Slot activated when this widget is show.
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ class Element;
|
|||||||
class QUndoCommand;
|
class QUndoCommand;
|
||||||
class ElementInfoPartWidget;
|
class ElementInfoPartWidget;
|
||||||
class ChangeElementInformationCommand;
|
class ChangeElementInformationCommand;
|
||||||
|
class QCheckBox;
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class ElementInfoWidget;
|
class ElementInfoWidget;
|
||||||
@@ -71,6 +72,8 @@ class ElementInfoWidget : public AbstractElementPropertiesEditorWidget
|
|||||||
private:
|
private:
|
||||||
Ui::ElementInfoWidget *ui;
|
Ui::ElementInfoWidget *ui;
|
||||||
QList <ElementInfoPartWidget *> m_eipw_list;
|
QList <ElementInfoPartWidget *> m_eipw_list;
|
||||||
|
QCheckBox *m_potential_isolating_cb = nullptr;
|
||||||
|
QCheckBox *m_exclude_from_bom_cb = nullptr;
|
||||||
bool m_first_activation;
|
bool m_first_activation;
|
||||||
bool m_ui_builded = false;
|
bool m_ui_builded = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -308,6 +308,9 @@ void ElementPropertiesWidget::updateUi()
|
|||||||
case Element::Terminale:
|
case Element::Terminale:
|
||||||
m_list_editor << new ElementInfoWidget(m_element, this);
|
m_list_editor << new ElementInfoWidget(m_element, this);
|
||||||
break;
|
break;
|
||||||
|
case Element::ConductorDefinition:
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ void XRefPropertiesWidget::saveProperties(int index) {
|
|||||||
else if(ui->m_xrefpos_cb->itemData(ui->m_xrefpos_cb->currentIndex()).toString() == "right") xrp.setXrefPos(Qt::AlignRight);
|
else if(ui->m_xrefpos_cb->itemData(ui->m_xrefpos_cb->currentIndex()).toString() == "right") xrp.setXrefPos(Qt::AlignRight);
|
||||||
else if(ui->m_xrefpos_cb->itemData(ui->m_xrefpos_cb->currentIndex()).toString() == "alignment") xrp.setXrefPos(Qt::AlignBaseline);
|
else if(ui->m_xrefpos_cb->itemData(ui->m_xrefpos_cb->currentIndex()).toString() == "alignment") xrp.setXrefPos(Qt::AlignBaseline);
|
||||||
xrp.setShowPowerContac(ui->m_show_power_cb->isChecked());
|
xrp.setShowPowerContac(ui->m_show_power_cb->isChecked());
|
||||||
|
xrp.setShowTerminalName(ui->m_show_terminal_name_cb->isChecked());
|
||||||
xrp.setPrefix("power", ui->m_power_prefix_le->text());
|
xrp.setPrefix("power", ui->m_power_prefix_le->text());
|
||||||
xrp.setPrefix("delay", ui->m_delay_prefix_le->text());
|
xrp.setPrefix("delay", ui->m_delay_prefix_le->text());
|
||||||
xrp.setPrefix("switch", ui->m_switch_prefix_le->text());
|
xrp.setPrefix("switch", ui->m_switch_prefix_le->text());
|
||||||
@@ -190,6 +191,7 @@ void XRefPropertiesWidget::updateDisplay()
|
|||||||
else if(xrp.getXrefPos() == Qt::AlignBaseline) ui->m_xrefpos_cb->setCurrentIndex(ui->m_xrefpos_cb->findData("alignment"));
|
else if(xrp.getXrefPos() == Qt::AlignBaseline) ui->m_xrefpos_cb->setCurrentIndex(ui->m_xrefpos_cb->findData("alignment"));
|
||||||
else if(xrp.getXrefPos() == Qt::AlignBottom) ui->m_xrefpos_cb->setCurrentIndex(ui->m_xrefpos_cb->findData("bottom"));
|
else if(xrp.getXrefPos() == Qt::AlignBottom) ui->m_xrefpos_cb->setCurrentIndex(ui->m_xrefpos_cb->findData("bottom"));
|
||||||
ui->m_show_power_cb->setChecked(xrp.showPowerContact());
|
ui->m_show_power_cb->setChecked(xrp.showPowerContact());
|
||||||
|
ui->m_show_terminal_name_cb->setChecked(xrp.showTerminalName());
|
||||||
ui->m_power_prefix_le-> setText(xrp.prefix("power"));
|
ui->m_power_prefix_le-> setText(xrp.prefix("power"));
|
||||||
ui->m_delay_prefix_le-> setText(xrp.prefix("delay"));
|
ui->m_delay_prefix_le-> setText(xrp.prefix("delay"));
|
||||||
ui->m_switch_prefix_le->setText(xrp.prefix("switch"));
|
ui->m_switch_prefix_le->setText(xrp.prefix("switch"));
|
||||||
|
|||||||
@@ -108,6 +108,13 @@
|
|||||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
<item>
|
<item>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="m_show_terminal_name_cb">
|
||||||
|
<property name="text">
|
||||||
|
<string>Afficher les numéros de bornes dans les Xrefs</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QRadioButton" name="m_display_has_contacts_rb">
|
<widget class="QRadioButton" name="m_display_has_contacts_rb">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@@ -283,6 +290,7 @@
|
|||||||
<tabstop>m_master_le</tabstop>
|
<tabstop>m_master_le</tabstop>
|
||||||
<tabstop>m_slave_le</tabstop>
|
<tabstop>m_slave_le</tabstop>
|
||||||
<tabstop>m_show_power_cb</tabstop>
|
<tabstop>m_show_power_cb</tabstop>
|
||||||
|
<tabstop>m_show_terminal_name_cb</tabstop>
|
||||||
<tabstop>m_power_prefix_le</tabstop>
|
<tabstop>m_power_prefix_le</tabstop>
|
||||||
<tabstop>m_delay_prefix_le</tabstop>
|
<tabstop>m_delay_prefix_le</tabstop>
|
||||||
<tabstop>m_switch_prefix_le</tabstop>
|
<tabstop>m_switch_prefix_le</tabstop>
|
||||||
|
|||||||
@@ -158,6 +158,45 @@ void WiringListExport::toCsv()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QSet<QString> conductorDefinitionTypes;
|
||||||
|
QDomElement rootElem = doc.documentElement();
|
||||||
|
QDomElement collection = rootElem.firstChildElement("collection");
|
||||||
|
if (!collection.isNull()) {
|
||||||
|
QDomNodeList defs = collection.elementsByTagName("definition");
|
||||||
|
for (int i = 0; i < defs.size(); ++i) {
|
||||||
|
QDomElement def = defs.at(i).toElement();
|
||||||
|
if (def.attribute("link_type") == "conductor_definition") {
|
||||||
|
QDomElement parentEl = def.parentNode().toElement();
|
||||||
|
if (parentEl.tagName().toLower() == "element") {
|
||||||
|
QString name = parentEl.attribute("name");
|
||||||
|
if (!name.isEmpty()) {
|
||||||
|
conductorDefinitionTypes.insert(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QSet<QString> conductorDefinitionUuids;
|
||||||
|
QDomNodeList projectElements = rootElem.elementsByTagName("element");
|
||||||
|
for (int i = 0; i < projectElements.size(); ++i) {
|
||||||
|
QDomElement el = projectElements.at(i).toElement();
|
||||||
|
QString typeVal = el.attribute("type");
|
||||||
|
bool isCondDef = false;
|
||||||
|
for (const QString &cType : conductorDefinitionTypes) {
|
||||||
|
if (typeVal.endsWith(cType)) {
|
||||||
|
isCondDef = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isCondDef) {
|
||||||
|
QString uuid = normalizeUuid(el.attribute("uuid", el.attribute("id", "")));
|
||||||
|
if (!uuid.isEmpty()) {
|
||||||
|
conductorDefinitionUuids.insert(uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QFileDialog dialog(m_parent);
|
QFileDialog dialog(m_parent);
|
||||||
dialog.setAcceptMode(QFileDialog::AcceptSave);
|
dialog.setAcceptMode(QFileDialog::AcceptSave);
|
||||||
dialog.setWindowTitle(tr("Exporter le plan de câblage"));
|
dialog.setWindowTitle(tr("Exporter le plan de câblage"));
|
||||||
@@ -200,6 +239,10 @@ void WiringListExport::toCsv()
|
|||||||
for (int i = 0; i < conductors.size(); ++i) {
|
for (int i = 0; i < conductors.size(); ++i) {
|
||||||
ConductorData c = conductors[i];
|
ConductorData c = conductors[i];
|
||||||
|
|
||||||
|
if (conductorDefinitionUuids.contains(c.el1_uuid) || conductorDefinitionUuids.contains(c.el2_uuid)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (c.element1_label.isEmpty() && elementsInfo.contains(c.el1_uuid)) {
|
if (c.element1_label.isEmpty() && elementsInfo.contains(c.el1_uuid)) {
|
||||||
c.element1_label = elementsInfo[c.el1_uuid].label;
|
c.element1_label = elementsInfo[c.el1_uuid].label;
|
||||||
if (c.element1_label.isEmpty()) c.element1_label = elementsInfo[c.el1_uuid].name;
|
if (c.element1_label.isEmpty()) c.element1_label = elementsInfo[c.el1_uuid].name;
|
||||||
|
|||||||
Reference in New Issue
Block a user