From 72c800821829bba467746e0347c830b35f0fa35f Mon Sep 17 00:00:00 2001 From: Levi Jetzer Date: Thu, 6 Aug 2026 17:20:48 +0200 Subject: [PATCH 01/20] Avoid needless dock churn in QETElementEditor::updateInformations() updateInformations() runs on every selection change and on every undo stack index change. It unconditionally called clearToolsDock(), which removes and hides the current editor widget, and then re-inserted the same widget into the stack. When the editor for the new selection is the one already shown, that means removing, hiding, reparenting and re-adding a widget only to end up in the same state. Look up the editor first and only clear and re-insert the tools dock when a different editor is needed. setPart()/setParts() still updates the contents in every case, so the visible result is unchanged. --- sources/editor/ui/qetelementeditor.cpp | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/sources/editor/ui/qetelementeditor.cpp b/sources/editor/ui/qetelementeditor.cpp index 9a4eb2ffb..691b370fd 100644 --- a/sources/editor/ui/qetelementeditor.cpp +++ b/sources/editor/ui/qetelementeditor.cpp @@ -573,9 +573,11 @@ void QETElementEditor::updateInformations() || selection_xml_name == "ellipse" || selection_xml_name == "arc") { - clearToolsDock(); //We add the editor widget ElementItemEditor *editor = static_cast(m_editors[selection_xml_name]); + if (m_tools_dock_stack -> widget(1) != editor) { + clearToolsDock(); + } #if TODO_LIST #pragma message("@TODO Check if it takes longer than setting the parts again to the editor.") @@ -608,7 +610,9 @@ void QETElementEditor::updateInformations() success = editor -> setParts(cep_list); } if (success) { - m_tools_dock_stack -> insertWidget(1, editor); + if (m_tools_dock_stack -> widget(1) != editor) { + m_tools_dock_stack -> insertWidget(1, editor); + } m_tools_dock_stack -> setCurrentIndex(1); } else { @@ -626,8 +630,10 @@ void QETElementEditor::updateInformations() // multi edit for polygons makes no sense // TODO: maybe allowing multipart edit when number of points is the same? //We add the editor widget - clearToolsDock(); ElementItemEditor *editor = static_cast(m_editors[selection_xml_name]); + if (m_tools_dock_stack -> widget(1) != editor) { + clearToolsDock(); + } CustomElementPart* part = editor -> currentPart(); bool equal = part == cep_list.first(); @@ -637,7 +643,9 @@ void QETElementEditor::updateInformations() success = editor -> setPart(cep_list.first()); } if (success) { - m_tools_dock_stack -> insertWidget(1, editor); + if (m_tools_dock_stack -> widget(1) != editor) { + m_tools_dock_stack -> insertWidget(1, editor); + } m_tools_dock_stack -> setCurrentIndex(1); } else { @@ -655,11 +663,15 @@ void QETElementEditor::updateInformations() //There's several parts selecteds and all can be edited by style editor. if (style_editable) { - clearToolsDock(); ElementItemEditor *selection_editor = m_editors["style"]; + if (m_tools_dock_stack -> widget(1) != selection_editor) { + clearToolsDock(); + } if (selection_editor) { if (selection_editor -> setParts(cep_list)) { - m_tools_dock_stack -> insertWidget(1, selection_editor); + if (m_tools_dock_stack -> widget(1) != selection_editor) { + m_tools_dock_stack -> insertWidget(1, selection_editor); + } m_tools_dock_stack -> setCurrentIndex(1); } else { From a106395679a5d8e4e68e5e21c57f91cd9b684f9b Mon Sep 17 00:00:00 2001 From: ispyisail Date: Tue, 15 Sep 2026 07:38:24 +1200 Subject: [PATCH 02/20] Add Linux CI: build, unit tests, and the IPC regression gate Nothing currently builds QElectroTech on Linux in CI, and nothing runs tests/ at all -- the existing workflows build Windows and generate documentation. Adds one job: configure and build with Qt 6 Debug, run ctest, then run the IPC open-forwarding regression test. Verified by running the job's exact step sequence in a clean ubuntu:26.04 container, in both directions: with the #868 fix 5/5 unit tests pass, gate passes, job green with the fix reverted 5/5 unit tests pass, gate fails, job red The unit tests passing in both arms is the point: the existing suite cannot see this class of bug, which is what the gate is for. Three choices that are not cosmetic: - Runs in an ubuntu:26.04 container rather than on the runner. ubuntu-latest ships Qt 6.4; the gate needs 6.10.2, and whether the crash reproduces on 6.4 has never been checked. A job that cannot go red is worse than none. - Debug, not Release. The pre-fix commit survives every attempt built -O3 -DNDEBUG, so a Release job would never catch a regression here. - An inconclusive gate run warns rather than fails. It means the crash path was not exercised, which proves nothing and is not the same as a regression; failing on it would make the job flaky rather than useful. extra-cmake-modules and the KF6 libraries are installed rather than left to FetchContent, which otherwise builds ECM from source and fails the configure demanding Qt6 documentation tools. Depends on the regression test added in #871. Co-Authored-By: Claude Opus 5 --- .github/workflows/linux-build.yml | 78 +++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 .github/workflows/linux-build.yml diff --git a/.github/workflows/linux-build.yml b/.github/workflows/linux-build.yml new file mode 100644 index 000000000..ff86b31f2 --- /dev/null +++ b/.github/workflows/linux-build.yml @@ -0,0 +1,78 @@ +# Linux build, unit tests, and the IPC open-forwarding regression gate. +# +# The existing workflows build Windows and generate documentation; nothing +# builds QElectroTech on Linux or runs tests/ in CI. This does both. +# +# It runs inside an ubuntu:26.04 container rather than directly on the runner +# because the IPC gate below needs Qt 6.10.2. The ubuntu-latest image ships +# Qt 6.4, and whether the crash that gate covers reproduces on 6.4 has never +# been checked -- a gate that cannot go red is worse than no gate. +name: Linux build and tests + +on: + push: + branches: [master] + pull_request: + workflow_dispatch: + +jobs: + linux: + name: Build and test (Qt 6, Debug) + runs-on: ubuntu-latest + container: ubuntu:26.04 + + steps: + - name: Install build and test dependencies + run: | + apt-get update + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ + ca-certificates build-essential cmake ninja-build git pkg-config \ + qt6-base-dev qt6-base-private-dev qt6-tools-dev qt6-tools-dev-tools \ + libqt6svg6-dev libqt6sql6-sqlite libsqlite3-dev libcups2-dev \ + libxkbcommon-x11-0 \ + xvfb openbox xdotool x11-utils + # extra-cmake-modules and the KF6 libraries are installed rather than + # left to FetchContent: without them CMake builds extra-cmake-modules + # from source, and its Qt Help documentation target then demands + # Qt6{Core,Gui,Widgets,DBus}Tools, failing the configure with an error + # that appears to be about QElectroTech and is not. + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ + extra-cmake-modules libkf6coreaddons-dev libkf6widgetsaddons-dev + + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Configure + # Debug, not Release. Whether a use-after-free faults depends on what + # the allocator does with the freed block: the commit before #868 + # crashes every time built Debug and survives every time built + # -O3 -DNDEBUG, so a Release job would never catch a regression here. + run: | + git config --global --add safe.directory "$GITHUB_WORKSPACE" + cmake -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Debug \ + -DQT_VERSION_MAJOR=6 + + - name: Build + run: cmake --build build --parallel $(nproc) + + - name: Unit tests + run: xvfb-run -a ctest --test-dir build --output-on-failure + + - name: IPC open-forwarding regression (#868) + # Fails the job only on an actual crash. An inconclusive run means the + # crash path was not exercised -- it proves nothing, which is not the + # same as a regression, and failing on it would make this job flaky + # rather than informative. + run: | + set +e + tests/ipc-regression/run.sh --binary build/qelectrotech + status=$? + set -e + case $status in + 0) echo "::notice::IPC gate passed" ;; + 2) echo "::warning::IPC gate inconclusive -- the crash path was not exercised, nothing was proven" ;; + *) echo "::error::IPC gate failed -- forwarded files are being opened inside the socket handler again" + exit 1 ;; + esac From c2f405c32ddc29e04b01bb61d031e20019998acd Mon Sep 17 00:00:00 2001 From: Kellermorph Date: Tue, 15 Sep 2026 21:48:47 +0200 Subject: [PATCH 03/20] Add Hide full masters checkbox to slave linking widget --- sources/ui/linksingleelementwidget.cpp | 69 ++++++++++++++++++++++++-- sources/ui/linksingleelementwidget.h | 6 +++ 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/sources/ui/linksingleelementwidget.cpp b/sources/ui/linksingleelementwidget.cpp index e2c3c8b38..60b843478 100644 --- a/sources/ui/linksingleelementwidget.cpp +++ b/sources/ui/linksingleelementwidget.cpp @@ -25,10 +25,13 @@ #include "../undocommand/linkelementcommand.h" #include "../qetinformation.h" #include "../qetproject.h" +#include "../qetgraphicsitem/masterelement.h" #include "../ui_linksingleelementwidget.h" #include #include +#include +#include /** @@ -52,6 +55,32 @@ LinkSingleElementWidget::LinkSingleElementWidget(Element *elmt, m_show_qtwi = new QAction(tr("Montrer l'élément"), this); m_show_element = new QAction(tr("Montrer l'élément esclave"), this); m_save_header_state = new QAction(tr("Enregistrer la disposition"), this); + + // Add "hide full masters" checkbox for slave elements only + if (elmt && elmt->elementData().m_type == ElementData::Slave) { + m_hide_full_masters_cb = new QCheckBox( + tr("Masquer les éléments maîtres pleins"), this); + ui->gridLayout->addWidget(m_hide_full_masters_cb, 2, 0, 1, 3); + + // Shift existing widgets down: search -> row 3, tree -> row 4 + ui->gridLayout->removeWidget(ui->m_search_field); + ui->gridLayout->addWidget(ui->m_search_field, 3, 0, 1, 3); + ui->gridLayout->removeWidget(ui->m_tree_widget); + ui->gridLayout->addWidget(ui->m_tree_widget, 4, 0, 1, 3); + + // Remove the hidden masters label (no longer needed) + ui->m_hidden_masters_label->hide(); + ui->gridLayout->removeWidget(ui->m_hidden_masters_label); + delete ui->m_hidden_masters_label; + ui->m_hidden_masters_label = nullptr; + + connect(m_hide_full_masters_cb, &QCheckBox::toggled, + this, &LinkSingleElementWidget::on_m_hide_full_masters_cb_toggled); + + QSettings settings; + m_hide_full_masters_cb->setChecked( + settings.value(QStringLiteral("link-element-widget/hideFullMasters"), false).toBool()); + } connect(m_show_qtwi, &QAction::triggered, this, [=]() { @@ -249,6 +278,7 @@ void LinkSingleElementWidget::buildTree() if (m_element->elementData().m_type == ElementData::Slave) { + m_full_masters.clear(); for(const auto &elmt : elmt_vector) { @@ -288,6 +318,16 @@ void LinkSingleElementWidget::buildTree() QTreeWidgetItem *qtwi = new QTreeWidgetItem(ui->m_tree_widget, str_list); m_qtwi_elmt_hash.insert(qtwi, elmt); m_qtwi_strl_hash.insert(qtwi, search_list); + + // Check if this master is full + if (elmt->linkType() == Element::Master) { + MasterElement *me = qobject_cast(elmt); + if (me && me->isFull()) { + m_full_masters.insert(qtwi); + if (m_hide_full_masters_cb && m_hide_full_masters_cb->isChecked()) + qtwi->setHidden(true); + } + } } @@ -787,7 +827,12 @@ void LinkSingleElementWidget::on_m_search_field_textEdited(const QString &arg1) { //Show all items if arg1 is empty, if not hide all items foreach(QTreeWidgetItem *qtwi, m_qtwi_elmt_hash.keys()) - qtwi->setHidden(!arg1.isEmpty()); + { + bool full_hidden = m_hide_full_masters_cb + && m_hide_full_masters_cb->isChecked() + && m_full_masters.contains(qtwi); + qtwi->setHidden(!arg1.isEmpty() || full_hidden); + } QList qtwi_list; @@ -803,7 +848,25 @@ void LinkSingleElementWidget::on_m_search_field_textEdited(const QString &arg1) } } - //Show items which match with arg1 + //Show items which match with arg1 (but not full masters if checkbox is on) foreach(QTreeWidgetItem *qtwi, qtwi_list) - qtwi->setHidden(false); + { + bool full_hidden = m_hide_full_masters_cb + && m_hide_full_masters_cb->isChecked() + && m_full_masters.contains(qtwi); + if (!full_hidden) + qtwi->setHidden(false); + } +} + +bool LinkSingleElementWidget::isMasterFull(QTreeWidgetItem *item) const +{ + return m_full_masters.contains(item); +} + +void LinkSingleElementWidget::on_m_hide_full_masters_cb_toggled(bool checked) +{ + QSettings settings; + settings.setValue(QStringLiteral("link-element-widget/hideFullMasters"), checked); + buildTree(); } diff --git a/sources/ui/linksingleelementwidget.h b/sources/ui/linksingleelementwidget.h index d477d107b..9cb35c858 100644 --- a/sources/ui/linksingleelementwidget.h +++ b/sources/ui/linksingleelementwidget.h @@ -26,6 +26,7 @@ #include class QTreeWidgetItem; +class QCheckBox; class Element; class QMenu; @@ -71,6 +72,7 @@ class LinkSingleElementWidget : public AbstractElementPropertiesEditorWidget void setUpCompleter(); void clearTreeWidget(); void setUpHeaderLabels(); + bool isMasterFull(QTreeWidgetItem *item) const; private slots: void diagramWasRemovedFromProject(); @@ -87,6 +89,7 @@ class LinkSingleElementWidget : public AbstractElementPropertiesEditorWidget void on_m_show_this_pb_clicked(); void on_m_search_field_textEdited(const QString &arg1); + void on_m_hide_full_masters_cb_toggled(bool checked); private: Ui::LinkSingleElementWidget *ui; @@ -103,6 +106,9 @@ class LinkSingleElementWidget : public AbstractElementPropertiesEditorWidget Element *m_showed_element = nullptr, *m_element_to_link = nullptr; + QCheckBox *m_hide_full_masters_cb{nullptr}; + QSet m_full_masters; + int m_pending_group_index = -1; QMenu *m_context_menu{nullptr}; From 7a85e2592c7f3573716c999fcf0ce7de472cee39 Mon Sep 17 00:00:00 2001 From: ispyisail Date: Wed, 16 Sep 2026 21:27:22 +1200 Subject: [PATCH 04/20] Fix stale geometry in LineEditor on multi-select LineEditor::setPart() no-ops (skipping updateForm()) when the part passed in is already m_part. That is harmless when the editor widget is torn down between selections, but this branch keeps the same editor instance installed across selection changes instead of recreating it, so a line already shown alone can also be parts.first() of a later multi-selection -- and the x1/y1/x2/y2 spinboxes then keep showing whatever was in them before, not this selection's actual first line. setParts() now always calls updateForm() after setPart() succeeds, closing the gap regardless of the identity check inside setPart(). Co-Authored-By: Claude Sonnet 5 --- sources/editor/ui/lineeditor.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sources/editor/ui/lineeditor.cpp b/sources/editor/ui/lineeditor.cpp index b75dfcf39..18ec7d0be 100644 --- a/sources/editor/ui/lineeditor.cpp +++ b/sources/editor/ui/lineeditor.cpp @@ -156,6 +156,16 @@ bool LineEditor::setParts(QList parts) return false; } + // setPart() no-ops when parts.first() is already m_part (its own + // identity guard), which skips updateForm(). That is fine when this + // editor widget stayed on screen the whole time, but since PR #675 + // keeps the same editor instance installed across selection changes + // instead of tearing it down, an already-shown single line can also be + // parts.first() of a later multi-selection -- and without this call + // the geometry spinboxes keep showing whatever was in them before, + // not this selection's actual first line. + updateForm(); + return m_style->setParts(parts); } From e6ffc8b622c31601cce02be35a551525488a3e9d Mon Sep 17 00:00:00 2001 From: Kellermorph Date: Wed, 16 Sep 2026 11:37:27 +0200 Subject: [PATCH 05/20] Address all review feedback --- sources/ui/linksingleelementwidget.cpp | 105 ++++++++++--------------- sources/ui/linksingleelementwidget.h | 5 +- sources/ui/linksingleelementwidget.ui | 20 ++--- 3 files changed, 50 insertions(+), 80 deletions(-) diff --git a/sources/ui/linksingleelementwidget.cpp b/sources/ui/linksingleelementwidget.cpp index 60b843478..322e955bf 100644 --- a/sources/ui/linksingleelementwidget.cpp +++ b/sources/ui/linksingleelementwidget.cpp @@ -32,6 +32,7 @@ #include #include #include +#include /** @@ -56,31 +57,21 @@ LinkSingleElementWidget::LinkSingleElementWidget(Element *elmt, m_show_element = new QAction(tr("Montrer l'élément esclave"), this); m_save_header_state = new QAction(tr("Enregistrer la disposition"), this); - // Add "hide full masters" checkbox for slave elements only - if (elmt && elmt->elementData().m_type == ElementData::Slave) { - m_hide_full_masters_cb = new QCheckBox( - tr("Masquer les éléments maîtres pleins"), this); - ui->gridLayout->addWidget(m_hide_full_masters_cb, 2, 0, 1, 3); - - // Shift existing widgets down: search -> row 3, tree -> row 4 - ui->gridLayout->removeWidget(ui->m_search_field); - ui->gridLayout->addWidget(ui->m_search_field, 3, 0, 1, 3); - ui->gridLayout->removeWidget(ui->m_tree_widget); - ui->gridLayout->addWidget(ui->m_tree_widget, 4, 0, 1, 3); - - // Remove the hidden masters label (no longer needed) - ui->m_hidden_masters_label->hide(); - ui->gridLayout->removeWidget(ui->m_hidden_masters_label); - delete ui->m_hidden_masters_label; - ui->m_hidden_masters_label = nullptr; - - connect(m_hide_full_masters_cb, &QCheckBox::toggled, - this, &LinkSingleElementWidget::on_m_hide_full_masters_cb_toggled); + // Hide the full-masters checkbox for non-slave elements + const bool is_slave = (elmt && elmt->elementData().m_type == ElementData::Slave); + ui->m_hide_full_masters_cb->setVisible(is_slave); + // Restore persisted state before connecting to avoid toggled firing buildTree() + // while m_element is still null. + { + const QSignalBlocker blocker(ui->m_hide_full_masters_cb); QSettings settings; - m_hide_full_masters_cb->setChecked( + ui->m_hide_full_masters_cb->setChecked( settings.value(QStringLiteral("link-element-widget/hideFullMasters"), false).toBool()); } + + connect(ui->m_hide_full_masters_cb, &QCheckBox::toggled, + this, &LinkSingleElementWidget::hideFullMastersToggled); connect(m_show_qtwi, &QAction::triggered, this, [=]() { @@ -324,7 +315,7 @@ void LinkSingleElementWidget::buildTree() MasterElement *me = qobject_cast(elmt); if (me && me->isFull()) { m_full_masters.insert(qtwi); - if (m_hide_full_masters_cb && m_hide_full_masters_cb->isChecked()) + if (ui->m_hide_full_masters_cb->isChecked()) qtwi->setHidden(true); } } @@ -495,6 +486,8 @@ void LinkSingleElementWidget::setUpCompleter() */ void LinkSingleElementWidget::clearTreeWidget() { + m_pending_qtwi = nullptr; + while(ui->m_tree_widget->topLevelItemCount()) { QTreeWidgetItem *qtwi = ui->m_tree_widget->takeTopLevelItem(0); @@ -507,6 +500,7 @@ void LinkSingleElementWidget::clearTreeWidget() m_qtwi_elmt_hash.clear(); m_qtwi_strl_hash.clear(); + m_full_masters.clear(); } void LinkSingleElementWidget::setUpHeaderLabels() @@ -715,6 +709,7 @@ void LinkSingleElementWidget::hideButtons() ui->m_show_linked_pb->hide(); ui->m_show_this_pb->hide(); ui->m_search_field->show(); + ui->m_hide_full_masters_cb->show(); } /** @@ -728,6 +723,7 @@ void LinkSingleElementWidget::showButtons() ui->m_show_linked_pb->show(); ui->m_show_this_pb->show(); ui->m_search_field->hide(); + ui->m_hide_full_masters_cb->hide(); } void LinkSingleElementWidget::headerCustomContextMenuRequested( @@ -823,50 +819,35 @@ void LinkSingleElementWidget::on_m_show_this_pb_clicked() If arg1 is empty, show all items. @param arg1 */ -void LinkSingleElementWidget::on_m_search_field_textEdited(const QString &arg1) +void LinkSingleElementWidget::on_m_search_field_textEdited(const QString &) { - //Show all items if arg1 is empty, if not hide all items - foreach(QTreeWidgetItem *qtwi, m_qtwi_elmt_hash.keys()) - { - bool full_hidden = m_hide_full_masters_cb - && m_hide_full_masters_cb->isChecked() - && m_full_masters.contains(qtwi); - qtwi->setHidden(!arg1.isEmpty() || full_hidden); - } - - QList qtwi_list; - - foreach(QTreeWidgetItem *qtwi, m_qtwi_strl_hash.keys()) - { - foreach(QString str, m_qtwi_strl_hash.value(qtwi)) - { - if(str.contains(arg1, Qt::CaseInsensitive)) - { - qtwi_list << qtwi; - continue; - } - } - } - - //Show items which match with arg1 (but not full masters if checkbox is on) - foreach(QTreeWidgetItem *qtwi, qtwi_list) - { - bool full_hidden = m_hide_full_masters_cb - && m_hide_full_masters_cb->isChecked() - && m_full_masters.contains(qtwi); - if (!full_hidden) - qtwi->setHidden(false); - } + updateItemsVisibility(); } -bool LinkSingleElementWidget::isMasterFull(QTreeWidgetItem *item) const -{ - return m_full_masters.contains(item); -} - -void LinkSingleElementWidget::on_m_hide_full_masters_cb_toggled(bool checked) +void LinkSingleElementWidget::hideFullMastersToggled(bool checked) { QSettings settings; settings.setValue(QStringLiteral("link-element-widget/hideFullMasters"), checked); - buildTree(); + updateItemsVisibility(); +} + +void LinkSingleElementWidget::updateItemsVisibility() +{ + const QString text = ui->m_search_field->text(); + const bool hide_full = ui->m_hide_full_masters_cb->isChecked(); + + for (auto it = m_qtwi_strl_hash.cbegin(); it != m_qtwi_strl_hash.cend(); ++it) + { + bool match = text.isEmpty(); + if (!match) { + for (const QString &str : it.value()) { + if (str.contains(text, Qt::CaseInsensitive)) { + match = true; + break; + } + } + } + it.key()->setHidden(!match + || (hide_full && m_full_masters.contains(it.key()))); + } } diff --git a/sources/ui/linksingleelementwidget.h b/sources/ui/linksingleelementwidget.h index 9cb35c858..b41acd211 100644 --- a/sources/ui/linksingleelementwidget.h +++ b/sources/ui/linksingleelementwidget.h @@ -72,7 +72,7 @@ class LinkSingleElementWidget : public AbstractElementPropertiesEditorWidget void setUpCompleter(); void clearTreeWidget(); void setUpHeaderLabels(); - bool isMasterFull(QTreeWidgetItem *item) const; + void updateItemsVisibility(); private slots: void diagramWasRemovedFromProject(); @@ -89,7 +89,7 @@ class LinkSingleElementWidget : public AbstractElementPropertiesEditorWidget void on_m_show_this_pb_clicked(); void on_m_search_field_textEdited(const QString &arg1); - void on_m_hide_full_masters_cb_toggled(bool checked); + void hideFullMastersToggled(bool checked); private: Ui::LinkSingleElementWidget *ui; @@ -106,7 +106,6 @@ class LinkSingleElementWidget : public AbstractElementPropertiesEditorWidget Element *m_showed_element = nullptr, *m_element_to_link = nullptr; - QCheckBox *m_hide_full_masters_cb{nullptr}; QSet m_full_masters; int m_pending_group_index = -1; diff --git a/sources/ui/linksingleelementwidget.ui b/sources/ui/linksingleelementwidget.ui index b31f7c10d..0488626eb 100644 --- a/sources/ui/linksingleelementwidget.ui +++ b/sources/ui/linksingleelementwidget.ui @@ -21,7 +21,7 @@ - + 5 @@ -57,30 +57,20 @@ - + Recherche - - - + + - Remarque : les éléments maîtres ayant atteint leur nombre maximal d'esclaves sont masqués. - - - true - - - - true - + Masquer les éléments maîtres pleins - From 12ecf6b28fe15e76b048f4f9b9e7fef864dac423 Mon Sep 17 00:00:00 2001 From: ispyisail Date: Thu, 17 Sep 2026 10:09:14 +1200 Subject: [PATCH 06/20] Add read-only enforcement, preview, import/export to custom SQL reports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs discussion #886: a saved-report manager built on custom SQL and nomenclature.json. Most of what was asked for already existed -- Projet > Exporter au format CSV already builds/saves/reuses named SELECT queries via ElementQueryWidget and nomenclature.json, and Projet > Ajouter une nomenclature already inserts one into a folio. This fills the three real gaps. - projectDataBase::isReadOnlySelect() rejects anything that isn't a single SELECT/WITH statement. Checked in newQuery() itself, the one choke point every query path already goes through -- including a query loaded from a saved nomenclature/summary table's element on project open, not just the dialog's own custom-SQL box. ElementQueryWidget shows the same check live as you type, and BOMExportDialog surfaces it before running or exporting anything. - BOMExportDialog gains a Preview button + table (QSqlQueryModel), so a report can be checked on screen before committing to a CSV file. - ElementQueryWidget gains Importer.../Exporter... buttons that read/write nomenclature.json's saved reports as a JSON file, so a report can be handed to a colleague or another install. Import asks before overwriting a locally-saved report of the same name. Verified: Qt 6.10.2, builds clean, ctest 8/8. Drove the real dialog through Xvfb: typed "DROP TABLE element" into the custom-SQL box and got the inline warning immediately, then confirmed Preview also refuses it with a "Requête refusée" dialog rather than running it. Preview against the real default query returned live column headers and a row. Export opens a save dialog without crashing. Co-Authored-By: Claude Sonnet 5 --- sources/dataBase/projectdatabase.cpp | 86 ++++++++++- sources/dataBase/projectdatabase.h | 3 +- sources/dataBase/ui/elementquerywidget.cpp | 158 ++++++++++++++++++++- sources/dataBase/ui/elementquerywidget.h | 3 + sources/dataBase/ui/elementquerywidget.ui | 33 +++++ sources/ui/bomexportdialog.cpp | 50 ++++++- sources/ui/bomexportdialog.h | 3 + sources/ui/bomexportdialog.ui | 33 ++++- 8 files changed, 361 insertions(+), 8 deletions(-) diff --git a/sources/dataBase/projectdatabase.cpp b/sources/dataBase/projectdatabase.cpp index bd0f0069d..20d2c150d 100644 --- a/sources/dataBase/projectdatabase.cpp +++ b/sources/dataBase/projectdatabase.cpp @@ -28,6 +28,7 @@ #include "../qetproject.h" #include +#include #include #include @@ -132,11 +133,88 @@ QETProject *projectDataBase::project() const } /** - @brief projectDataBase::newQuery - @return a QSqlquery with query as query - and the internal database of this class as database to use. + @brief projectDataBase::isReadOnlySelect + Every query that reaches newQuery() goes through this check first -- + including one loaded from a saved nomenclature/summary table's + element (ProjectDBModel::fromXml()), which makes this a defense against + a crafted project file, not just a careless custom-SQL edit + (qelectrotech-source-mirror#886 asked for read-only enforcement on the + custom SQL reports feature; this covers every path into newQuery(), not + just that one dialog). + + Deliberately simple rather than a real SQL parser: reject more than one + statement (blocks stacking a write after a leading SELECT with `;`), and + require the query to start with SELECT or WITH. A determined attacker + with arbitrary SQL access to a local SQLite connection can still find + tricks a simple prefix check won't catch; this is meant to stop the + ordinary mistake and the obvious payload, not to be a security boundary + against a hostile file assumed to already run in some other trust + context. + @param query the raw SQL text to check + @param error set to a human-readable reason when this returns false + @return true if @p query looks like a single read-only SELECT/WITH */ -QSqlQuery projectDataBase::newQuery(const QString &query) { +bool projectDataBase::isReadOnlySelect(const QString &query, QString *error) +{ + if (error) { + error->clear(); + } + + QString trimmed = query.trimmed(); + if (trimmed.endsWith(QLatin1Char(';'))) { + trimmed.chop(1); + trimmed = trimmed.trimmed(); + } + + if (trimmed.isEmpty()) { + if (error) { + *error = projectDataBase::tr("La requête est vide."); + } + return false; + } + + if (trimmed.contains(QLatin1Char(';'))) { + if (error) { + *error = projectDataBase::tr("Une seule requête SELECT est autorisée" + " (le caractère ';' ne peut apparaître" + " qu'à la toute fin)."); + } + return false; + } + + const int first_space = trimmed.indexOf(QRegularExpression(QStringLiteral("\\s"))); + const QString first_word = (first_space == -1 ? trimmed : trimmed.left(first_space)).toUpper(); + if (first_word != QLatin1String("SELECT") && first_word != QLatin1String("WITH")) { + if (error) { + *error = projectDataBase::tr("Seules les requêtes en lecture seule" + " (SELECT ou WITH ... SELECT) sont" + " autorisées."); + } + return false; + } + + return true; +} + +/** + @brief projectDataBase::newQuery + @param query the SQL text to run -- must be a single read-only + SELECT/WITH statement, see isReadOnlySelect() + @param error set to a human-readable reason when the query was rejected + before ever reaching the database + @return a QSqlQuery with query as query and the internal database of + this class as database to use, or an unexecuted, harmless QSqlQuery if + the query was rejected +*/ +QSqlQuery projectDataBase::newQuery(const QString &query, QString *error) { + QString reason; + if (!isReadOnlySelect(query, &reason)) { + qWarning().noquote() << "projectDataBase::newQuery: rejected query:" << reason << "--" << query; + if (error) { + *error = reason; + } + return QSqlQuery(m_data_base); + } return QSqlQuery(query, m_data_base); } diff --git a/sources/dataBase/projectdatabase.h b/sources/dataBase/projectdatabase.h index 2ce9266bb..abd5e966c 100644 --- a/sources/dataBase/projectdatabase.h +++ b/sources/dataBase/projectdatabase.h @@ -59,7 +59,8 @@ class projectDataBase : public QObject */ void setUpdateBlocked(bool blocked); QETProject *project() const; - QSqlQuery newQuery(const QString &query = QString()); + QSqlQuery newQuery(const QString &query = QString(), QString *error = nullptr); + static bool isReadOnlySelect(const QString &query, QString *error = nullptr); QSqlDatabase database() const {return m_data_base;} int excludedConductorCount() const; diff --git a/sources/dataBase/ui/elementquerywidget.cpp b/sources/dataBase/ui/elementquerywidget.cpp index aafdeb32d..f5a42beb7 100644 --- a/sources/dataBase/ui/elementquerywidget.cpp +++ b/sources/dataBase/ui/elementquerywidget.cpp @@ -20,8 +20,15 @@ #include "../../properties/elementdata.h" #include "../../qetapp.h" #include "../../qetinformation.h" +#include "../projectdatabase.h" #include "ui_elementquerywidget.h" +#include +#include +#include +#include +#include + #include /** @@ -94,6 +101,9 @@ ElementQueryWidget::ElementQueryWidget(QWidget *parent) : setUpItems(); fillSavedQuery(); + + connect(ui->m_sql_query, &QLineEdit::textChanged, this, &ElementQueryWidget::checkQueryValidity); + checkQueryValidity(); } /** @@ -599,6 +609,8 @@ void ElementQueryWidget::on_m_edit_sql_query_cb_clicked() m_custom_query = ui->m_sql_query->text(); updateQueryLine(); } + + checkQueryValidity(); } /** @@ -672,6 +684,10 @@ void ElementQueryWidget::on_m_load_pb_clicked() */ void ElementQueryWidget::on_m_save_current_conf_pb_clicked() { + if (!projectDataBase::isReadOnlySelect(queryStr())) { + return; + } + QFile file_(QETApp::configDir() % "/nomenclature.json"); if (file_.open(QFile::ReadWrite)) @@ -698,7 +714,147 @@ void ElementQueryWidget::on_m_save_current_conf_pb_clicked() } void ElementQueryWidget::on_m_save_name_le_textChanged(const QString &arg1) { - ui->m_save_current_conf_pb->setDisabled(arg1.isEmpty()); + Q_UNUSED(arg1) + checkQueryValidity(); +} + +/** + @brief ElementQueryWidget::checkQueryValidity + Live feedback for the custom-SQL box: only the free-text path can carry + anything other than a SELECT (the column/filter builder always produces + one), so this only actually restricts something once "Requête SQL + personnalisée" is checked. Same rule projectDataBase::isReadOnlySelect() + enforces at execution time -- this just tells the user *before* they hit + Preview/Export/OK instead of after (qelectrotech-source-mirror#886). +*/ +void ElementQueryWidget::checkQueryValidity() +{ + if (!ui->m_edit_sql_query_cb->isChecked()) { + ui->m_query_warning_label->clear(); + ui->m_save_current_conf_pb->setEnabled(!ui->m_save_name_le->text().isEmpty()); + return; + } + + QString reason; + const bool valid = projectDataBase::isReadOnlySelect(ui->m_sql_query->text(), &reason); + ui->m_query_warning_label->setText(valid ? QString() : reason); + ui->m_save_current_conf_pb->setEnabled(valid && !ui->m_save_name_le->text().isEmpty()); +} + +/** + @brief ElementQueryWidget::on_m_export_reports_pb_clicked + Write every saved report in nomenclature.json to a file the user picks, + so it can be handed to a colleague or another install + (qelectrotech-source-mirror#886's "import and export report definitions + for sharing between users or company installations"). +*/ +void ElementQueryWidget::on_m_export_reports_pb_clicked() +{ + QFile source(QETApp::configDir() % "/nomenclature.json"); + if (!source.open(QFile::ReadOnly)) { + QMessageBox::information(this, tr("Exporter"), tr("Aucun rapport enregistré à exporter.")); + return; + } + const auto content = source.readAll(); + source.close(); + + if (QJsonDocument::fromJson(content).object().isEmpty()) { + QMessageBox::information(this, tr("Exporter"), tr("Aucun rapport enregistré à exporter.")); + return; + } + + const QString file_path = QFileDialog::getSaveFileName( + this, tr("Exporter les rapports"), QStringLiteral("rapports_qet.json"), + tr("Fichiers JSON (*.json)")); + if (file_path.isEmpty()) { + return; + } + + QFile dest(file_path); + if (!dest.open(QFile::WriteOnly) || dest.write(content) == -1) { + QMessageBox::critical(this, tr("Erreur"), tr("Impossible d'écrire dans %1.").arg(file_path)); + } +} + +/** + @brief ElementQueryWidget::on_m_import_reports_pb_clicked + Merge a previously-exported reports file into this install's + nomenclature.json. A name already used locally is not overwritten + silently -- the user is asked, per report, whether to replace it. +*/ +void ElementQueryWidget::on_m_import_reports_pb_clicked() +{ + const QString file_path = QFileDialog::getOpenFileName( + this, tr("Importer des rapports"), QString(), tr("Fichiers JSON (*.json)")); + if (file_path.isEmpty()) { + return; + } + + QFile source(file_path); + if (!source.open(QFile::ReadOnly)) { + QMessageBox::critical(this, tr("Erreur"), tr("Impossible de lire %1.").arg(file_path)); + return; + } + + QJsonParseError parse_error; + const auto incoming_doc = QJsonDocument::fromJson(source.readAll(), &parse_error); + source.close(); + + if (parse_error.error != QJsonParseError::NoError || !incoming_doc.isObject()) { + QMessageBox::critical( + this, tr("Erreur"), + tr("%1 ne contient pas des rapports QElectroTech valides.").arg(file_path)); + return; + } + + const auto incoming = incoming_doc.object(); + if (incoming.isEmpty()) { + QMessageBox::information(this, tr("Importer"), tr("Ce fichier ne contient aucun rapport.")); + return; + } + + QFile dest_file(QETApp::configDir() % "/nomenclature.json"); + QJsonObject existing; + if (dest_file.open(QFile::ReadOnly)) { + existing = QJsonDocument::fromJson(dest_file.readAll()).object(); + dest_file.close(); + } + + int imported = 0, skipped = 0; + for (auto it = incoming.begin(); it != incoming.end(); ++it) + { + if (existing.contains(it.key())) + { + const auto answer = QMessageBox::question( + this, tr("Rapport déjà existant"), + tr("Un rapport nommé « %1 » existe déjà. Le remplacer ?").arg(it.key()), + QMessageBox::Yes | QMessageBox::No); + if (answer != QMessageBox::Yes) { + ++skipped; + continue; + } + } + existing[it.key()] = it.value(); + ++imported; + } + + if (dest_file.open(QFile::WriteOnly | QFile::Truncate)) + { + dest_file.write(QJsonDocument(existing).toJson()); + dest_file.close(); + } + else + { + QMessageBox::critical(this, tr("Erreur"), tr("Impossible d'écrire la configuration locale.")); + return; + } + + ui->m_conf_cb->clear(); + fillSavedQuery(); + + QMessageBox::information( + this, tr("Importer"), + tr("%1 rapport(s) importé(s), %2 ignoré(s).").arg(imported).arg(skipped)); } void ElementQueryWidget::on_m_choosen_list_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) diff --git a/sources/dataBase/ui/elementquerywidget.h b/sources/dataBase/ui/elementquerywidget.h index b44377e12..f89883793 100644 --- a/sources/dataBase/ui/elementquerywidget.h +++ b/sources/dataBase/ui/elementquerywidget.h @@ -59,6 +59,9 @@ class ElementQueryWidget : public QWidget void on_m_load_pb_clicked(); void on_m_save_current_conf_pb_clicked(); void on_m_save_name_le_textChanged(const QString &arg1); + void on_m_import_reports_pb_clicked(); + void on_m_export_reports_pb_clicked(); + void checkQueryValidity(); void on_m_choosen_list_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous); void on_m_var_list_itemDoubleClicked(QListWidgetItem *item); void on_m_choosen_list_itemDoubleClicked(QListWidgetItem *item); diff --git a/sources/dataBase/ui/elementquerywidget.ui b/sources/dataBase/ui/elementquerywidget.ui index 7f3ae30fb..8de65df47 100644 --- a/sources/dataBase/ui/elementquerywidget.ui +++ b/sources/dataBase/ui/elementquerywidget.ui @@ -388,6 +388,26 @@ + + + + Importer des rapports depuis un fichier + + + Importer... + + + + + + + Exporter tous les rapports enregistrés vers un fichier + + + Exporter... + + + @@ -429,6 +449,19 @@ + + + + color: red; + + + + + + true + + + diff --git a/sources/ui/bomexportdialog.cpp b/sources/ui/bomexportdialog.cpp index e2ea6ce77..da2be53ca 100644 --- a/sources/ui/bomexportdialog.cpp +++ b/sources/ui/bomexportdialog.cpp @@ -26,6 +26,7 @@ #include #include +#include #include /** @@ -44,6 +45,9 @@ BOMExportDialog::BOMExportDialog(QETProject *project, QWidget *parent) : ui->m_main_layout->insertWidget(0, m_query_widget); m_query_widget->setQuery(BomExport::defaultQuery()); on_m_format_as_bom_clicked(false); + + m_preview_model = new QSqlQueryModel(this); + ui->m_preview_table->setModel(m_preview_model); } /** @@ -89,7 +93,15 @@ QByteArray BOMExportDialog::getBom(QString *error) error->clear(); } m_project->dataBase()->updateDB(); - auto query_ = m_project->dataBase()->newQuery(m_query_widget->queryStr()); + QString rejection; + auto query_ = m_project->dataBase()->newQuery(m_query_widget->queryStr(), &rejection); + + if (!rejection.isEmpty()) { + if (error) { + *error = rejection; + } + return {}; + } if (!query_.exec()) { qDebug() << "BOMExportDialog::getBom : query errir : " << query_.lastError(); @@ -130,3 +142,39 @@ void BOMExportDialog::on_m_format_as_bom_clicked(bool checked) { m_query_widget->setGroupBy("designation", checked); m_query_widget->setCount("COUNT(*) AS designation_qty", checked); } + +/** + @brief BOMExportDialog::on_m_preview_pb_clicked + Run the current query and show its result live, without going through + the CSV round-trip -- lets a report be checked and adjusted before + committing to a file (qelectrotech-source-mirror#886). +*/ +void BOMExportDialog::on_m_preview_pb_clicked() +{ + m_project->dataBase()->updateDB(); + QString rejection; + auto query_ = m_project->dataBase()->newQuery(m_query_widget->queryStr(), &rejection); + + if (!rejection.isEmpty()) { + QMessageBox::warning(this, tr("Requête refusée"), rejection); + return; + } + + if (!query_.exec()) { + QMessageBox::warning( + this, tr("Erreur"), + tr("Erreur dans la requête :\n%1").arg(query_.lastError().text())); + return; + } + + m_preview_model->setQuery(std::move(query_)); + for (int i = 0; i < m_preview_model->columnCount(); ++i) + { + const auto field_name = m_preview_model->record().fieldName(i); + const auto translated = QETInformation::translatedInfoKey(field_name); + if (!translated.isEmpty()) { + m_preview_model->setHeaderData(i, Qt::Horizontal, translated); + } + } + ui->m_preview_table->resizeColumnsToContents(); +} diff --git a/sources/ui/bomexportdialog.h b/sources/ui/bomexportdialog.h index 36fa40d15..0c5a0743e 100644 --- a/sources/ui/bomexportdialog.h +++ b/sources/ui/bomexportdialog.h @@ -23,6 +23,7 @@ class QETProject; class ElementQueryWidget; +class QSqlQueryModel; namespace Ui { class BOMExportDialog; @@ -44,11 +45,13 @@ class BOMExportDialog : public QDialog private slots: void on_m_format_as_bom_clicked(bool checked); + void on_m_preview_pb_clicked(); private: Ui::BOMExportDialog *ui; ElementQueryWidget *m_query_widget = nullptr; QETProject *m_project = nullptr; + QSqlQueryModel *m_preview_model = nullptr; }; #endif // BOMEXPORTDIALOG_H diff --git a/sources/ui/bomexportdialog.ui b/sources/ui/bomexportdialog.ui index b9e60b329..04b673f46 100644 --- a/sources/ui/bomexportdialog.ui +++ b/sources/ui/bomexportdialog.ui @@ -7,7 +7,7 @@ 0 0 610 - 232 + 480 @@ -56,6 +56,37 @@ + + + + + + Aperçu + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + QAbstractItemView::NoEditTriggers + + + From ff3c2e0916bcee2b45ab7fbc93f8b51d3f850429 Mon Sep 17 00:00:00 2001 From: Laurent Trinques Date: Thu, 17 Sep 2026 06:43:19 +0200 Subject: [PATCH 07/20] bump KDE KF6 frameworks version to 6.28.0 stable release --- cmake/fetch_kdeaddons.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/fetch_kdeaddons.cmake b/cmake/fetch_kdeaddons.cmake index 76df0a3f4..f0930193c 100644 --- a/cmake/fetch_kdeaddons.cmake +++ b/cmake/fetch_kdeaddons.cmake @@ -25,7 +25,7 @@ if(BUILD_WITH_KF) if(NOT DEFINED KF_GIT_TAG) # this is a more or less random version, taken as an conservative approach - set(KF_GIT_TAG v6.10.0) + set(KF_GIT_TAG v6.28.0) endif() # using a function in order to limit the scope of the variables # with CMake >=3.25 we could use a block() From 923723a9de05c6ff04df127c25cc765753126849 Mon Sep 17 00:00:00 2001 From: Andre Rummler Date: Thu, 17 Sep 2026 11:34:25 +0200 Subject: [PATCH 08/20] Fix the following issues: a) paste - conductor does not move - pressing escape does not abort - pressing anything during the process crashes the program instead of aborting b) similar issue with the escape not working fixed for graphics items, texxt fields (and probably others) --- sources/diagram.h | 1 + sources/diagramevent/diagrameventaddpaste.cpp | 24 +++++++++++-------- sources/diagramevent/diagrameventaddpaste.h | 1 + sources/diagramevent/diagrameventaddshape.cpp | 8 ++++++- sources/diagramview.cpp | 4 ++++ sources/qetdiagrameditor.cpp | 4 ++++ 6 files changed, 31 insertions(+), 11 deletions(-) diff --git a/sources/diagram.h b/sources/diagram.h index d2e0f0e12..aa9cb1d9f 100644 --- a/sources/diagram.h +++ b/sources/diagram.h @@ -216,6 +216,7 @@ class Diagram : public QGraphicsScene // methods related to graphics items addition/removal on the diagram virtual void addItem (QGraphicsItem *item); virtual void removeItem (QGraphicsItem *item); + bool hasEventInterface() const { return m_event_interface != nullptr; } // methods related to graphics options ExportProperties applyProperties(const ExportProperties &); diff --git a/sources/diagramevent/diagrameventaddpaste.cpp b/sources/diagramevent/diagrameventaddpaste.cpp index b08406b68..d9be0f159 100644 --- a/sources/diagramevent/diagrameventaddpaste.cpp +++ b/sources/diagramevent/diagrameventaddpaste.cpp @@ -87,12 +87,12 @@ DiagramEventAddPaste::DiagramEventAddPaste(Diagram *diagram, const QPointF &star */ DiagramEventAddPaste::~DiagramEventAddPaste() { - if (!m_finished && m_diagram) { - cancel(); - } - if (m_status_bar) { - m_status_bar->clearMessage(); + if (!m_finished) { + removeItems(); + m_finished = true; + m_running = false; } + if (m_status_bar) m_status_bar->clearMessage(); } /** @@ -139,6 +139,10 @@ void DiagramEventAddPaste::moveTo(const QPointF &scene_pos) it.key()->setPos(anchor + it.value()); } } + const auto conductors = m_content.conductors(); // AnyConductor by default + for (auto *cond : conductors) { + cond->updatePath(); + } } void DiagramEventAddPaste::mouseMoveEvent(QGraphicsSceneMouseEvent *event) @@ -216,13 +220,14 @@ void DiagramEventAddPaste::commit() void DiagramEventAddPaste::cancel() { if (m_finished || !m_diagram) return; + removeItems(); m_finished = true; m_running = false; + emit finish(); // only the user-driven path signals +} - //Conductors first: they hold pointers to the terminals of the - //elements below, so removing an element out from under one would - //leave it pointing at freed memory for as long as it is still in the - //scene. +void DiagramEventAddPaste::removeItems() +{ const QList conductors = m_content.conductors(DiagramContent::AnyConductor); for (auto *conductor : conductors) { m_diagram->removeItem(conductor); @@ -237,5 +242,4 @@ void DiagramEventAddPaste::cancel() m_content.clear(); m_relative_pos.clear(); - emit finish(); } diff --git a/sources/diagramevent/diagrameventaddpaste.h b/sources/diagramevent/diagrameventaddpaste.h index a200ca04d..8c7bcf86f 100644 --- a/sources/diagramevent/diagrameventaddpaste.h +++ b/sources/diagramevent/diagrameventaddpaste.h @@ -73,6 +73,7 @@ class DiagramEventAddPaste : public DiagramEventInterface void commit(); void cancel(); void showHint(); + void removeItems(); DiagramContent m_content; ///Each movable item's position relative to the group's top left, diff --git a/sources/diagramevent/diagrameventaddshape.cpp b/sources/diagramevent/diagrameventaddshape.cpp index d19669e76..9795d86f8 100644 --- a/sources/diagramevent/diagrameventaddshape.cpp +++ b/sources/diagramevent/diagrameventaddshape.cpp @@ -317,10 +317,16 @@ void DiagramEventAddShape::mouseMoveEvent(QGraphicsSceneMouseEvent *event) did anything. Re-running the last known mouse position through applyPosition() here makes the key press or release itself the trigger, giving immediate feedback instead of waiting on chance. + Any other key falls back to standard handling, e.g. pressing ESC aborts the operation. */ void DiagramEventAddShape::keyPressEvent(QKeyEvent *event) { - reapplyLastPosition(event); + if (event->key() == Qt::Key_Shift || event->key() == Qt::Key_Control) { + reapplyLastPosition(event); + event->setAccepted(true); + } else { + DiagramEventInterface::keyPressEvent(event); + } } void DiagramEventAddShape::keyReleaseEvent(QKeyEvent *event) diff --git a/sources/diagramview.cpp b/sources/diagramview.cpp index e16a02840..3d2a6d340 100644 --- a/sources/diagramview.cpp +++ b/sources/diagramview.cpp @@ -733,6 +733,10 @@ void DiagramView::keyPressEvent(QKeyEvent *e) //way off the canvas for someone working without a mouse. //Escape steps back out: first it drops the selection, then it //hands focus to the next widget. + if (m_diagram && m_diagram->hasEventInterface()) { + QGraphicsView::keyPressEvent(e); // let the active tool see it + return; + } if (m_diagram && !m_diagram->selectedItems().isEmpty()) { m_diagram->clearSelection(); } else { diff --git a/sources/qetdiagrameditor.cpp b/sources/qetdiagrameditor.cpp index c9f4c868e..247d889ad 100644 --- a/sources/qetdiagrameditor.cpp +++ b/sources/qetdiagrameditor.cpp @@ -1754,6 +1754,10 @@ void QETDiagramEditor::addItemGroupTriggered(QAction *action) // here makes the button's appearance match its actual state // regardless of whether Qt's own change notification fired // correctly. + + if (DiagramView *dv = currentDiagramView()) + dv->setFocus(); // so the view (and the active tool) actually receives Escape etc + if (QWidget *button = m_add_item_tool_bar->widgetForAction(action)) button->update(); } From 52713704bcd927144c6fc25ddab247f412fee360 Mon Sep 17 00:00:00 2001 From: ispyisail Date: Thu, 17 Sep 2026 22:10:03 +1200 Subject: [PATCH 09/20] Offer the crash report after the recovery prompt, not instead of it (#901) QETApp::checkBackupFiles() only reached checkCrashDump() when there was nothing to recover: if (stale_files.isEmpty()) { checkCrashDump(); return; } A crash with a project open always leaves a stale KAutoSaveFile, so on the next launch the recovery prompt won every time and the dump sat on disk unoffered -- the report was unreachable in exactly the case it is most wanted. Reported by ChuckNr11 as a side note in #898: "the report appeared only once despite there being 10 or more crashes". It appears on the runs that happen to have nothing to recover. Discussion #644 step 5 asks that the two prompts never show at the same time, which this keeps: the recovery prompt is answered first, then the report. The recovery half moves into offerBackupFiles() so both paths fall through to the same place. Verified under Xvfb, from a real crash state (SIGABRT with a project open, leaving both a stale file and a 5.4 KB crash_dump.log): the recovery prompt appears, and dismissing it now brings up "Rapport de plantage" carrying the version, git SHA, OS, Qt version and the log ring. Before this change the report never appeared -- that half rests on the four lines above rather than on a captured before/after, since re-creating the crash state for a clean baseline run kept consuming it. Not addressed: the dump is a single fixed path opened O_TRUNC, so consecutive crashes still overwrite one another. ctest 8/8. Co-Authored-By: Claude Opus 5 --- sources/qetapp.cpp | 26 ++++++++++++++++++++------ sources/qetapp.h | 2 ++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/sources/qetapp.cpp b/sources/qetapp.cpp index d8e625d4d..4f334c676 100644 --- a/sources/qetapp.cpp +++ b/sources/qetapp.cpp @@ -2660,14 +2660,28 @@ void QETApp::checkBackupFiles() } } - if (stale_files.isEmpty()) { - // Only offer an unretrieved crash dump when there's no project - // to recover this run -- discussion #644 step 5 is explicit - // that the two prompts must never both show at once. - checkCrashDump(); - return; + if (!stale_files.isEmpty()) { + offerBackupFiles(stale_files); } + // Discussion #644 step 5 asks that the recovery prompt and the crash + // report never show at the same time -- not that the report be dropped + // whenever there is something to recover. Offering it here, once the + // recovery prompt has been answered, keeps the two sequential without + // losing the report after the most common crash there is: one with a + // project open, which always leaves a stale file behind, so the report + // was unreachable in exactly the case it is most wanted (issue #901). + checkCrashDump(); +} + +/** + @brief QETApp::offerBackupFiles + Ask whether to reopen the recovery files left by a previous run, and + open or discard them accordingly. + @param stale_files : the recovery files to offer +*/ +void QETApp::offerBackupFiles(const QList &stale_files) +{ QString text; if(stale_files.size() == 1) { text.append(tr("Le fichier de restauration suivant a été trouvé,
" diff --git a/sources/qetapp.h b/sources/qetapp.h index ee793ecb6..7d0e5e7ee 100644 --- a/sources/qetapp.h +++ b/sources/qetapp.h @@ -31,6 +31,7 @@ class QSplashScreen; class QMenu; class QAction; class QMainWindow; +class KAutoSaveFile; #define QETAPP_COMMON_TBT_PROTOCOL "commontbt" #define QETAPP_COMPANY_TBT_PROTOCOL "companytbt" @@ -290,6 +291,7 @@ class QETApp : public QObject void initSystemTray(); void buildSystemTrayMenu(); void checkBackupFiles(); + void offerBackupFiles(const QList &stale_files); void checkCrashDump(); void fetchWindowStats( const QList &, From 0a7ea032d98b09ad910958cd82d009005c2490c3 Mon Sep 17 00:00:00 2001 From: Laurent Trinques Date: Thu, 17 Sep 2026 12:18:10 +0200 Subject: [PATCH 10/20] Revert "bump KDE KF6 frameworks version to 6.28.0 stable release" Close https://github.com/qelectrotech/qelectrotech-source-mirror/issues/900 This reverts commit ff3c2e0916bcee2b45ab7fbc93f8b51d3f850429. --- cmake/fetch_kdeaddons.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/fetch_kdeaddons.cmake b/cmake/fetch_kdeaddons.cmake index f0930193c..76df0a3f4 100644 --- a/cmake/fetch_kdeaddons.cmake +++ b/cmake/fetch_kdeaddons.cmake @@ -25,7 +25,7 @@ if(BUILD_WITH_KF) if(NOT DEFINED KF_GIT_TAG) # this is a more or less random version, taken as an conservative approach - set(KF_GIT_TAG v6.28.0) + set(KF_GIT_TAG v6.10.0) endif() # using a function in order to limit the scope of the variables # with CMake >=3.25 we could use a block() From 8ef130a59d3e881a75ad77495590135479705aa8 Mon Sep 17 00:00:00 2001 From: Jeff Patterson Date: Wed, 16 Sep 2026 05:40:33 -0500 Subject: [PATCH 11/20] Add a "qet" icon theme and load icons by name QET had no icon theme: the 446 entries of the icon table and the 116 iconsets in .ui files each named a resource path, so an icon could only ever be one file, and a variant for another palette or a vector source had nowhere to go (GitHub #466, #690, #870). This adds the theme layout without changing a single pixel; a dark variant comes in a follow-up. The theme "qet" follows the freedesktop layout Qt's icon loader understands. misc/make_icon_themes.py generates ico/icon-themes.qrc, which aliases the existing ico//.png files into themes/qet//.png, and ico/themes/qet/index.theme. No file moves. The four table entries that paired a 16 pixel file with a 22 pixel file of another name (ConductorSettings, DiagramAdd, DiagramDelete, DialogInformation) get the 22 pixel file aliased under the 16 pixel name. QETApp::initIconTheme() registers the theme before initIcons() and makes it current on every platform, so a desktop icon theme cannot replace QET's icons. Icons are then looked up by name: QIcon::fromTheme() in qeticons.cpp and in the few places that built a QIcon from a resource path directly, and theme="..." on the iconsets in .ui files, with the resource path kept as fallback. Flags, color swatches, application and MIME icons stay on their paths. One entry does not go through the theme. The elements panel draws the project root with ProjectFileGP in the 50 pixel slot it reserves for element previews, and the name "project" also carries the 128 pixel file the configuration dialog uses. On a 2x display Qt's loader picks that file for a 50 pixel request and fills the slot. ProjectFileGP loads the 16 and 22 pixel files directly, as before. tests/qttest/tst_qeticons: every name in the theme resolves, the four aliases resolve at 22 pixels, a Fusion tool button shows its icon at 3:1 with disabled weaker than enabled, and the project root icon stays at 22 pixels or less when asked for 50 at 2x while the configuration dialog still gets its 128 pixel file. The rendering helpers shared with tst_qetpalette moved to tests/qttest/inkcontrast.h. --- CMakeLists.txt | 2 + ico/icon-themes.qrc | 285 ++++++ ico/themes/qet/index.theme | 30 + misc/make_icon_themes.py | 130 +++ .../fileelementcollectionitem.cpp | 8 +- sources/NameList/ui/namelistwidget.ui | 4 +- .../ui/replaceconductordialog.ui | 6 +- .../SearchAndReplace/ui/replacefoliowidget.ui | 2 +- .../ui/searchandreplacewidget.ui | 8 +- .../TerminalStrip/ui/freeterminaleditor.ui | 2 +- .../TerminalStrip/ui/terminalstripeditor.ui | 2 +- .../ui/terminalstripeditorwindow.ui | 6 +- sources/autoNum/ui/autonumberingdockwidget.ui | 8 +- sources/autoNum/ui/selectautonumw.ui | 10 +- sources/dataBase/ui/elementquerywidget.ui | 12 +- sources/dataBase/ui/summaryquerywidget.ui | 8 +- sources/editor/ui/polygoneditor.ui | 4 +- sources/editor/ui/qetelementeditor.ui | 78 +- sources/print/projectprintwindow.ui | 30 +- sources/qetapp.cpp | 17 + sources/qetapp.h | 1 + sources/qetdiagrameditor.cpp | 2 +- .../ui/graphicstablepropertieseditor.ui | 8 +- .../ui/projectdbmodelpropertieswidget.ui | 4 +- sources/qeticons.cpp | 836 ++++++++---------- sources/richtext/richtexteditor.cpp | 12 +- sources/ui/conductorpropertieswidget.ui | 8 +- sources/ui/configsaveloaderwidget.ui | 4 +- sources/ui/dynamicelementtextitemeditor.ui | 10 +- sources/ui/masterpropertieswidget.ui | 4 +- sources/ui/thirdpartybinaryinstalldialog.ui | 4 +- sources/ui/titleblockpropertieswidget.ui | 10 +- tests/qttest/CMakeLists.txt | 16 + tests/qttest/inkcontrast.h | 77 ++ tests/qttest/tst_qeticons.cpp | 166 ++++ tests/qttest/tst_qetpalette.cpp | 41 +- 36 files changed, 1237 insertions(+), 618 deletions(-) create mode 100644 ico/icon-themes.qrc create mode 100644 ico/themes/qet/index.theme create mode 100644 misc/make_icon_themes.py create mode 100644 tests/qttest/inkcontrast.h create mode 100644 tests/qttest/tst_qeticons.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 4fc0fffd4..60ea552e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -139,6 +139,7 @@ if(WIN32) ${QET_SRC_FILES} ${QM_FILES} ${QET_DIR}/qelectrotech.qrc + ${QET_DIR}/ico/icon-themes.qrc ) else() add_executable( @@ -147,6 +148,7 @@ else() ${QET_SRC_FILES} ${QM_FILES} ${QET_DIR}/qelectrotech.qrc + ${QET_DIR}/ico/icon-themes.qrc ) endif() diff --git a/ico/icon-themes.qrc b/ico/icon-themes.qrc new file mode 100644 index 000000000..1a3635da5 --- /dev/null +++ b/ico/icon-themes.qrc @@ -0,0 +1,285 @@ + + + + + themes/qet/index.theme + 16x16/application-exit.png + 16x16/arrow-left-double.png + 16x16/arrow-left.png + 16x16/arrow-right-double.png + 16x16/arrow-right.png + 16x16/circle.png + 16x16/conductor-edit.png + 16x16/conductor-reset.png + 16x16/conductor.png + 16x16/configure-toolbars.png + 16x16/configure.png + 16x16/diagram.png + 16x16/dialog-cancel.png + 16x16/dialog-ok.png + 16x16/document-close.png + 16x16/document-export.png + 16x16/document-import.png + 16x16/document-new.png + 16x16/document-open-recent.png + 16x16/document-open.png + 16x16/document-print-frame.png + 16x16/document-print.png + 16x16/document-save-all.png + 16x16/document-save-as.png + 16x16/document-save.png + 16x16/east.png + 16x16/edit-clear-locationbar-ltr.png + 16x16/edit-clear-locationbar-rtl.png + 16x16/edit-clear.png + 16x16/edit-copy.png + 16x16/edit-cut.png + 16x16/edit-delete.png + 16x16/edit-download.png + 16x16/edit-paste.png + 16x16/edit-redo.png + 16x16/edit-rename.png + 16x16/edit-select-all.png + 16x16/edit-select-invert.png + 16x16/edit-select-none.png + 16x16/edit-table-cell-merge.png + 16x16/edit-table-cell-split.png + 16x16/edit-table-delete-column.png + 16x16/edit-table-delete-row.png + 16x16/edit-table-insert-column-left.png + 16x16/edit-table-insert-column-right.png + 16x16/edit-table-insert-row-above.png + 16x16/edit-table-insert-row-under.png + 16x16/edit-undo.png + 16x16/element-delete.png + 16x16/element-edit.png + 16x16/element-master.png + 16x16/element-new.png + 16x16/element-slave.png + 16x16/element.png + 16x16/endline-circle.png + 16x16/endline-diamond.png + 16x16/endline-none.png + 16x16/endline-simple.png + 16x16/endline-triangle.png + 16x16/export-csv.png + 16x16/flip.png + 16x16/folder-delete.png + 16x16/folder-edit.png + 16x16/folder-new.png + 16x16/folder-only-this.png + 16x16/folder-open.png + 16x16/folder-properties.png + 16x16/folder-show-all.png + 16x16/folder.png + 16x16/folio-delete.png + 16x16/folio-new.png + 16x16/folio-properties.png + 16x16/folio-ref-coming.png + 16x16/go-bottom.png + 16x16/go-company.png + 16x16/go-down-double.png + 16x16/go-down.png + 16x16/go-home.png + 16x16/go-top.png + 16x16/go-up-double.png + 16x16/go-up.png + 16x16/grid.png + 16x16/ground.png + 16x16/help-contents.png + 16x16/help-donate.png + 16x16/item-cancel.png + 16x16/item-copy.png + 16x16/item-move.png + 16x16/kdenlive-show-video.png + 16x16/label.png + 16x16/list-add.png + 16x16/list-remove.png + 16x16/masquer.png + 16x16/mirror.png + 16x16/move.png + 16x16/neutral.png + 16x16/north.png + 16x16/object-group.png + 16x16/object-rotate-right.png + 16x16/orientations.png + 16x16/phase.png + 16x16/preferences-desktop-user.png + 16x16/project-close.png + 16x16/project-delete.png + 16x16/project-new.png + 16x16/project-properties.png + 16x16/project.png + 16x16/qet.png + 16x16/qt.png + 16x16/run-dxf.png + 16x16/select.png + 16x16/south.png + 16x16/table-of-content.png + 16x16/terminal.png + 16x16/terminalstrip.png + 16x16/text-xml.png + 16x16/transform-rotate.png + 16x16/user-busy.png + 16x16/user-online.png + 16x16/view-fullscreen.png + 16x16/view-refresh.png + 16x16/view-restore.png + 16x16/west.png + 16x16/window-new.png + 16x16/zoom-draw.png + 16x16/zoom-fit-best.png + 16x16/zoom-in.png + 16x16/zoom-original.png + 16x16/zoom-out.png + 22x22/Actions-player-time-icon.png + 22x22/all_pages.png + 22x22/application-exit.png + 22x22/applications-development-translation.png + 22x22/arc.png + 22x22/arrow-left-double.png + 22x22/arrow-left.png + 22x22/arrow-right-double.png + 22x22/arrow-right.png + 22x22/autoconnect.png + 22x22/bring_forward.png + 22x22/conductor2.png + 22x22/conductor2.png + 22x22/configure-toolbars.png + 22x22/configure.png + 22x22/diagram.png + 22x22/diagram_add.png + 22x22/diagram_add.png + 22x22/diagram_bg.png + 22x22/diagram_del.png + 22x22/diagram_del.png + 22x22/dialog-cancel.png + 22x22/dialog-information.png + 22x22/dialog-information.png + 22x22/dialog-ok.png + 22x22/document-close.png + 22x22/document-export.png + 22x22/document-import.png + 22x22/document-new.png + 22x22/document-open-recent.png + 22x22/document-open.png + 22x22/document-print-frame.png + 22x22/document-print.png + 22x22/document-save-all.png + 22x22/document-save-as.png + 22x22/document-save.png + 22x22/edit-clear-locationbar-ltr.png + 22x22/edit-clear-locationbar-rtl.png + 22x22/edit-clear.png + 22x22/edit-copy.png + 22x22/edit-cut.png + 22x22/edit-delete.png + 22x22/edit-paste.png + 22x22/edit-redo.png + 22x22/edit-rename.png + 22x22/edit-select-all.png + 22x22/edit-table-cell-merge.png + 22x22/edit-table-cell-split.png + 22x22/edit-table-delete-column.png + 22x22/edit-table-delete-row.png + 22x22/edit-table-insert-column-left.png + 22x22/edit-table-insert-column-right.png + 22x22/edit-table-insert-row-above.png + 22x22/edit-table-insert-row-under.png + 22x22/edit-undo.png + 22x22/element-delete.png + 22x22/element-edit.png + 22x22/element-new.png + 22x22/ellipse.png + 22x22/export-csv.png + 22x22/folder-delete.png + 22x22/folder-edit.png + 22x22/folder-new.png + 22x22/folder-open.png + 22x22/format-text-subscript.png + 22x22/format-text-superscript.png + 22x22/go-company.png + 22x22/go-down.png + 22x22/go-home.png + 22x22/go-up.png + 22x22/grid.png + 22x22/guides.png + 22x22/hotspot.png + 22x22/insert-image.png + 22x22/label.png + 22x22/landscape.png + 22x22/line.png + 22x22/list-add.png + 22x22/list-remove.png + 22x22/lower.png + 22x22/move.png + 22x22/names.png + 22x22/object-locked.png + 22x22/object-rotate-right.png + 22x22/object-unlocked.png + 22x22/pdf-import.png + 22x22/polygon.png + 22x22/portrait.png + 22x22/preferences-desktop-user.png + 22x22/project-close.png + 22x22/project-delete.png + 22x22/project-new.png + 22x22/project.png + 22x22/raise.png + 22x22/rectangle.png + 22x22/restaurer.png + 22x22/select.png + 22x22/send_backward.png + 22x22/single_page.png + 22x22/start.png + 22x22/terminal.png + 22x22/terminalstrip.png + 22x22/text-xml.png + 22x22/text.png + 22x22/textfield.png + 22x22/titleblock-bottom.png + 22x22/titleblock-right.png + 22x22/transform-scale.png + 22x22/two_pages.png + 22x22/view-fit-window.png + 22x22/view-fullscreen.png + 22x22/view-refresh.png + 22x22/view-restore.png + 22x22/view_fit_width.png + 22x22/window-new.png + 22x22/zoom-draw.png + 22x22/zoom-fit-best.png + 22x22/zoom-in.png + 22x22/zoom-original.png + 22x22/zoom-out.png + 32x32/application-pdf.png + 32x32/format-text-bold.png + 32x32/format-text-color.png + 32x32/format-text-italic.png + 32x32/format-text-underline.png + 32x32/image-x-eps.png + 32x32/insert-link.png + 32x32/printer.png + 32x32/qt.png + 32x32/simplifyrichtext.png + 32x32/text-xml.png + 48x48/document-print-frame.png + 48x48/user-away-extended.png + 48x48/user-away.png + 48x48/view-pim-journal.png + 128x128/diagram.png + 128x128/document-export.png + 128x128/plasmagik.png + 128x128/printer.png + 128x128/project.png + 128x128/settings.png + breeze-icons/scalable/apps/hidef/edit-opacity.svg + breeze-icons/scalable/apps/hidef/image-flip-horizontal-symbolic.svg + breeze-icons/scalable/apps/hidef/image-flip-vertical-symbolic.svg + breeze-icons/scalable/apps/hidef/draw-bezier-curves.svg + breeze-icons/scalable/apps/hidef/transform-crop.svg + generated/ellipse-to-bezier.svg + generated/rect-to-bezier.svg + generated/rect-to-polyline.svg + + diff --git a/ico/themes/qet/index.theme b/ico/themes/qet/index.theme new file mode 100644 index 000000000..74c1b904b --- /dev/null +++ b/ico/themes/qet/index.theme @@ -0,0 +1,30 @@ +[Icon Theme] +Name=qet +Comment=QElectroTech icons +Directories=16x16,22x22,32x32,48x48,128x128,scalable + +[16x16] +Size=16 +Type=Fixed + +[22x22] +Size=22 +Type=Fixed + +[32x32] +Size=32 +Type=Fixed + +[48x48] +Size=48 +Type=Fixed + +[128x128] +Size=128 +Type=Fixed + +[scalable] +Size=22 +Type=Scalable +MinSize=8 +MaxSize=256 diff --git a/misc/make_icon_themes.py b/misc/make_icon_themes.py new file mode 100644 index 000000000..864798b1a --- /dev/null +++ b/misc/make_icon_themes.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 +# Copyright 2006-2026 The QElectroTech Team +# This file is part of QElectroTech. +# +# QElectroTech is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# QElectroTech is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with QElectroTech. If not, see . +"""Build QET's icon theme "qet" from the PNG and SVG sources in ico/. + +Run from the repository root: + + python3 misc/make_icon_themes.py + +It writes: + + ico/themes/qet/index.theme the theme description + ico/icon-themes.qrc resource file listing the theme + +The theme does not copy any file. The .qrc aliases the existing +ico//.png files into the theme layout Qt's icon loader +expects (themes/qet//.png), so QIcon::fromTheme("name") +finds them. Idempotent: running it twice changes nothing. +""" + +import os +import re +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +ICO = ROOT / "ico" +THEMES = ICO / "themes" +QRC = ICO / "icon-themes.qrc" + +# Fixed-size folders that hold toolbar, menu and dialog icons. The 24x16 +# flags, the 22x22/color swatches and the application icons are not theme +# icons and stay on their resource paths. +SIZES = ["16x16", "22x22", "32x32", "48x48", "128x128"] + +# Table entries in sources/qeticons.cpp that pair a 16 pixel file with a +# 22 pixel file of another name. The theme needs one name per icon, so +# the 22 pixel file is exposed under the 16 pixel name as well. +ALIASES = { + "22x22/conductor2.png": "conductor-reset", + "22x22/diagram_add.png": "folio-new", + "22x22/diagram_del.png": "folio-delete", + "22x22/dialog-information.png": "folio-properties", +} + +# SVG icons referenced from sources/qeticons.cpp. +SVGS = [ + "breeze-icons/scalable/apps/hidef/edit-opacity.svg", + "breeze-icons/scalable/apps/hidef/image-flip-horizontal-symbolic.svg", + "breeze-icons/scalable/apps/hidef/image-flip-vertical-symbolic.svg", + "breeze-icons/scalable/apps/hidef/draw-bezier-curves.svg", + "breeze-icons/scalable/apps/hidef/transform-crop.svg", + "generated/ellipse-to-bezier.svg", + "generated/rect-to-bezier.svg", + "generated/rect-to-polyline.svg", +] + + +def write_if_changed(path, data): + """Write text only when the content differs, so git stays quiet.""" + if path.exists() and path.read_text(encoding="utf-8") == data: + return False + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(data, encoding="utf-8") + return True + + +def index_theme(name, comment, dirs, inherits=None): + lines = ["[Icon Theme]", f"Name={name}", f"Comment={comment}"] + if inherits: + lines.append(f"Inherits={inherits}") + lines.append("Directories=" + ",".join(dirs)) + lines.append("") + for d in dirs: + lines.append(f"[{d}]") + if d == "scalable": + lines += ["Size=22", "Type=Scalable", "MinSize=8", "MaxSize=256"] + else: + lines += [f"Size={d.split('x')[0]}", "Type=Fixed"] + lines.append("") + return "\n".join(lines) + + +def main(): + entries = [] # (alias, source) pairs, paths relative to ico/ + changed = 0 + + for size in SIZES: + for png in sorted((ICO / size).glob("*.png")): + rel = f"{size}/{png.name}" + names = [png.stem] + if rel in ALIASES: + names.append(ALIASES[rel]) + for name in names: + entries.append((f"themes/qet/{size}/{name}.png", rel)) + + for rel in SVGS: + name = re.sub(r"-symbolic(?=\.svg$)", "", Path(rel).name) + entries.append((f"themes/qet/scalable/{name}", rel)) + + dirs = SIZES + ["scalable"] + changed += write_if_changed(THEMES / "qet" / "index.theme", + index_theme("qet", "QElectroTech icons", dirs)) + + qrc = ["", "", + '', ' ', + " themes/qet/index.theme"] + for alias, source in entries: + qrc.append(f' {source}') + qrc += [" ", "", ""] + changed += write_if_changed(QRC, "\n".join(qrc)) + + print(f"{len(entries)} theme entries; {changed} files written") + + +if __name__ == "__main__": + os.chdir(ROOT) + main() diff --git a/sources/ElementsCollection/fileelementcollectionitem.cpp b/sources/ElementsCollection/fileelementcollectionitem.cpp index 15a6cdb83..2183507ab 100644 --- a/sources/ElementsCollection/fileelementcollectionitem.cpp +++ b/sources/ElementsCollection/fileelementcollectionitem.cpp @@ -456,13 +456,13 @@ void FileElementCollectionItem::setUpIcon() if (macrosPath.endsWith("/")) macrosPath.remove(macrosPath.length() - 1, 1); if (m_path == QETApp::commonElementsDirN()) - setIcon(QIcon(":/ico/16x16/qet.png")); + setIcon(QIcon::fromTheme("qet")); else if (m_path == QETApp::companyElementsDirN()) - setIcon(QIcon(":/ico/16x16/go-company.png")); + setIcon(QIcon::fromTheme("go-company")); else if (m_path == macrosPath) - setIcon(QIcon(":/ico/16x16/go-home.png")); + setIcon(QIcon::fromTheme("go-home")); else - setIcon(QIcon(":/ico/16x16/go-home.png")); + setIcon(QIcon::fromTheme("go-home")); } else { diff --git a/sources/NameList/ui/namelistwidget.ui b/sources/NameList/ui/namelistwidget.ui index f021a77a3..a38dd046d 100644 --- a/sources/NameList/ui/namelistwidget.ui +++ b/sources/NameList/ui/namelistwidget.ui @@ -36,7 +36,7 @@ Ajouter une ligne
- + :/ico/16x16/list-add.png:/ico/16x16/list-add.png
@@ -48,7 +48,7 @@ Copier dans le presse papier
- + :/ico/16x16/edit-paste.png:/ico/16x16/edit-paste.png diff --git a/sources/SearchAndReplace/ui/replaceconductordialog.ui b/sources/SearchAndReplace/ui/replaceconductordialog.ui index 4be6a94d3..f5cefc78c 100644 --- a/sources/SearchAndReplace/ui/replaceconductordialog.ui +++ b/sources/SearchAndReplace/ui/replaceconductordialog.ui @@ -370,7 +370,7 @@ phase - + :/ico/16x16/phase.png:/ico/16x16/phase.png @@ -397,7 +397,7 @@ neutre - + :/ico/16x16/neutral.png:/ico/16x16/neutral.png @@ -433,7 +433,7 @@ terre - + :/ico/16x16/ground.png:/ico/16x16/ground.png diff --git a/sources/SearchAndReplace/ui/replacefoliowidget.ui b/sources/SearchAndReplace/ui/replacefoliowidget.ui index b5ee7e0df..b438180b9 100644 --- a/sources/SearchAndReplace/ui/replacefoliowidget.ui +++ b/sources/SearchAndReplace/ui/replacefoliowidget.ui @@ -180,7 +180,7 @@ - + :/ico/22x22/start.png:/ico/22x22/start.png diff --git a/sources/SearchAndReplace/ui/searchandreplacewidget.ui b/sources/SearchAndReplace/ui/searchandreplacewidget.ui index fcc280960..597e1f439 100644 --- a/sources/SearchAndReplace/ui/searchandreplacewidget.ui +++ b/sources/SearchAndReplace/ui/searchandreplacewidget.ui @@ -106,7 +106,7 @@ - + :/ico/16x16/go-bottom.png:/ico/16x16/go-bottom.png @@ -123,7 +123,7 @@ - + :/ico/16x16/go-top.png:/ico/16x16/go-top.png @@ -140,7 +140,7 @@ - + :/ico/16x16/view-refresh.png:/ico/16x16/view-refresh.png @@ -157,7 +157,7 @@ - + :/ico/16x16/configure-toolbars.png:/ico/16x16/configure-toolbars.png diff --git a/sources/TerminalStrip/ui/freeterminaleditor.ui b/sources/TerminalStrip/ui/freeterminaleditor.ui index f464b3e1d..4f318c60e 100644 --- a/sources/TerminalStrip/ui/freeterminaleditor.ui +++ b/sources/TerminalStrip/ui/freeterminaleditor.ui @@ -84,7 +84,7 @@ - + :/ico/22x22/dialog-ok.png:/ico/22x22/dialog-ok.png diff --git a/sources/TerminalStrip/ui/terminalstripeditor.ui b/sources/TerminalStrip/ui/terminalstripeditor.ui index 29f6854e3..ef65cdf9c 100644 --- a/sources/TerminalStrip/ui/terminalstripeditor.ui +++ b/sources/TerminalStrip/ui/terminalstripeditor.ui @@ -63,7 +63,7 @@ - + :/ico/16x16/dialog-ok.png:/ico/16x16/dialog-ok.png diff --git a/sources/TerminalStrip/ui/terminalstripeditorwindow.ui b/sources/TerminalStrip/ui/terminalstripeditorwindow.ui index f390f33cf..53b413b3b 100644 --- a/sources/TerminalStrip/ui/terminalstripeditorwindow.ui +++ b/sources/TerminalStrip/ui/terminalstripeditorwindow.ui @@ -54,7 +54,7 @@ - + :/ico/16x16/list-add.png:/ico/16x16/list-add.png @@ -66,7 +66,7 @@ - + :/ico/16x16/list-remove.png:/ico/16x16/list-remove.png @@ -78,7 +78,7 @@ - + :/ico/16x16/view-refresh.png:/ico/16x16/view-refresh.png diff --git a/sources/autoNum/ui/autonumberingdockwidget.ui b/sources/autoNum/ui/autonumberingdockwidget.ui index 4fcd61901..b0bcc6e61 100644 --- a/sources/autoNum/ui/autonumberingdockwidget.ui +++ b/sources/autoNum/ui/autonumberingdockwidget.ui @@ -73,7 +73,7 @@ - + :/ico/16x16/view-refresh.png:/ico/16x16/view-refresh.png @@ -161,7 +161,7 @@ - + :/ico/16x16/view-refresh.png:/ico/16x16/view-refresh.png @@ -238,7 +238,7 @@ - + :/ico/16x16/view-refresh.png:/ico/16x16/view-refresh.png @@ -319,7 +319,7 @@ Configurer - + :/ico/16x16/configure.png:/ico/16x16/configure.png diff --git a/sources/autoNum/ui/selectautonumw.ui b/sources/autoNum/ui/selectautonumw.ui index 3c4d953af..8da8d2e70 100644 --- a/sources/autoNum/ui/selectautonumw.ui +++ b/sources/autoNum/ui/selectautonumw.ui @@ -63,7 +63,7 @@ - + :/ico/16x16/edit-delete.png:/ico/16x16/edit-delete.png @@ -136,7 +136,7 @@ - + :/ico/22x22/list-remove.png:/ico/22x22/list-remove.png @@ -153,7 +153,7 @@ - + :/ico/22x22/list-add.png:/ico/22x22/list-add.png @@ -170,7 +170,7 @@ - + :/ico/16x16/arrow-left.png:/ico/16x16/arrow-left.png @@ -184,7 +184,7 @@ - + :/ico/16x16/arrow-right.png:/ico/16x16/arrow-right.png diff --git a/sources/dataBase/ui/elementquerywidget.ui b/sources/dataBase/ui/elementquerywidget.ui index 8de65df47..8300b2387 100644 --- a/sources/dataBase/ui/elementquerywidget.ui +++ b/sources/dataBase/ui/elementquerywidget.ui @@ -88,7 +88,7 @@ - + :/ico/16x16/go-up.png:/ico/16x16/go-up.png @@ -105,7 +105,7 @@ - + :/ico/16x16/list-add.png:/ico/16x16/list-add.png @@ -119,7 +119,7 @@ - + :/ico/16x16/list-remove.png:/ico/16x16/list-remove.png @@ -133,7 +133,7 @@ - + :/ico/16x16/go-down.png:/ico/16x16/go-down.png @@ -353,7 +353,7 @@ - + :/ico/16x16/folder-open.png:/ico/16x16/folder-open.png @@ -380,7 +380,7 @@ - + :/ico/16x16/document-save.png:/ico/16x16/document-save.png diff --git a/sources/dataBase/ui/summaryquerywidget.ui b/sources/dataBase/ui/summaryquerywidget.ui index d6eb50e10..de98cd343 100644 --- a/sources/dataBase/ui/summaryquerywidget.ui +++ b/sources/dataBase/ui/summaryquerywidget.ui @@ -76,7 +76,7 @@ - + :/ico/16x16/go-up.png:/ico/16x16/go-up.png @@ -87,7 +87,7 @@ - + :/ico/16x16/list-add.png:/ico/16x16/list-add.png @@ -98,7 +98,7 @@ - + :/ico/16x16/list-remove.png:/ico/16x16/list-remove.png @@ -109,7 +109,7 @@ - + :/ico/16x16/go-down.png:/ico/16x16/go-down.png diff --git a/sources/editor/ui/polygoneditor.ui b/sources/editor/ui/polygoneditor.ui index 98612f832..c34d26888 100644 --- a/sources/editor/ui/polygoneditor.ui +++ b/sources/editor/ui/polygoneditor.ui @@ -48,7 +48,7 @@ - + :/ico/16x16/list-add.png:/ico/16x16/list-add.png @@ -57,7 +57,7 @@ - + :/ico/16x16/list-remove.png:/ico/16x16/list-remove.png diff --git a/sources/editor/ui/qetelementeditor.ui b/sources/editor/ui/qetelementeditor.ui index 65a55aa8b..0ec31adc3 100644 --- a/sources/editor/ui/qetelementeditor.ui +++ b/sources/editor/ui/qetelementeditor.ui @@ -14,7 +14,7 @@ QElectroTech - Éditeur d'élément - + :/ico/16x16/qet.png:/ico/16x16/qet.png @@ -55,7 +55,7 @@ Coller depuis... - + :/ico/16x16/edit-paste.png:/ico/16x16/edit-paste.png @@ -202,7 +202,7 @@ - + :/ico/16x16/edit-select-all.png:/ico/16x16/edit-select-all.png @@ -211,7 +211,7 @@ - + :/ico/22x22/document-new.png:/ico/22x22/document-new.png @@ -220,7 +220,7 @@ - + :/ico/22x22/folder-open.png:/ico/22x22/folder-open.png @@ -229,7 +229,7 @@ - + :/ico/22x22/folder-open.png:/ico/22x22/folder-open.png @@ -238,7 +238,7 @@ - + :/ico/22x22/document-save.png:/ico/22x22/document-save.png @@ -247,7 +247,7 @@ - + :/ico/22x22/document-save-as.png:/ico/22x22/document-save-as.png @@ -256,7 +256,7 @@ - + :/ico/22x22/document-save.png:/ico/22x22/document-save.png @@ -265,7 +265,7 @@ - + :/ico/22x22/document-export.png:/ico/22x22/document-export.png @@ -274,7 +274,7 @@ - + :/ico/22x22/view-refresh.png:/ico/22x22/view-refresh.png @@ -283,7 +283,7 @@ - + :/ico/16x16/application-exit.png:/ico/16x16/application-exit.png @@ -295,7 +295,7 @@ - + :/ico/16x16/edit-select-none.png:/ico/16x16/edit-select-none.png @@ -304,7 +304,7 @@ - + :/ico/16x16/edit-cut.png:/ico/16x16/edit-cut.png @@ -313,7 +313,7 @@ - + :/ico/16x16/edit-copy.png:/ico/16x16/edit-copy.png @@ -322,7 +322,7 @@ - + :/ico/16x16/edit-paste.png:/ico/16x16/edit-paste.png @@ -331,7 +331,7 @@ - + :/ico/16x16/edit-paste.png:/ico/16x16/edit-paste.png @@ -340,7 +340,7 @@ - + :/ico/16x16/text-xml.png:/ico/16x16/text-xml.png @@ -349,7 +349,7 @@ - + :/ico/16x16/element.png:/ico/16x16/element.png @@ -358,7 +358,7 @@ - + :/ico/16x16/edit-select-invert.png:/ico/16x16/edit-select-invert.png @@ -367,7 +367,7 @@ - + :/ico/22x22/edit-delete.png:/ico/22x22/edit-delete.png @@ -376,7 +376,7 @@ - + :/ico/22x22/names.png:/ico/22x22/names.png @@ -385,7 +385,7 @@ - + :/ico/16x16/preferences-desktop-user.png:/ico/16x16/preferences-desktop-user.png @@ -394,7 +394,7 @@ - + :/ico/22x22/element-edit.png:/ico/22x22/element-edit.png @@ -403,7 +403,7 @@ - + :/ico/22x22/zoom-in.png:/ico/22x22/zoom-in.png @@ -412,7 +412,7 @@ - + :/ico/22x22/zoom-out.png:/ico/22x22/zoom-out.png @@ -421,7 +421,7 @@ - + :/ico/22x22/zoom-fit-best.png:/ico/22x22/zoom-fit-best.png @@ -430,7 +430,7 @@ - + :/ico/22x22/zoom-original.png:/ico/22x22/zoom-original.png @@ -439,7 +439,7 @@ - + :/ico/16x16/qet.png:/ico/16x16/qet.png @@ -451,7 +451,7 @@ - + :/ico/16x16/help-contents.png:/ico/16x16/help-contents.png @@ -463,7 +463,7 @@ - + :/ico/16x16/kdenlive-show-video.png:/ico/16x16/kdenlive-show-video.png @@ -475,7 +475,7 @@ - + :/ico/16x16/help-donate.png:/ico/16x16/help-donate.png @@ -487,7 +487,7 @@ - + :/ico/16x16/qt.png:/ico/16x16/qt.png @@ -499,7 +499,7 @@ - + :/ico/16x16/orientations.png:/ico/16x16/orientations.png @@ -508,7 +508,7 @@ - + :/ico/16x16/orientations.png:/ico/16x16/orientations.png @@ -517,7 +517,7 @@ - + :/ico/16x16/mirror.png:/ico/16x16/mirror.png @@ -526,7 +526,7 @@ - + :/ico/16x16/flip.png:/ico/16x16/flip.png @@ -535,7 +535,7 @@ - + :/ico/16x16/run-dxf.png:/ico/16x16/run-dxf.png @@ -544,7 +544,7 @@ - + :/ico/22x22/folder-open.png:/ico/22x22/folder-open.png diff --git a/sources/print/projectprintwindow.ui b/sources/print/projectprintwindow.ui index a1db8e411..c13399fb0 100644 --- a/sources/print/projectprintwindow.ui +++ b/sources/print/projectprintwindow.ui @@ -134,7 +134,7 @@ - + :/ico/16x16/dialog-ok.png:/ico/16x16/dialog-ok.png @@ -305,7 +305,7 @@ - + :/ico/22x22/view_fit_width.png:/ico/22x22/view_fit_width.png @@ -314,7 +314,7 @@ - + :/ico/22x22/view-fit-window.png:/ico/22x22/view-fit-window.png @@ -323,7 +323,7 @@ - + :/ico/16x16/zoom-out.png:/ico/16x16/zoom-out.png @@ -332,7 +332,7 @@ - + :/ico/16x16/zoom-in.png:/ico/16x16/zoom-in.png @@ -341,7 +341,7 @@ - + :/ico/22x22/landscape.png:/ico/22x22/landscape.png @@ -350,7 +350,7 @@ - + :/ico/22x22/portrait.png:/ico/22x22/portrait.png @@ -359,7 +359,7 @@ - + :/ico/16x16/arrow-left-double.png:/ico/16x16/arrow-left-double.png @@ -368,7 +368,7 @@ - + :/ico/16x16/arrow-left.png:/ico/16x16/arrow-left.png @@ -377,7 +377,7 @@ - + :/ico/16x16/arrow-right.png:/ico/16x16/arrow-right.png @@ -386,7 +386,7 @@ - + :/ico/22x22/arrow-right-double.png:/ico/22x22/arrow-right-double.png @@ -395,7 +395,7 @@ - + :/ico/22x22/single_page.png:/ico/22x22/single_page.png @@ -404,7 +404,7 @@ - + :/ico/22x22/two_pages.png:/ico/22x22/two_pages.png @@ -413,7 +413,7 @@ - + :/ico/22x22/all_pages.png:/ico/22x22/all_pages.png @@ -422,7 +422,7 @@ - + :/ico/48x48/document-print-frame.png:/ico/48x48/document-print-frame.png diff --git a/sources/qetapp.cpp b/sources/qetapp.cpp index 4f334c676..76c1203fc 100644 --- a/sources/qetapp.cpp +++ b/sources/qetapp.cpp @@ -120,6 +120,7 @@ QETApp::QETApp() : } initConfiguration(); initLanguage(); + initIconTheme(); QET::Icons::initIcons(); initFonts(); initStyle(); @@ -2330,6 +2331,22 @@ void QETApp::initFonts() } } +/** + @brief QETApp::initIconTheme + Register QET's icon theme "qet" (see misc/make_icon_themes.py and + ico/icon-themes.qrc) and make it the current theme, so + QIcon::fromTheme("name") resolves to QET's own icons on every + platform. Must run before QET::Icons::initIcons(), which looks icons + up by name. +*/ +void QETApp::initIconTheme() +{ + QStringList paths = QIcon::themeSearchPaths(); + paths.prepend(QStringLiteral(":/ico/themes")); + QIcon::setThemeSearchPaths(paths); + QIcon::setThemeName(QStringLiteral("qet")); +} + /** @brief QETApp::initStyle Setup the gui style diff --git a/sources/qetapp.h b/sources/qetapp.h index 7d0e5e7ee..8242abf71 100644 --- a/sources/qetapp.h +++ b/sources/qetapp.h @@ -286,6 +286,7 @@ class QETApp : public QObject void setSplashScreenStep(const QString & = QString()); void initLanguage(); void initFonts(); + void initIconTheme(); void initStyle(); void initConfiguration(); void initSystemTray(); diff --git a/sources/qetdiagrameditor.cpp b/sources/qetdiagrameditor.cpp index 247d889ad..d1a4be91b 100644 --- a/sources/qetdiagrameditor.cpp +++ b/sources/qetdiagrameditor.cpp @@ -430,7 +430,7 @@ void QETDiagramEditor::setUpActions() }); // Draw or not the custom guides - m_draw_guides = new QAction ( QIcon(":/ico/22x22/guides.png"), tr("Afficher les guides"), this); + m_draw_guides = new QAction ( QIcon::fromTheme("guides"), tr("Afficher les guides"), this); m_draw_guides->setStatusTip(tr("Affiche ou masque les guides")); m_draw_guides->setCheckable(true); m_draw_guides->setChecked(settings.value("diagrameditor/guides_display_startup", false).toBool()); diff --git a/sources/qetgraphicsitem/ViewItem/ui/graphicstablepropertieseditor.ui b/sources/qetgraphicsitem/ViewItem/ui/graphicstablepropertieseditor.ui index 439512405..28edea330 100644 --- a/sources/qetgraphicsitem/ViewItem/ui/graphicstablepropertieseditor.ui +++ b/sources/qetgraphicsitem/ViewItem/ui/graphicstablepropertieseditor.ui @@ -62,7 +62,7 @@ - + :/ico/16x16/arrow-right.png:/ico/16x16/arrow-right.png @@ -104,7 +104,7 @@ - + :/ico/22x22/zoom-fit-best.png:/ico/22x22/zoom-fit-best.png @@ -179,7 +179,7 @@ - + :/ico/16x16/arrow-left.png:/ico/16x16/arrow-left.png @@ -203,7 +203,7 @@ - + :/ico/22x22/all_pages.png:/ico/22x22/all_pages.png diff --git a/sources/qetgraphicsitem/ViewItem/ui/projectdbmodelpropertieswidget.ui b/sources/qetgraphicsitem/ViewItem/ui/projectdbmodelpropertieswidget.ui index 3ce470886..649e756c7 100644 --- a/sources/qetgraphicsitem/ViewItem/ui/projectdbmodelpropertieswidget.ui +++ b/sources/qetgraphicsitem/ViewItem/ui/projectdbmodelpropertieswidget.ui @@ -20,7 +20,7 @@ Requête - + :/ico/16x16/edit-rename.png:/ico/16x16/edit-rename.png @@ -31,7 +31,7 @@ Recharger - + :/ico/16x16/view-refresh.png:/ico/16x16/view-refresh.png diff --git a/sources/qeticons.cpp b/sources/qeticons.cpp index 309ef423c..39030275c 100644 --- a/sources/qeticons.cpp +++ b/sources/qeticons.cpp @@ -392,471 +392,401 @@ void QET::Icons::initIcons() bool rtl = QApplication::isRightToLeft(); QTransform reverse = QTransform().scale(-1, 1); - Add .addFile(":/ico/16x16/list-add.png"); - Add .addFile(":/ico/22x22/list-add.png"); - AddFolio .addFile(":/ico/16x16/folio-new.png"); - Allowed .addFile(":/ico/16x16/user-online.png"); - ApplicationExit .addFile(":/ico/16x16/application-exit.png"); - ApplicationExit .addFile(":/ico/22x22/application-exit.png"); - ArrowLeft .addFile(":/ico/16x16/arrow-left.png"); - ArrowLeft .addFile(":/ico/22x22/arrow-left.png"); - ArrowLeftDouble .addFile(":/ico/16x16/arrow-left-double.png"); - ArrowLeftDouble .addFile(":/ico/22x22/arrow-left-double.png"); - ArrowRight .addFile(":/ico/16x16/arrow-right.png"); - ArrowRight .addFile(":/ico/22x22/arrow-right.png"); - ArrowRightDouble .addFile(":/ico/16x16/arrow-right-double.png"); - ArrowRightDouble .addFile(":/ico/22x22/arrow-right-double.png"); - Autoconnect .addFile(":/ico/22x22/autoconnect.png"); - BringForward .addFile(":/ico/22x22/bring_forward.png"); - Cancel .addFile(":/ico/16x16/item-cancel.png"); - Company .addFile(":/ico/16x16/go-company.png"); - Company .addFile(":/ico/22x22/go-company.png"); - Conductor .addFile(":/ico/16x16/conductor.png"); - ConductorEdit .addFile(":/ico/16x16/conductor-edit.png"); - ConductorSettings .addFile(":/ico/16x16/conductor-reset.png"); - ConductorSettings .addFile(":/ico/22x22/conductor2.png"); - Configure .addFile(":/ico/16x16/configure.png"); - Configure .addFile(":/ico/22x22/configure.png"); - ConfigureToolbars .addFile(":/ico/16x16/configure-toolbars.png"); - ConfigureToolbars .addFile(":/ico/22x22/configure-toolbars.png"); - IC_CopyFile .addFile(":/ico/16x16/item-copy.png"); - DiagramAdd .addFile(":/ico/16x16/folio-new.png"); - DiagramAdd .addFile(":/ico/22x22/diagram_add.png"); - Diagram .addFile(":/ico/16x16/diagram.png"); - Diagram .addFile(":/ico/diagram.png"); - DiagramBg .addFile(":/ico/22x22/diagram_bg.png"); - DiagramDelete .addFile(":/ico/16x16/folio-delete.png"); - DiagramDelete .addFile(":/ico/22x22/diagram_del.png"); - DialogCancel .addFile(":/ico/16x16/dialog-cancel.png"); - DialogCancel .addFile(":/ico/22x22/dialog-cancel.png"); - DialogInformation .addFile(":/ico/16x16/folio-properties.png"); - DialogInformation .addFile(":/ico/22x22/dialog-information.png"); - DialogOk .addFile(":/ico/16x16/dialog-ok.png"); - DialogOk .addFile(":/ico/22x22/dialog-ok.png"); - DocumentClose .addFile(":/ico/16x16/document-close.png"); - DocumentClose .addFile(":/ico/22x22/document-close.png"); - DocumentExport .addFile(":/ico/16x16/document-export.png"); - DocumentExport .addFile(":/ico/22x22/document-export.png"); - DocumentSpreadsheet .addFile(":/ico/16x16/export-csv.png"); - DocumentExport .addFile(":/ico/128x128/document-export.png"); - DocumentImport .addFile(":/ico/16x16/document-import.png"); - DocumentImport .addFile(":/ico/22x22/document-import.png"); - DocumentNew .addFile(":/ico/16x16/document-new.png"); - DocumentNew .addFile(":/ico/22x22/document-new.png"); - DocumentOpen .addFile(":/ico/16x16/document-open.png"); - DocumentOpen .addFile(":/ico/22x22/document-open.png"); - DocumentOpenRecent .addFile(":/ico/16x16/document-open-recent.png"); - DocumentOpenRecent .addFile(":/ico/22x22/document-open-recent.png"); - DocumentPrint .addFile(":/ico/16x16/document-print.png"); - DocumentPrint .addFile(":/ico/22x22/document-print.png"); - DocumentPrintFrame .addFile(":/ico/16x16/document-print-frame.png"); - DocumentPrintFrame .addFile(":/ico/48x48/document-print-frame.png"); - DocumentSave .addFile(":/ico/16x16/document-save.png"); - DocumentSave .addFile(":/ico/22x22/document-save.png"); - DocumentSaveAll .addFile(":/ico/16x16/document-save-all.png"); - DocumentSaveAll .addFile(":/ico/22x22/document-save-all.png"); - DocumentSaveAs .addFile(":/ico/16x16/document-save-as.png"); - DocumentSaveAs .addFile(":/ico/22x22/document-save-as.png"); - East .addFile(":/ico/16x16/east.png"); - EditClear .addFile(":/ico/16x16/edit-clear.png"); - EditClear .addFile(":/ico/22x22/edit-clear.png"); - EditText .addFile(":/ico/22x22/names.png"); - adding_image .addFile(":/ico/22x22/insert-image.png"); - adding_pdf .addFile(":/ico/22x22/pdf-import.png"); + Add = QIcon::fromTheme("list-add"); + AddFolio = QIcon::fromTheme("folio-new"); + Allowed = QIcon::fromTheme("user-online"); + ApplicationExit = QIcon::fromTheme("application-exit"); + ArrowLeft = QIcon::fromTheme("arrow-left"); + ArrowLeftDouble = QIcon::fromTheme("arrow-left-double"); + ArrowRight = QIcon::fromTheme("arrow-right"); + ArrowRightDouble = QIcon::fromTheme("arrow-right-double"); + Autoconnect = QIcon::fromTheme("autoconnect"); + BringForward = QIcon::fromTheme("bring_forward"); + Cancel = QIcon::fromTheme("item-cancel"); + Company = QIcon::fromTheme("go-company"); + Conductor = QIcon::fromTheme("conductor"); + ConductorEdit = QIcon::fromTheme("conductor-edit"); + ConductorSettings = QIcon::fromTheme("conductor-reset"); + Configure = QIcon::fromTheme("configure"); + ConfigureToolbars = QIcon::fromTheme("configure-toolbars"); + IC_CopyFile = QIcon::fromTheme("item-copy"); + DiagramAdd = QIcon::fromTheme("folio-new"); + Diagram.addFile(":/ico/16x16/diagram.png"); + Diagram.addFile(":/ico/diagram.png"); + DiagramBg = QIcon::fromTheme("diagram_bg"); + DiagramDelete = QIcon::fromTheme("folio-delete"); + DialogCancel = QIcon::fromTheme("dialog-cancel"); + DialogInformation = QIcon::fromTheme("folio-properties"); + DialogOk = QIcon::fromTheme("dialog-ok"); + DocumentClose = QIcon::fromTheme("document-close"); + DocumentExport = QIcon::fromTheme("document-export"); + DocumentSpreadsheet = QIcon::fromTheme("export-csv"); + DocumentExport = QIcon::fromTheme("document-export"); + DocumentImport = QIcon::fromTheme("document-import"); + DocumentNew = QIcon::fromTheme("document-new"); + DocumentOpen = QIcon::fromTheme("document-open"); + DocumentOpenRecent = QIcon::fromTheme("document-open-recent"); + DocumentPrint = QIcon::fromTheme("document-print"); + DocumentPrintFrame = QIcon::fromTheme("document-print-frame"); + DocumentSave = QIcon::fromTheme("document-save"); + DocumentSaveAll = QIcon::fromTheme("document-save-all"); + DocumentSaveAs = QIcon::fromTheme("document-save-as"); + East = QIcon::fromTheme("east"); + EditClear = QIcon::fromTheme("edit-clear"); + EditText = QIcon::fromTheme("names"); + adding_image = QIcon::fromTheme("insert-image"); + adding_pdf = QIcon::fromTheme("pdf-import"); if (rtl) { - EditClearLocationBar.addPixmap(QPixmap(":/ico/16x16/edit-clear-locationbar-ltr.png").transformed(reverse)); - EditClearLocationBar.addPixmap(QPixmap(":/ico/22x22/edit-clear-locationbar-ltr.png").transformed(reverse)); + EditClearLocationBar.addPixmap(QIcon::fromTheme("edit-clear-locationbar-ltr").pixmap(16).transformed(reverse)); + EditClearLocationBar.addPixmap(QIcon::fromTheme("edit-clear-locationbar-ltr").pixmap(22).transformed(reverse)); } else { - EditClearLocationBar.addFile(":/ico/16x16/edit-clear-locationbar-ltr.png"); - EditClearLocationBar.addFile(":/ico/22x22/edit-clear-locationbar-ltr.png"); + EditClearLocationBar= QIcon::fromTheme("edit-clear-locationbar-ltr"); } - EditCopy .addFile(":/ico/16x16/edit-copy.png"); - EditCopy .addFile(":/ico/22x22/edit-copy.png"); - EditCut .addFile(":/ico/16x16/edit-cut.png"); - EditCut .addFile(":/ico/22x22/edit-cut.png"); - EditDelete .addFile(":/ico/16x16/edit-delete.png"); - EditDelete .addFile(":/ico/22x22/edit-delete.png"); - EditOpacity .addFile(":/ico/breeze-icons/scalable/apps/hidef/edit-opacity.svg"); - EditPaste .addFile(":/ico/22x22/edit-paste.png"); - EditPaste .addFile(":/ico/16x16/edit-paste.png"); + EditCopy = QIcon::fromTheme("edit-copy"); + EditCut = QIcon::fromTheme("edit-cut"); + EditDelete = QIcon::fromTheme("edit-delete"); + EditOpacity = QIcon::fromTheme("edit-opacity"); + EditPaste = QIcon::fromTheme("edit-paste"); if (rtl) { - EditRedo.addPixmap(QPixmap(":/ico/16x16/edit-redo.png").transformed(reverse)); - EditRedo.addPixmap(QPixmap(":/ico/22x22/edit-redo.png").transformed(reverse)); + EditRedo.addPixmap(QIcon::fromTheme("edit-redo").pixmap(16).transformed(reverse)); + EditRedo.addPixmap(QIcon::fromTheme("edit-redo").pixmap(22).transformed(reverse)); } else { - EditRedo .addFile(":/ico/16x16/edit-redo.png"); - EditRedo .addFile(":/ico/22x22/edit-redo.png"); + EditRedo = QIcon::fromTheme("edit-redo"); } - EditRename .addFile(":/ico/16x16/edit-rename.png"); - EditRename .addFile(":/ico/22x22/edit-rename.png"); - EditSelectAll .addFile(":/ico/16x16/edit-select-all.png"); - EditSelectAll .addFile(":/ico/22x22/edit-select-all.png"); - EditSelectInvert .addFile(":/ico/16x16/edit-select-invert.png"); - EditSelectNone .addFile(":/ico/16x16/edit-select-none.png"); - EditTableCellMerge .addFile(":/ico/16x16/edit-table-cell-merge.png"); - EditTableCellMerge .addFile(":/ico/22x22/edit-table-cell-merge.png"); - EditTableCellSplit .addFile(":/ico/16x16/edit-table-cell-split.png"); - EditTableCellSplit .addFile(":/ico/22x22/edit-table-cell-split.png"); - EditTableDeleteColumn .addFile(":/ico/16x16/edit-table-delete-column.png"); - EditTableDeleteColumn .addFile(":/ico/22x22/edit-table-delete-column.png"); - EditTableDeleteRow .addFile(":/ico/16x16/edit-table-delete-row.png"); - EditTableDeleteRow .addFile(":/ico/22x22/edit-table-delete-row.png"); - EditTableInsertColumnLeft .addFile(":/ico/16x16/edit-table-insert-column-left.png"); - EditTableInsertColumnLeft .addFile(":/ico/22x22/edit-table-insert-column-left.png"); - EditTableInsertColumnRight.addFile(":/ico/16x16/edit-table-insert-column-right.png"); - EditTableInsertColumnRight.addFile(":/ico/22x22/edit-table-insert-column-right.png"); - EditTableInsertRowAbove .addFile(":/ico/16x16/edit-table-insert-row-above.png"); - EditTableInsertRowAbove .addFile(":/ico/22x22/edit-table-insert-row-above.png"); - EditTableInsertRowUnder .addFile(":/ico/16x16/edit-table-insert-row-under.png"); - EditTableInsertRowUnder .addFile(":/ico/22x22/edit-table-insert-row-under.png"); + EditRename = QIcon::fromTheme("edit-rename"); + EditSelectAll = QIcon::fromTheme("edit-select-all"); + EditSelectInvert = QIcon::fromTheme("edit-select-invert"); + EditSelectNone = QIcon::fromTheme("edit-select-none"); + EditTableCellMerge = QIcon::fromTheme("edit-table-cell-merge"); + EditTableCellSplit = QIcon::fromTheme("edit-table-cell-split"); + EditTableDeleteColumn= QIcon::fromTheme("edit-table-delete-column"); + EditTableDeleteRow = QIcon::fromTheme("edit-table-delete-row"); + EditTableInsertColumnLeft= QIcon::fromTheme("edit-table-insert-column-left"); + EditTableInsertColumnRight= QIcon::fromTheme("edit-table-insert-column-right"); + EditTableInsertRowAbove= QIcon::fromTheme("edit-table-insert-row-above"); + EditTableInsertRowUnder= QIcon::fromTheme("edit-table-insert-row-under"); if (rtl) { - EditUndo.addPixmap(QPixmap(":/ico/16x16/edit-undo.png").transformed(reverse)); - EditUndo.addPixmap(QPixmap(":/ico/22x22/edit-undo.png").transformed(reverse)); + EditUndo.addPixmap(QIcon::fromTheme("edit-undo").pixmap(16).transformed(reverse)); + EditUndo.addPixmap(QIcon::fromTheme("edit-undo").pixmap(22).transformed(reverse)); } else { - EditUndo .addFile(":/ico/16x16/edit-undo.png"); - EditUndo .addFile(":/ico/22x22/edit-undo.png"); + EditUndo = QIcon::fromTheme("edit-undo"); } - Element .addFile(":/ico/breeze-icons/16x16/mimetypes/application-x-qet-element.png"); - Element .addFile(":/ico/breeze-icons/22x22/mimetypes/application-x-qet-element.png"); - Element .addFile(":/ico/breeze-icons/32x32/mimetypes/application-x-qet-element.png"); - ElementDelete .addFile(":/ico/16x16/element-delete.png"); - ElementDelete .addFile(":/ico/22x22/element-delete.png"); - ElementEdit .addFile(":/ico/16x16/element-edit.png"); - ElementEdit .addFile(":/ico/22x22/element-edit.png"); - ElementNew .addFile(":/ico/16x16/element-new.png"); - ElementNew .addFile(":/ico/22x22/element-new.png"); - ElementSmall .addFile(":/ico/16x16/element.png"); - EllipseToBezier .addFile(":/ico/generated/ellipse-to-bezier.svg"); - EndLineCircle .addFile(":/ico/16x16/endline-circle.png"); - EndLineDiamond .addFile(":/ico/16x16/endline-diamond.png"); - EndLineNone .addFile(":/ico/16x16/endline-none.png"); - EndLineSimple .addFile(":/ico/16x16/endline-simple.png"); - EndLineTriangle .addFile(":/ico/16x16/endline-triangle.png"); - EPS .addFile(":/ico/32x32/image-x-eps.png"); - Flip .addFile(":/ico/16x16/flip.png"); - Folder .addFile(":/ico/16x16/folder.png"); - FolderDelete .addFile(":/ico/16x16/folder-delete.png"); - FolderDelete .addFile(":/ico/22x22/folder-delete.png"); - FolderEdit .addFile(":/ico/16x16/folder-edit.png"); - FolderEdit .addFile(":/ico/22x22/folder-edit.png"); - FolderNew .addFile(":/ico/16x16/folder-new.png"); - FolderNew .addFile(":/ico/22x22/folder-new.png"); - FolderOnlyThis .addFile(":/ico/16x16/folder-only-this.png"); - FolderOpen .addFile(":/ico/16x16/folder-open.png"); - FolderOpen .addFile(":/ico/22x22/folder-open.png"); - FolderProperties .addFile(":/ico/16x16/folder-properties.png"); - FolderShowAll .addFile(":/ico/16x16/folder-show-all.png"); - Forbidden .addFile(":/ico/16x16/user-busy.png"); - FullScreenEnter .addFile(":/ico/16x16/view-fullscreen.png"); - FullScreenEnter .addFile(":/ico/22x22/view-fullscreen.png"); - FullScreenExit .addFile(":/ico/16x16/view-restore.png"); - FullScreenExit .addFile(":/ico/22x22/view-restore.png"); - GoBottom .addFile(":/ico/16x16/go-bottom.png"); - GoDown .addFile(":/ico/16x16/go-down.png"); - GoDown .addFile(":/ico/22x22/go-down.png"); - GoDownDouble .addFile(":/ico/16x16/go-down-double.png"); - GoTop .addFile(":/ico/16x16/go-top.png"); - GoUp .addFile(":/ico/16x16/go-up.png"); - GoUp .addFile(":/ico/22x22/go-up.png"); - GoUpDouble .addFile(":/ico/16x16/go-up-double.png"); - Ground .addFile(":/ico/16x16/ground.png"); - Grid .addFile(":/ico/16x16/grid.png"); - Grid .addFile(":/ico/22x22/grid.png"); - Hide .addFile(":/ico/16x16/masquer.png"); - Home .addFile(":/ico/16x16/go-home.png"); - Home .addFile(":/ico/22x22/go-home.png"); - HotSpot .addFile(":/ico/22x22/hotspot.png"); - ImageFlipHorizontal .addFile(":/ico/breeze-icons/scalable/apps/hidef/image-flip-horizontal-symbolic.svg"); - ImageFlipVertical .addFile(":/ico/breeze-icons/scalable/apps/hidef/image-flip-vertical-symbolic.svg"); - InsertImage .addFile(":/ico/22x22/insert-image.png"); - Lower .addFile(":/ico/22x22/lower.png"); - IC_MoveFile .addFile(":/ico/16x16/item-move.png"); - Mirror .addFile(":/ico/16x16/mirror.png"); - Names .addFile(":/ico/22x22/names.png"); - Neutral .addFile(":/ico/16x16/neutral.png"); - NewDiagram .addFile(":/ico/128x128/diagram.png"); - North .addFile(":/ico/16x16/north.png"); - ObjectLocked .addFile(":/ico/22x22/object-locked.png"); - ObjectRotateRight .addFile(":/ico/16x16/object-rotate-right.png"); - ObjectRotateRight .addFile(":/ico/22x22/object-rotate-right.png"); - ObjectUnlocked .addFile(":/ico/22x22/object-unlocked.png"); - Orientations .addFile(":/ico/16x16/orientations.png"); - PartArc .addFile(":/ico/22x22/arc.png"); - PartBezier .addFile(":/ico/breeze-icons/scalable/apps/hidef/draw-bezier-curves.svg"); - PartCircle .addFile(":/ico/16x16/circle.png"); - PartEllipse .addFile(":/ico/22x22/ellipse.png"); - PartLine .addFile(":/ico/22x22/line.png"); - PartPolygon .addFile(":/ico/22x22/polygon.png"); - PartRectangle .addFile(":/ico/22x22/rectangle.png"); - PartSelect .addFile(":/ico/16x16/select.png"); - PartSelect .addFile(":/ico/22x22/select.png"); - PartText .addFile(":/ico/22x22/text.png"); - PartTextField .addFile(":/ico/22x22/textfield.png"); - PDF .addFile(":/ico/32x32/application-pdf.png"); - Phase .addFile(":/ico/16x16/phase.png"); - PrintAllPages .addFile(":/ico/22x22/all_pages.png"); - Printer .addFile(":/ico/32x32/printer.png"); - Printer .addFile(":/ico/128x128/printer.png"); - PrintLandscape .addFile(":/ico/22x22/landscape.png"); - PrintPortrait .addFile(":/ico/22x22/portrait.png"); - PrintTwoPages .addFile(":/ico/22x22/two_pages.png"); - Projects .addFile(":/ico/128x128/project.png"); - ProjectClose .addFile(":/ico/16x16/project-close.png"); - ProjectClose .addFile(":/ico/22x22/project-close.png"); - ProjectNew .addFile(":/ico/16x16/project-new.png"); - ProjectNew .addFile(":/ico/22x22/project-new.png"); - ProjectFile .addFile(":/ico/breeze-icons/16x16/mimetypes/application-x-qet-project.png"); - ProjectFile .addFile(":/ico/breeze-icons/22x22/mimetypes/application-x-qet-project.png"); - ProjectFile .addFile(":/ico/breeze-icons/32x32/mimetypes/application-x-qet-project.png"); - ProjectProperties .addFile(":/ico/16x16/project-properties.png"); - QETDonate .addFile(":/ico/16x16/help-donate.png"); - QETDownload .addFile(":/ico/16x16/edit-download.png"); - QETIcon .addFile(":/ico/256x256/qelectrotech.png"); - ProjectFileGP .addFile(":/ico/16x16/project.png"); - QETLogo .addFile(":/ico/16x16/qet.png"); - QETManual .addFile(":/ico/16x16/help-contents.png"); - QETLogo .addFile(":/ico/256x256/qet.png"); - QETOxygenLogo .addFile(":/ico/breeze-icons/128x128/apps/qelectrotech.png"); - QETOxygenLogo .addFile(":/ico/breeze-icons/16x16/apps/qelectrotech.png"); - QETOxygenLogo .addFile(":/ico/breeze-icons/22x22/apps/qelectrotech.png"); - QETOxygenLogo .addFile(":/ico/breeze-icons/256x256/apps/qelectrotech.png"); - QETOxygenLogo .addFile(":/ico/breeze-icons/32x32/apps/qelectrotech.png"); - QETOxygenLogo .addFile(":/ico/breeze-icons/48x48/apps/qelectrotech.png"); - QETOxygenLogo .addFile(":/ico/breeze-icons/64x64/apps/qelectrotech.png"); - QETVideo .addFile(":/ico/16x16/kdenlive-show-video.png"); - QtLogo .addFile(":/ico/16x16/qt.png"); - Raise .addFile(":/ico/22x22/raise.png"); - RectToBezier .addFile(":/ico/generated/rect-to-bezier.svg"); - RectToPolyline .addFile(":/ico/generated/rect-to-polyline.svg"); - Remove .addFile(":/ico/16x16/list-remove.png"); - Remove .addFile(":/ico/22x22/list-remove.png"); - resize_image .addFile(":/ico/22x22/transform-scale.png"); - Restore .addFile(":/ico/22x22/restaurer.png"); - RunDxf .addFile(":/ico/16x16/run-dxf.png"); - SendBackward .addFile(":/ico/22x22/send_backward.png"); - Settings .addFile(":/ico/128x128/settings.png"); - SinglePage .addFile(":/ico/22x22/single_page.png"); - South .addFile(":/ico/16x16/south.png"); - Start .addFile(":/ico/22x22/start.png"); - TableOfContent .addFile(":/ico/16x16/table-of-content.png"); - Terminal .addFile(":/ico/22x22/terminal.png"); - TerminalStrip .addFile(":/ico/16x16/terminalstrip.png"); - TerminalStrip .addFile(":/ico/22x22/terminalstrip.png"); - TitleBlock .addFile(":/ico/16x16/label.png"); - TitleBlock .addFile(":/ico/22x22/label.png"); - TitleBlockBottom .addFile(":/ico/22x22/titleblock-bottom.png"); - TitleBlockRight .addFile(":/ico/22x22/titleblock-right.png"); - TransformCrop .addFile(":/ico/breeze-icons/scalable/apps/hidef/transform-crop.svg"); - TransformRotate .addFile(":/ico/16x16/transform-rotate.png"); - UserInformations .addFile(":/ico/16x16/preferences-desktop-user.png"); - UserInformations .addFile(":/ico/22x22/preferences-desktop-user.png"); - ViewFitWidth .addFile(":/ico/22x22/view_fit_width.png"); - ViewFitWindow .addFile(":/ico/22x22/view-fit-window.png"); - ViewMove .addFile(":/ico/16x16/move.png"); - ViewMove .addFile(":/ico/22x22/move.png"); + Element.addFile(":/ico/breeze-icons/16x16/mimetypes/application-x-qet-element.png"); + Element.addFile(":/ico/breeze-icons/22x22/mimetypes/application-x-qet-element.png"); + Element.addFile(":/ico/breeze-icons/32x32/mimetypes/application-x-qet-element.png"); + ElementDelete = QIcon::fromTheme("element-delete"); + ElementEdit = QIcon::fromTheme("element-edit"); + ElementNew = QIcon::fromTheme("element-new"); + ElementSmall = QIcon::fromTheme("element"); + EllipseToBezier = QIcon::fromTheme("ellipse-to-bezier"); + EndLineCircle = QIcon::fromTheme("endline-circle"); + EndLineDiamond = QIcon::fromTheme("endline-diamond"); + EndLineNone = QIcon::fromTheme("endline-none"); + EndLineSimple = QIcon::fromTheme("endline-simple"); + EndLineTriangle = QIcon::fromTheme("endline-triangle"); + EPS = QIcon::fromTheme("image-x-eps"); + Flip = QIcon::fromTheme("flip"); + Folder = QIcon::fromTheme("folder"); + FolderDelete = QIcon::fromTheme("folder-delete"); + FolderEdit = QIcon::fromTheme("folder-edit"); + FolderNew = QIcon::fromTheme("folder-new"); + FolderOnlyThis = QIcon::fromTheme("folder-only-this"); + FolderOpen = QIcon::fromTheme("folder-open"); + FolderProperties = QIcon::fromTheme("folder-properties"); + FolderShowAll = QIcon::fromTheme("folder-show-all"); + Forbidden = QIcon::fromTheme("user-busy"); + FullScreenEnter = QIcon::fromTheme("view-fullscreen"); + FullScreenExit = QIcon::fromTheme("view-restore"); + GoBottom = QIcon::fromTheme("go-bottom"); + GoDown = QIcon::fromTheme("go-down"); + GoDownDouble = QIcon::fromTheme("go-down-double"); + GoTop = QIcon::fromTheme("go-top"); + GoUp = QIcon::fromTheme("go-up"); + GoUpDouble = QIcon::fromTheme("go-up-double"); + Ground = QIcon::fromTheme("ground"); + Grid = QIcon::fromTheme("grid"); + Hide = QIcon::fromTheme("masquer"); + Home = QIcon::fromTheme("go-home"); + HotSpot = QIcon::fromTheme("hotspot"); + ImageFlipHorizontal = QIcon::fromTheme("image-flip-horizontal"); + ImageFlipVertical = QIcon::fromTheme("image-flip-vertical"); + InsertImage = QIcon::fromTheme("insert-image"); + Lower = QIcon::fromTheme("lower"); + IC_MoveFile = QIcon::fromTheme("item-move"); + Mirror = QIcon::fromTheme("mirror"); + Names = QIcon::fromTheme("names"); + Neutral = QIcon::fromTheme("neutral"); + NewDiagram = QIcon::fromTheme("diagram"); + North = QIcon::fromTheme("north"); + ObjectLocked = QIcon::fromTheme("object-locked"); + ObjectRotateRight = QIcon::fromTheme("object-rotate-right"); + ObjectUnlocked = QIcon::fromTheme("object-unlocked"); + Orientations = QIcon::fromTheme("orientations"); + PartArc = QIcon::fromTheme("arc"); + PartBezier = QIcon::fromTheme("draw-bezier-curves"); + PartCircle = QIcon::fromTheme("circle"); + PartEllipse = QIcon::fromTheme("ellipse"); + PartLine = QIcon::fromTheme("line"); + PartPolygon = QIcon::fromTheme("polygon"); + PartRectangle = QIcon::fromTheme("rectangle"); + PartSelect = QIcon::fromTheme("select"); + PartText = QIcon::fromTheme("text"); + PartTextField = QIcon::fromTheme("textfield"); + PDF = QIcon::fromTheme("application-pdf"); + Phase = QIcon::fromTheme("phase"); + PrintAllPages = QIcon::fromTheme("all_pages"); + Printer = QIcon::fromTheme("printer"); + PrintLandscape = QIcon::fromTheme("landscape"); + PrintPortrait = QIcon::fromTheme("portrait"); + PrintTwoPages = QIcon::fromTheme("two_pages"); + Projects = QIcon::fromTheme("project"); + ProjectClose = QIcon::fromTheme("project-close"); + ProjectNew = QIcon::fromTheme("project-new"); + ProjectFile.addFile(":/ico/breeze-icons/16x16/mimetypes/application-x-qet-project.png"); + ProjectFile.addFile(":/ico/breeze-icons/22x22/mimetypes/application-x-qet-project.png"); + ProjectFile.addFile(":/ico/breeze-icons/32x32/mimetypes/application-x-qet-project.png"); + ProjectProperties = QIcon::fromTheme("project-properties"); + QETDonate = QIcon::fromTheme("help-donate"); + QETDownload = QIcon::fromTheme("edit-download"); + QETIcon.addFile(":/ico/256x256/qelectrotech.png"); + // The elements panel shows the project root with this icon in a 50 px + // slot meant for element previews. The theme name "project" also + // carries the 128 px file used by the configuration dialog, and on a + // 2x display Qt's theme loader would pick it and fill the slot. Load + // the two small files directly so the item stays at 16/22 px. + ProjectFileGP.addFile(":/ico/themes/qet/16x16/project.png"); + ProjectFileGP.addFile(":/ico/themes/qet/22x22/project.png"); + QETLogo = QIcon::fromTheme("qet"); + QETManual = QIcon::fromTheme("help-contents"); + QETLogo.addFile(":/ico/256x256/qet.png"); + QETOxygenLogo.addFile(":/ico/breeze-icons/128x128/apps/qelectrotech.png"); + QETOxygenLogo.addFile(":/ico/breeze-icons/16x16/apps/qelectrotech.png"); + QETOxygenLogo.addFile(":/ico/breeze-icons/22x22/apps/qelectrotech.png"); + QETOxygenLogo.addFile(":/ico/breeze-icons/256x256/apps/qelectrotech.png"); + QETOxygenLogo.addFile(":/ico/breeze-icons/32x32/apps/qelectrotech.png"); + QETOxygenLogo.addFile(":/ico/breeze-icons/48x48/apps/qelectrotech.png"); + QETOxygenLogo.addFile(":/ico/breeze-icons/64x64/apps/qelectrotech.png"); + QETVideo = QIcon::fromTheme("kdenlive-show-video"); + QtLogo = QIcon::fromTheme("qt"); + Raise = QIcon::fromTheme("raise"); + RectToBezier = QIcon::fromTheme("rect-to-bezier"); + RectToPolyline = QIcon::fromTheme("rect-to-polyline"); + Remove = QIcon::fromTheme("list-remove"); + resize_image = QIcon::fromTheme("transform-scale"); + Restore = QIcon::fromTheme("restaurer"); + RunDxf = QIcon::fromTheme("run-dxf"); + SendBackward = QIcon::fromTheme("send_backward"); + Settings = QIcon::fromTheme("settings"); + SinglePage = QIcon::fromTheme("single_page"); + South = QIcon::fromTheme("south"); + Start = QIcon::fromTheme("start"); + TableOfContent = QIcon::fromTheme("table-of-content"); + Terminal = QIcon::fromTheme("terminal"); + TerminalStrip = QIcon::fromTheme("terminalstrip"); + TitleBlock = QIcon::fromTheme("label"); + TitleBlockBottom = QIcon::fromTheme("titleblock-bottom"); + TitleBlockRight = QIcon::fromTheme("titleblock-right"); + TransformCrop = QIcon::fromTheme("transform-crop"); + TransformRotate = QIcon::fromTheme("transform-rotate"); + UserInformations = QIcon::fromTheme("preferences-desktop-user"); + ViewFitWidth = QIcon::fromTheme("view_fit_width"); + ViewFitWindow = QIcon::fromTheme("view-fit-window"); + ViewMove = QIcon::fromTheme("move"); if (rtl) { - ViewRefresh.addPixmap(QPixmap(":/ico/16x16/view-refresh.png").transformed(reverse)); - ViewRefresh.addPixmap(QPixmap(":/ico/22x22/view-refresh.png").transformed(reverse)); + ViewRefresh.addPixmap(QIcon::fromTheme("view-refresh").pixmap(16).transformed(reverse)); + ViewRefresh.addPixmap(QIcon::fromTheme("view-refresh").pixmap(22).transformed(reverse)); } else { - ViewRefresh .addFile(":/ico/16x16/view-refresh.png"); - ViewRefresh .addFile(":/ico/22x22/view-refresh.png"); + ViewRefresh = QIcon::fromTheme("view-refresh"); } - West .addFile(":/ico/16x16/west.png"); - WindowNew .addFile(":/ico/16x16/window-new.png"); - WindowNew .addFile(":/ico/22x22/window-new.png"); - XmlTextFile .addFile(":/ico/16x16/text-xml.png"); - XmlTextFile .addFile(":/ico/22x22/text-xml.png"); - XmlTextFile .addFile(":/ico/32x32/text-xml.png"); - ZoomDraw .addFile(":/ico/16x16/zoom-draw.png"); - ZoomDraw .addFile(":/ico/22x22/zoom-draw.png"); - ZoomFitBest .addFile(":/ico/16x16/zoom-fit-best.png"); - ZoomFitBest .addFile(":/ico/22x22/zoom-fit-best.png"); - ZoomIn .addFile(":/ico/16x16/zoom-in.png"); - ZoomIn .addFile(":/ico/22x22/zoom-in.png"); - ZoomOriginal .addFile(":/ico/16x16/zoom-original.png"); - ZoomOriginal .addFile(":/ico/22x22/zoom-original.png"); - ZoomOut .addFile(":/ico/16x16/zoom-out.png"); - ZoomOut .addFile(":/ico/22x22/zoom-out.png"); - sa .addFile(":/ico/24x16/sa.png"); - br .addFile(":/ico/24x16/br.png"); - catalonia .addFile(":/ico/24x16/catalonia.png"); - cs .addFile(":/ico/24x16/cs.png"); - da .addFile(":/ico/24x16/dk.png"); - de .addFile(":/ico/24x16/de.png"); - gr .addFile(":/ico/24x16/gr.png"); - en .addFile(":/ico/24x16/en.png"); - es .addFile(":/ico/24x16/es.png"); - fr .addFile(":/ico/24x16/fr.png"); - hr .addFile(":/ico/24x16/hr.png"); - hu .addFile(":/ico/24x16/hu.png"); - it .addFile(":/ico/24x16/it.png"); - jp .addFile(":/ico/24x16/jp.png"); - ko .addFile(":/ico/24x16/kr.png"); - pl .addFile(":/ico/24x16/pl.png"); - pt .addFile(":/ico/24x16/pt.png"); - ro .addFile(":/ico/24x16/ro.png"); - ru .addFile(":/ico/24x16/ru.png"); - sl .addFile(":/ico/24x16/sl.png"); - nl .addFile(":/ico/24x16/nl.png"); - no .addFile(":/ico/24x16/no.png"); - nl_BE .addFile(":/ico/24x16/nl_BE.png"); - tr .addFile(":/ico/24x16/tr.png"); - uk .addFile(":/ico/24x16/ua.png"); - mn .addFile(":/ico/24x16/mn.png"); - zh .addFile(":/ico/24x16/zh.png"); - se .addFile(":/ico/24x16/se.png"); - translation .addFile(":/ico/22x22/applications-development-translation.png"); - listDrawings .addFile(":/ico/48x48/view-pim-journal.png"); - AutoNum .addFile(":/ico/128x128/plasmagik.png"); - sub .addFile(":/ico/22x22/format-text-subscript.png"); - super .addFile(":/ico/22x22/format-text-superscript.png"); - textGroup .addFile(":/ico/16x16/object-group.png"); - ElementMaster .addFile(":/ico/16x16/element-master.png"); - ElementSlave .addFile(":/ico/16x16/element-slave.png"); - FolioXrefComing .addFile(":/ico/16x16/folio-ref-coming.png"); - ElementTerminal .addFile(":/ico/16x16/terminal.png"); - ColorBrown .addFile(":/ico/22x22/color/color-brown.png"); - ColorGray .addFile(":/ico/22x22/color/color-gray.png"); - ColorOrange .addFile(":/ico/22x22/color/color-orange.png"); - ColorPurple .addFile(":/ico/22x22/color/color-purple.png"); - ColorHTMLPinkPink .addFile(":/ico/22x22/color/color-01-pink-01-pink.png"); - ColorHTMLPinkLightPink .addFile(":/ico/22x22/color/color-01-pink-02-lightpink.png"); - ColorHTMLPinkHotPink .addFile(":/ico/22x22/color/color-01-pink-03-hotpink.png"); - ColorHTMLPinkDeepPink .addFile(":/ico/22x22/color/color-01-pink-04-deeppink.png"); - ColorHTMLPinkPaleVioletRed .addFile(":/ico/22x22/color/color-01-pink-05-palevioletred.png"); - ColorHTMLPinkMediumVioletRed .addFile(":/ico/22x22/color/color-01-pink-06-mediumvioletred.png"); - ColorHTMLRedLightSalmon .addFile(":/ico/22x22/color/color-02-red-01-lightsalmon.png"); - ColorHTMLRedSalmon .addFile(":/ico/22x22/color/color-02-red-02-salmon.png"); - ColorHTMLRedDarkSalmon .addFile(":/ico/22x22/color/color-02-red-03-darksalmon.png"); - ColorHTMLRedLightCoral .addFile(":/ico/22x22/color/color-02-red-04-lightcoral.png"); - ColorHTMLRedIndianRed .addFile(":/ico/22x22/color/color-02-red-05-indianred.png"); - ColorHTMLRedCrimson .addFile(":/ico/22x22/color/color-02-red-06-crimson.png"); - ColorHTMLRedFirebrick .addFile(":/ico/22x22/color/color-02-red-07-firebrick.png"); - ColorHTMLRedDarkRed .addFile(":/ico/22x22/color/color-02-red-08-darkred.png"); - ColorHTMLRedRed .addFile(":/ico/22x22/color/color-02-red-09-red.png"); - ColorHTMLOrangeOrangeRed .addFile(":/ico/22x22/color/color-03-orange-01-orangered.png"); - ColorHTMLOrangeTomato .addFile(":/ico/22x22/color/color-03-orange-02-tomato.png"); - ColorHTMLOrangeCoral .addFile(":/ico/22x22/color/color-03-orange-03-coral.png"); - ColorHTMLOrangeDarkOrange .addFile(":/ico/22x22/color/color-03-orange-04-darkorange.png"); - ColorHTMLOrangeOrange .addFile(":/ico/22x22/color/color-03-orange-05-orange.png"); - ColorHTMLYellowYellow .addFile(":/ico/22x22/color/color-04-yellow-01-yellow.png"); - ColorHTMLYellowLightYellow .addFile(":/ico/22x22/color/color-04-yellow-02-lightyellow.png"); - ColorHTMLYellowLemonChiffon .addFile(":/ico/22x22/color/color-04-yellow-03-lemonchiffon.png"); + West = QIcon::fromTheme("west"); + WindowNew = QIcon::fromTheme("window-new"); + XmlTextFile = QIcon::fromTheme("text-xml"); + ZoomDraw = QIcon::fromTheme("zoom-draw"); + ZoomFitBest = QIcon::fromTheme("zoom-fit-best"); + ZoomIn = QIcon::fromTheme("zoom-in"); + ZoomOriginal = QIcon::fromTheme("zoom-original"); + ZoomOut = QIcon::fromTheme("zoom-out"); + sa.addFile(":/ico/24x16/sa.png"); + br.addFile(":/ico/24x16/br.png"); + catalonia.addFile(":/ico/24x16/catalonia.png"); + cs.addFile(":/ico/24x16/cs.png"); + da.addFile(":/ico/24x16/dk.png"); + de.addFile(":/ico/24x16/de.png"); + gr.addFile(":/ico/24x16/gr.png"); + en.addFile(":/ico/24x16/en.png"); + es.addFile(":/ico/24x16/es.png"); + fr.addFile(":/ico/24x16/fr.png"); + hr.addFile(":/ico/24x16/hr.png"); + hu.addFile(":/ico/24x16/hu.png"); + it.addFile(":/ico/24x16/it.png"); + jp.addFile(":/ico/24x16/jp.png"); + ko.addFile(":/ico/24x16/kr.png"); + pl.addFile(":/ico/24x16/pl.png"); + pt.addFile(":/ico/24x16/pt.png"); + ro.addFile(":/ico/24x16/ro.png"); + ru.addFile(":/ico/24x16/ru.png"); + sl.addFile(":/ico/24x16/sl.png"); + nl.addFile(":/ico/24x16/nl.png"); + no.addFile(":/ico/24x16/no.png"); + nl_BE.addFile(":/ico/24x16/nl_BE.png"); + tr.addFile(":/ico/24x16/tr.png"); + uk.addFile(":/ico/24x16/ua.png"); + mn.addFile(":/ico/24x16/mn.png"); + zh.addFile(":/ico/24x16/zh.png"); + se.addFile(":/ico/24x16/se.png"); + translation = QIcon::fromTheme("applications-development-translation"); + listDrawings = QIcon::fromTheme("view-pim-journal"); + AutoNum = QIcon::fromTheme("plasmagik"); + sub = QIcon::fromTheme("format-text-subscript"); + super = QIcon::fromTheme("format-text-superscript"); + textGroup = QIcon::fromTheme("object-group"); + ElementMaster = QIcon::fromTheme("element-master"); + ElementSlave = QIcon::fromTheme("element-slave"); + FolioXrefComing = QIcon::fromTheme("folio-ref-coming"); + ElementTerminal = QIcon::fromTheme("terminal"); + ColorBrown.addFile(":/ico/22x22/color/color-brown.png"); + ColorGray.addFile(":/ico/22x22/color/color-gray.png"); + ColorOrange.addFile(":/ico/22x22/color/color-orange.png"); + ColorPurple.addFile(":/ico/22x22/color/color-purple.png"); + ColorHTMLPinkPink.addFile(":/ico/22x22/color/color-01-pink-01-pink.png"); + ColorHTMLPinkLightPink.addFile(":/ico/22x22/color/color-01-pink-02-lightpink.png"); + ColorHTMLPinkHotPink.addFile(":/ico/22x22/color/color-01-pink-03-hotpink.png"); + ColorHTMLPinkDeepPink.addFile(":/ico/22x22/color/color-01-pink-04-deeppink.png"); + ColorHTMLPinkPaleVioletRed.addFile(":/ico/22x22/color/color-01-pink-05-palevioletred.png"); + ColorHTMLPinkMediumVioletRed.addFile(":/ico/22x22/color/color-01-pink-06-mediumvioletred.png"); + ColorHTMLRedLightSalmon.addFile(":/ico/22x22/color/color-02-red-01-lightsalmon.png"); + ColorHTMLRedSalmon.addFile(":/ico/22x22/color/color-02-red-02-salmon.png"); + ColorHTMLRedDarkSalmon.addFile(":/ico/22x22/color/color-02-red-03-darksalmon.png"); + ColorHTMLRedLightCoral.addFile(":/ico/22x22/color/color-02-red-04-lightcoral.png"); + ColorHTMLRedIndianRed.addFile(":/ico/22x22/color/color-02-red-05-indianred.png"); + ColorHTMLRedCrimson.addFile(":/ico/22x22/color/color-02-red-06-crimson.png"); + ColorHTMLRedFirebrick.addFile(":/ico/22x22/color/color-02-red-07-firebrick.png"); + ColorHTMLRedDarkRed.addFile(":/ico/22x22/color/color-02-red-08-darkred.png"); + ColorHTMLRedRed.addFile(":/ico/22x22/color/color-02-red-09-red.png"); + ColorHTMLOrangeOrangeRed.addFile(":/ico/22x22/color/color-03-orange-01-orangered.png"); + ColorHTMLOrangeTomato.addFile(":/ico/22x22/color/color-03-orange-02-tomato.png"); + ColorHTMLOrangeCoral.addFile(":/ico/22x22/color/color-03-orange-03-coral.png"); + ColorHTMLOrangeDarkOrange.addFile(":/ico/22x22/color/color-03-orange-04-darkorange.png"); + ColorHTMLOrangeOrange.addFile(":/ico/22x22/color/color-03-orange-05-orange.png"); + ColorHTMLYellowYellow.addFile(":/ico/22x22/color/color-04-yellow-01-yellow.png"); + ColorHTMLYellowLightYellow.addFile(":/ico/22x22/color/color-04-yellow-02-lightyellow.png"); + ColorHTMLYellowLemonChiffon.addFile(":/ico/22x22/color/color-04-yellow-03-lemonchiffon.png"); ColorHTMLYellowLightGoldenrodYellow.addFile(":/ico/22x22/color/color-04-yellow-04-lightgoldenrodyellow.png"); - ColorHTMLYellowPapayaWhip .addFile(":/ico/22x22/color/color-04-yellow-05-papayawhip.png"); - ColorHTMLYellowMoccasin .addFile(":/ico/22x22/color/color-04-yellow-06-moccasin.png"); - ColorHTMLYellowPeachPuff .addFile(":/ico/22x22/color/color-04-yellow-07-peachpuff.png"); - ColorHTMLYellowPaleGoldenrod .addFile(":/ico/22x22/color/color-04-yellow-08-palegoldenrod.png"); - ColorHTMLYellowKhaki .addFile(":/ico/22x22/color/color-04-yellow-09-khaki.png"); - ColorHTMLYellowDarkKhaki .addFile(":/ico/22x22/color/color-04-yellow-10-darkkhaki.png"); - ColorHTMLYellowGold .addFile(":/ico/22x22/color/color-04-yellow-11-gold.png"); - ColorHTMLBrownCornsilk .addFile(":/ico/22x22/color/color-05-brown-01-cornsilk.png"); - ColorHTMLBrownBlanchedAlmond .addFile(":/ico/22x22/color/color-05-brown-02-blanchedalmond.png"); - ColorHTMLBrownBisque .addFile(":/ico/22x22/color/color-05-brown-03-bisque.png"); - ColorHTMLBrownNavajoWhite .addFile(":/ico/22x22/color/color-05-brown-04-navajowhite.png"); - ColorHTMLBrownWheat .addFile(":/ico/22x22/color/color-05-brown-05-wheat.png"); - ColorHTMLBrownBurlywood .addFile(":/ico/22x22/color/color-05-brown-06-burlywood.png"); - ColorHTMLBrownTan .addFile(":/ico/22x22/color/color-05-brown-07-tan.png"); - ColorHTMLBrownRosyBrown .addFile(":/ico/22x22/color/color-05-brown-08-rosybrown.png"); - ColorHTMLBrownSandyBrown .addFile(":/ico/22x22/color/color-05-brown-09-sandybrown.png"); - ColorHTMLBrownGoldenrod .addFile(":/ico/22x22/color/color-05-brown-10-goldenrod.png"); - ColorHTMLBrownDarkGoldenrod .addFile(":/ico/22x22/color/color-05-brown-11-darkgoldenrod.png"); - ColorHTMLBrownPeru .addFile(":/ico/22x22/color/color-05-brown-12-peru.png"); - ColorHTMLBrownChocolate .addFile(":/ico/22x22/color/color-05-brown-13-chocolate.png"); - ColorHTMLBrownSaddleBrown .addFile(":/ico/22x22/color/color-05-brown-14-saddlebrown.png"); - ColorHTMLBrownSienna .addFile(":/ico/22x22/color/color-05-brown-15-sienna.png"); - ColorHTMLBrownBrown .addFile(":/ico/22x22/color/color-05-brown-16-brown.png"); - ColorHTMLBrownMaroon .addFile(":/ico/22x22/color/color-05-brown-17-maroon.png"); - ColorHTMLGreenDarkOliveGreen .addFile(":/ico/22x22/color/color-06-green-01-darkolivegreen.png"); - ColorHTMLGreenOlive .addFile(":/ico/22x22/color/color-06-green-02-olive.png"); - ColorHTMLGreenOliveDrab .addFile(":/ico/22x22/color/color-06-green-03-olivedrab.png"); - ColorHTMLGreenYellowGreen .addFile(":/ico/22x22/color/color-06-green-04-yellowgreen.png"); - ColorHTMLGreenLimeGreen .addFile(":/ico/22x22/color/color-06-green-05-limegreen.png"); - ColorHTMLGreenLime .addFile(":/ico/22x22/color/color-06-green-06-lime.png"); - ColorHTMLGreenLawnGreen .addFile(":/ico/22x22/color/color-06-green-07-lawngreen.png"); - ColorHTMLGreenChartreuse .addFile(":/ico/22x22/color/color-06-green-08-chartreuse.png"); - ColorHTMLGreenGreenYellow .addFile(":/ico/22x22/color/color-06-green-09-greenyellow.png"); - ColorHTMLGreenSpringGreen .addFile(":/ico/22x22/color/color-06-green-10-springgreen.png"); - ColorHTMLGreenMediumSpringGreen .addFile(":/ico/22x22/color/color-06-green-11-mediumspringgreen.png"); - ColorHTMLGreenLightGreen .addFile(":/ico/22x22/color/color-06-green-12-lightgreen.png"); - ColorHTMLGreenPaleGreen .addFile(":/ico/22x22/color/color-06-green-13-palegreen.png"); - ColorHTMLGreenDarkSeaGreen .addFile(":/ico/22x22/color/color-06-green-14-darkseagreen.png"); - ColorHTMLGreenMediumAquamarine .addFile(":/ico/22x22/color/color-06-green-15-mediumaquamarine.png"); - ColorHTMLGreenMediumSeaGreen .addFile(":/ico/22x22/color/color-06-green-16-mediumseagreen.png"); - ColorHTMLGreenSeaGreen .addFile(":/ico/22x22/color/color-06-green-17-seagreen.png"); - ColorHTMLGreenForestGreen .addFile(":/ico/22x22/color/color-06-green-18-forestgreen.png"); - ColorHTMLGreenGreen .addFile(":/ico/22x22/color/color-06-green-19-green.png"); - ColorHTMLGreenDarkGreen .addFile(":/ico/22x22/color/color-06-green-20-darkgreen.png"); - ColorHTMLCyanAqua .addFile(":/ico/22x22/color/color-07-cyan-01-aqua.png"); - ColorHTMLCyanCyan .addFile(":/ico/22x22/color/color-07-cyan-02-cyan.png"); - ColorHTMLCyanLightCyan .addFile(":/ico/22x22/color/color-07-cyan-03-lightcyan.png"); - ColorHTMLCyanPaleTurquoise .addFile(":/ico/22x22/color/color-07-cyan-04-paleturquoise.png"); - ColorHTMLCyanAquamarine .addFile(":/ico/22x22/color/color-07-cyan-05-aquamarine.png"); - ColorHTMLCyanTurquoise .addFile(":/ico/22x22/color/color-07-cyan-06-turquoise.png"); - ColorHTMLCyanMediumTurquoise .addFile(":/ico/22x22/color/color-07-cyan-07-mediumturquoise.png"); - ColorHTMLCyanDarkTurquoise .addFile(":/ico/22x22/color/color-07-cyan-08-darkturquoise.png"); - ColorHTMLCyanLightSeaGreen .addFile(":/ico/22x22/color/color-07-cyan-09-lightseagreen.png"); - ColorHTMLCyanCadetBlue .addFile(":/ico/22x22/color/color-07-cyan-10-cadetblue.png"); - ColorHTMLCyanDarkCyan .addFile(":/ico/22x22/color/color-07-cyan-11-darkcyan.png"); - ColorHTMLCyanTeal .addFile(":/ico/22x22/color/color-07-cyan-12-teal.png"); - ColorHTMLBlueLightSteelBlue .addFile(":/ico/22x22/color/color-08-blue-01-lightsteelblue.png"); - ColorHTMLBluePowderBlue .addFile(":/ico/22x22/color/color-08-blue-02-powderblue.png"); - ColorHTMLBlueLightBlue .addFile(":/ico/22x22/color/color-08-blue-03-lightblue.png"); - ColorHTMLBlueSkyBlue .addFile(":/ico/22x22/color/color-08-blue-04-skyblue.png"); - ColorHTMLBlueLightSkyBlue .addFile(":/ico/22x22/color/color-08-blue-05-lightskyblue.png"); - ColorHTMLBlueDeepSkyBlue .addFile(":/ico/22x22/color/color-08-blue-06-deepskyblue.png"); - ColorHTMLBlueDodgerBlue .addFile(":/ico/22x22/color/color-08-blue-07-dodgerblue.png"); - ColorHTMLBlueCornflowerBlue .addFile(":/ico/22x22/color/color-08-blue-08-cornflowerblue.png"); - ColorHTMLBlueSteelBlue .addFile(":/ico/22x22/color/color-08-blue-09-steelblue.png"); - ColorHTMLBlueRoyalBlue .addFile(":/ico/22x22/color/color-08-blue-10-royalblue.png"); - ColorHTMLBlueBlue .addFile(":/ico/22x22/color/color-08-blue-11-blue.png"); - ColorHTMLBlueMediumBlue .addFile(":/ico/22x22/color/color-08-blue-12-mediumblue.png"); - ColorHTMLBlueDarkBlue .addFile(":/ico/22x22/color/color-08-blue-13-darkblue.png"); - ColorHTMLBlueNavy .addFile(":/ico/22x22/color/color-08-blue-14-navy.png"); - ColorHTMLBlueMidnightBlue .addFile(":/ico/22x22/color/color-08-blue-15-midnightblue.png"); - ColorHTMLPurpleLavender .addFile(":/ico/22x22/color/color-09-purple-01-lavender.png"); - ColorHTMLPurpleThistle .addFile(":/ico/22x22/color/color-09-purple-02-thistle.png"); - ColorHTMLPurplePlum .addFile(":/ico/22x22/color/color-09-purple-03-plum.png"); - ColorHTMLPurpleViolet .addFile(":/ico/22x22/color/color-09-purple-04-violet.png"); - ColorHTMLPurpleOrchid .addFile(":/ico/22x22/color/color-09-purple-05-orchid.png"); - ColorHTMLPurpleFuchsia .addFile(":/ico/22x22/color/color-09-purple-06-fuchsia.png"); - ColorHTMLPurpleMagenta .addFile(":/ico/22x22/color/color-09-purple-07-magenta.png"); - ColorHTMLPurpleMediumOrchid .addFile(":/ico/22x22/color/color-09-purple-08-mediumorchid.png"); - ColorHTMLPurpleMediumPurple .addFile(":/ico/22x22/color/color-09-purple-09-mediumpurple.png"); - ColorHTMLPurpleBlueViolet .addFile(":/ico/22x22/color/color-09-purple-10-blueviolet.png"); - ColorHTMLPurpleDarkViolet .addFile(":/ico/22x22/color/color-09-purple-11-darkviolet.png"); - ColorHTMLPurpleDarkOrchid .addFile(":/ico/22x22/color/color-09-purple-12-darkorchid.png"); - ColorHTMLPurpleDarkMagenta .addFile(":/ico/22x22/color/color-09-purple-13-darkmagenta.png"); - ColorHTMLPurplePurple .addFile(":/ico/22x22/color/color-09-purple-14-purple.png"); - ColorHTMLPurpleIndigo .addFile(":/ico/22x22/color/color-09-purple-15-indigo.png"); - ColorHTMLPurpleDarkSlateBlue .addFile(":/ico/22x22/color/color-09-purple-16-darkslateblue.png"); - ColorHTMLPurpleSlateBlue .addFile(":/ico/22x22/color/color-09-purple-17-slateblue.png"); - ColorHTMLPurpleMediumSlateBlue .addFile(":/ico/22x22/color/color-09-purple-18-mediumslateblue.png"); - ColorHTMLWhiteWhite .addFile(":/ico/22x22/color/color-10-white-01-white.png"); - ColorHTMLWhiteSnow .addFile(":/ico/22x22/color/color-10-white-02-snow.png"); - ColorHTMLWhiteHoneydew .addFile(":/ico/22x22/color/color-10-white-03-honeydew.png"); - ColorHTMLWhiteMintCream .addFile(":/ico/22x22/color/color-10-white-04-mintcream.png"); - ColorHTMLWhiteAzure .addFile(":/ico/22x22/color/color-10-white-05-azure.png"); - ColorHTMLWhiteAliceBlue .addFile(":/ico/22x22/color/color-10-white-06-aliceblue.png"); - ColorHTMLWhiteGhostWhite .addFile(":/ico/22x22/color/color-10-white-07-ghostwhite.png"); - ColorHTMLWhiteWhiteSmoke .addFile(":/ico/22x22/color/color-10-white-08-whitesmoke.png"); - ColorHTMLWhiteSeashell .addFile(":/ico/22x22/color/color-10-white-09-seashell.png"); - ColorHTMLWhiteBeige .addFile(":/ico/22x22/color/color-10-white-10-beige.png"); - ColorHTMLWhiteOldLace .addFile(":/ico/22x22/color/color-10-white-11-oldlace.png"); - ColorHTMLWhiteFloralWhite .addFile(":/ico/22x22/color/color-10-white-12-floralwhite.png"); - ColorHTMLWhiteIvory .addFile(":/ico/22x22/color/color-10-white-13-ivory.png"); - ColorHTMLWhiteAntiqueWhite .addFile(":/ico/22x22/color/color-10-white-14-antiquewhite.png"); - ColorHTMLWhiteLinen .addFile(":/ico/22x22/color/color-10-white-15-linen.png"); - ColorHTMLWhiteLavenderBlush .addFile(":/ico/22x22/color/color-10-white-16-lavenderblush.png"); - ColorHTMLWhiteMistyRose .addFile(":/ico/22x22/color/color-10-white-17-mistyrose.png"); - ColorHTMLGrayGainsboro .addFile(":/ico/22x22/color/color-11-gray-01-gainsboro.png"); - ColorHTMLGrayLightGray .addFile(":/ico/22x22/color/color-11-gray-02-lightgray.png"); - ColorHTMLGraySilver .addFile(":/ico/22x22/color/color-11-gray-03-silver.png"); - ColorHTMLGrayDarkGray .addFile(":/ico/22x22/color/color-11-gray-04-darkgray.png"); - ColorHTMLGrayGray .addFile(":/ico/22x22/color/color-11-gray-05-gray.png"); - ColorHTMLGrayDimGray .addFile(":/ico/22x22/color/color-11-gray-06-dimgray.png"); - ColorHTMLGrayLightSlateGray .addFile(":/ico/22x22/color/color-11-gray-07-lightslategray.png"); - ColorHTMLGraySlateGray .addFile(":/ico/22x22/color/color-11-gray-08-slategray.png"); - ColorHTMLGrayDarkSlateGray .addFile(":/ico/22x22/color/color-11-gray-09-darkslategray.png"); - ColorHTMLGrayBlack .addFile(":/ico/22x22/color/color-11-gray-10-black.png"); + ColorHTMLYellowPapayaWhip.addFile(":/ico/22x22/color/color-04-yellow-05-papayawhip.png"); + ColorHTMLYellowMoccasin.addFile(":/ico/22x22/color/color-04-yellow-06-moccasin.png"); + ColorHTMLYellowPeachPuff.addFile(":/ico/22x22/color/color-04-yellow-07-peachpuff.png"); + ColorHTMLYellowPaleGoldenrod.addFile(":/ico/22x22/color/color-04-yellow-08-palegoldenrod.png"); + ColorHTMLYellowKhaki.addFile(":/ico/22x22/color/color-04-yellow-09-khaki.png"); + ColorHTMLYellowDarkKhaki.addFile(":/ico/22x22/color/color-04-yellow-10-darkkhaki.png"); + ColorHTMLYellowGold.addFile(":/ico/22x22/color/color-04-yellow-11-gold.png"); + ColorHTMLBrownCornsilk.addFile(":/ico/22x22/color/color-05-brown-01-cornsilk.png"); + ColorHTMLBrownBlanchedAlmond.addFile(":/ico/22x22/color/color-05-brown-02-blanchedalmond.png"); + ColorHTMLBrownBisque.addFile(":/ico/22x22/color/color-05-brown-03-bisque.png"); + ColorHTMLBrownNavajoWhite.addFile(":/ico/22x22/color/color-05-brown-04-navajowhite.png"); + ColorHTMLBrownWheat.addFile(":/ico/22x22/color/color-05-brown-05-wheat.png"); + ColorHTMLBrownBurlywood.addFile(":/ico/22x22/color/color-05-brown-06-burlywood.png"); + ColorHTMLBrownTan.addFile(":/ico/22x22/color/color-05-brown-07-tan.png"); + ColorHTMLBrownRosyBrown.addFile(":/ico/22x22/color/color-05-brown-08-rosybrown.png"); + ColorHTMLBrownSandyBrown.addFile(":/ico/22x22/color/color-05-brown-09-sandybrown.png"); + ColorHTMLBrownGoldenrod.addFile(":/ico/22x22/color/color-05-brown-10-goldenrod.png"); + ColorHTMLBrownDarkGoldenrod.addFile(":/ico/22x22/color/color-05-brown-11-darkgoldenrod.png"); + ColorHTMLBrownPeru.addFile(":/ico/22x22/color/color-05-brown-12-peru.png"); + ColorHTMLBrownChocolate.addFile(":/ico/22x22/color/color-05-brown-13-chocolate.png"); + ColorHTMLBrownSaddleBrown.addFile(":/ico/22x22/color/color-05-brown-14-saddlebrown.png"); + ColorHTMLBrownSienna.addFile(":/ico/22x22/color/color-05-brown-15-sienna.png"); + ColorHTMLBrownBrown.addFile(":/ico/22x22/color/color-05-brown-16-brown.png"); + ColorHTMLBrownMaroon.addFile(":/ico/22x22/color/color-05-brown-17-maroon.png"); + ColorHTMLGreenDarkOliveGreen.addFile(":/ico/22x22/color/color-06-green-01-darkolivegreen.png"); + ColorHTMLGreenOlive.addFile(":/ico/22x22/color/color-06-green-02-olive.png"); + ColorHTMLGreenOliveDrab.addFile(":/ico/22x22/color/color-06-green-03-olivedrab.png"); + ColorHTMLGreenYellowGreen.addFile(":/ico/22x22/color/color-06-green-04-yellowgreen.png"); + ColorHTMLGreenLimeGreen.addFile(":/ico/22x22/color/color-06-green-05-limegreen.png"); + ColorHTMLGreenLime.addFile(":/ico/22x22/color/color-06-green-06-lime.png"); + ColorHTMLGreenLawnGreen.addFile(":/ico/22x22/color/color-06-green-07-lawngreen.png"); + ColorHTMLGreenChartreuse.addFile(":/ico/22x22/color/color-06-green-08-chartreuse.png"); + ColorHTMLGreenGreenYellow.addFile(":/ico/22x22/color/color-06-green-09-greenyellow.png"); + ColorHTMLGreenSpringGreen.addFile(":/ico/22x22/color/color-06-green-10-springgreen.png"); + ColorHTMLGreenMediumSpringGreen.addFile(":/ico/22x22/color/color-06-green-11-mediumspringgreen.png"); + ColorHTMLGreenLightGreen.addFile(":/ico/22x22/color/color-06-green-12-lightgreen.png"); + ColorHTMLGreenPaleGreen.addFile(":/ico/22x22/color/color-06-green-13-palegreen.png"); + ColorHTMLGreenDarkSeaGreen.addFile(":/ico/22x22/color/color-06-green-14-darkseagreen.png"); + ColorHTMLGreenMediumAquamarine.addFile(":/ico/22x22/color/color-06-green-15-mediumaquamarine.png"); + ColorHTMLGreenMediumSeaGreen.addFile(":/ico/22x22/color/color-06-green-16-mediumseagreen.png"); + ColorHTMLGreenSeaGreen.addFile(":/ico/22x22/color/color-06-green-17-seagreen.png"); + ColorHTMLGreenForestGreen.addFile(":/ico/22x22/color/color-06-green-18-forestgreen.png"); + ColorHTMLGreenGreen.addFile(":/ico/22x22/color/color-06-green-19-green.png"); + ColorHTMLGreenDarkGreen.addFile(":/ico/22x22/color/color-06-green-20-darkgreen.png"); + ColorHTMLCyanAqua.addFile(":/ico/22x22/color/color-07-cyan-01-aqua.png"); + ColorHTMLCyanCyan.addFile(":/ico/22x22/color/color-07-cyan-02-cyan.png"); + ColorHTMLCyanLightCyan.addFile(":/ico/22x22/color/color-07-cyan-03-lightcyan.png"); + ColorHTMLCyanPaleTurquoise.addFile(":/ico/22x22/color/color-07-cyan-04-paleturquoise.png"); + ColorHTMLCyanAquamarine.addFile(":/ico/22x22/color/color-07-cyan-05-aquamarine.png"); + ColorHTMLCyanTurquoise.addFile(":/ico/22x22/color/color-07-cyan-06-turquoise.png"); + ColorHTMLCyanMediumTurquoise.addFile(":/ico/22x22/color/color-07-cyan-07-mediumturquoise.png"); + ColorHTMLCyanDarkTurquoise.addFile(":/ico/22x22/color/color-07-cyan-08-darkturquoise.png"); + ColorHTMLCyanLightSeaGreen.addFile(":/ico/22x22/color/color-07-cyan-09-lightseagreen.png"); + ColorHTMLCyanCadetBlue.addFile(":/ico/22x22/color/color-07-cyan-10-cadetblue.png"); + ColorHTMLCyanDarkCyan.addFile(":/ico/22x22/color/color-07-cyan-11-darkcyan.png"); + ColorHTMLCyanTeal.addFile(":/ico/22x22/color/color-07-cyan-12-teal.png"); + ColorHTMLBlueLightSteelBlue.addFile(":/ico/22x22/color/color-08-blue-01-lightsteelblue.png"); + ColorHTMLBluePowderBlue.addFile(":/ico/22x22/color/color-08-blue-02-powderblue.png"); + ColorHTMLBlueLightBlue.addFile(":/ico/22x22/color/color-08-blue-03-lightblue.png"); + ColorHTMLBlueSkyBlue.addFile(":/ico/22x22/color/color-08-blue-04-skyblue.png"); + ColorHTMLBlueLightSkyBlue.addFile(":/ico/22x22/color/color-08-blue-05-lightskyblue.png"); + ColorHTMLBlueDeepSkyBlue.addFile(":/ico/22x22/color/color-08-blue-06-deepskyblue.png"); + ColorHTMLBlueDodgerBlue.addFile(":/ico/22x22/color/color-08-blue-07-dodgerblue.png"); + ColorHTMLBlueCornflowerBlue.addFile(":/ico/22x22/color/color-08-blue-08-cornflowerblue.png"); + ColorHTMLBlueSteelBlue.addFile(":/ico/22x22/color/color-08-blue-09-steelblue.png"); + ColorHTMLBlueRoyalBlue.addFile(":/ico/22x22/color/color-08-blue-10-royalblue.png"); + ColorHTMLBlueBlue.addFile(":/ico/22x22/color/color-08-blue-11-blue.png"); + ColorHTMLBlueMediumBlue.addFile(":/ico/22x22/color/color-08-blue-12-mediumblue.png"); + ColorHTMLBlueDarkBlue.addFile(":/ico/22x22/color/color-08-blue-13-darkblue.png"); + ColorHTMLBlueNavy.addFile(":/ico/22x22/color/color-08-blue-14-navy.png"); + ColorHTMLBlueMidnightBlue.addFile(":/ico/22x22/color/color-08-blue-15-midnightblue.png"); + ColorHTMLPurpleLavender.addFile(":/ico/22x22/color/color-09-purple-01-lavender.png"); + ColorHTMLPurpleThistle.addFile(":/ico/22x22/color/color-09-purple-02-thistle.png"); + ColorHTMLPurplePlum.addFile(":/ico/22x22/color/color-09-purple-03-plum.png"); + ColorHTMLPurpleViolet.addFile(":/ico/22x22/color/color-09-purple-04-violet.png"); + ColorHTMLPurpleOrchid.addFile(":/ico/22x22/color/color-09-purple-05-orchid.png"); + ColorHTMLPurpleFuchsia.addFile(":/ico/22x22/color/color-09-purple-06-fuchsia.png"); + ColorHTMLPurpleMagenta.addFile(":/ico/22x22/color/color-09-purple-07-magenta.png"); + ColorHTMLPurpleMediumOrchid.addFile(":/ico/22x22/color/color-09-purple-08-mediumorchid.png"); + ColorHTMLPurpleMediumPurple.addFile(":/ico/22x22/color/color-09-purple-09-mediumpurple.png"); + ColorHTMLPurpleBlueViolet.addFile(":/ico/22x22/color/color-09-purple-10-blueviolet.png"); + ColorHTMLPurpleDarkViolet.addFile(":/ico/22x22/color/color-09-purple-11-darkviolet.png"); + ColorHTMLPurpleDarkOrchid.addFile(":/ico/22x22/color/color-09-purple-12-darkorchid.png"); + ColorHTMLPurpleDarkMagenta.addFile(":/ico/22x22/color/color-09-purple-13-darkmagenta.png"); + ColorHTMLPurplePurple.addFile(":/ico/22x22/color/color-09-purple-14-purple.png"); + ColorHTMLPurpleIndigo.addFile(":/ico/22x22/color/color-09-purple-15-indigo.png"); + ColorHTMLPurpleDarkSlateBlue.addFile(":/ico/22x22/color/color-09-purple-16-darkslateblue.png"); + ColorHTMLPurpleSlateBlue.addFile(":/ico/22x22/color/color-09-purple-17-slateblue.png"); + ColorHTMLPurpleMediumSlateBlue.addFile(":/ico/22x22/color/color-09-purple-18-mediumslateblue.png"); + ColorHTMLWhiteWhite.addFile(":/ico/22x22/color/color-10-white-01-white.png"); + ColorHTMLWhiteSnow.addFile(":/ico/22x22/color/color-10-white-02-snow.png"); + ColorHTMLWhiteHoneydew.addFile(":/ico/22x22/color/color-10-white-03-honeydew.png"); + ColorHTMLWhiteMintCream.addFile(":/ico/22x22/color/color-10-white-04-mintcream.png"); + ColorHTMLWhiteAzure.addFile(":/ico/22x22/color/color-10-white-05-azure.png"); + ColorHTMLWhiteAliceBlue.addFile(":/ico/22x22/color/color-10-white-06-aliceblue.png"); + ColorHTMLWhiteGhostWhite.addFile(":/ico/22x22/color/color-10-white-07-ghostwhite.png"); + ColorHTMLWhiteWhiteSmoke.addFile(":/ico/22x22/color/color-10-white-08-whitesmoke.png"); + ColorHTMLWhiteSeashell.addFile(":/ico/22x22/color/color-10-white-09-seashell.png"); + ColorHTMLWhiteBeige.addFile(":/ico/22x22/color/color-10-white-10-beige.png"); + ColorHTMLWhiteOldLace.addFile(":/ico/22x22/color/color-10-white-11-oldlace.png"); + ColorHTMLWhiteFloralWhite.addFile(":/ico/22x22/color/color-10-white-12-floralwhite.png"); + ColorHTMLWhiteIvory.addFile(":/ico/22x22/color/color-10-white-13-ivory.png"); + ColorHTMLWhiteAntiqueWhite.addFile(":/ico/22x22/color/color-10-white-14-antiquewhite.png"); + ColorHTMLWhiteLinen.addFile(":/ico/22x22/color/color-10-white-15-linen.png"); + ColorHTMLWhiteLavenderBlush.addFile(":/ico/22x22/color/color-10-white-16-lavenderblush.png"); + ColorHTMLWhiteMistyRose.addFile(":/ico/22x22/color/color-10-white-17-mistyrose.png"); + ColorHTMLGrayGainsboro.addFile(":/ico/22x22/color/color-11-gray-01-gainsboro.png"); + ColorHTMLGrayLightGray.addFile(":/ico/22x22/color/color-11-gray-02-lightgray.png"); + ColorHTMLGraySilver.addFile(":/ico/22x22/color/color-11-gray-03-silver.png"); + ColorHTMLGrayDarkGray.addFile(":/ico/22x22/color/color-11-gray-04-darkgray.png"); + ColorHTMLGrayGray.addFile(":/ico/22x22/color/color-11-gray-05-gray.png"); + ColorHTMLGrayDimGray.addFile(":/ico/22x22/color/color-11-gray-06-dimgray.png"); + ColorHTMLGrayLightSlateGray.addFile(":/ico/22x22/color/color-11-gray-07-lightslategray.png"); + ColorHTMLGraySlateGray.addFile(":/ico/22x22/color/color-11-gray-08-slategray.png"); + ColorHTMLGrayDarkSlateGray.addFile(":/ico/22x22/color/color-11-gray-09-darkslategray.png"); + ColorHTMLGrayBlack.addFile(":/ico/22x22/color/color-11-gray-10-black.png"); } diff --git a/sources/richtext/richtexteditor.cpp b/sources/richtext/richtexteditor.cpp index 489d3391a..9c8b9b0a6 100644 --- a/sources/richtext/richtexteditor.cpp +++ b/sources/richtext/richtexteditor.cpp @@ -514,19 +514,19 @@ RichTextEditorToolBar::RichTextEditorToolBar(RichTextEditor *editor, // Bold, italic and underline buttons m_bold_action = createCheckableAction( - QIcon(":/ico/32x32/format-text-bold.png"), + QIcon::fromTheme("format-text-bold"), tr("Texte en gras"), editor, &RichTextEditor::setFontBold, this); ShortcutManager::instance().registerAction(m_bold_action, "richtext.bold", tr("Éditeur de texte"), Qt::CTRL | Qt::Key_B); addAction(m_bold_action); m_italic_action = createCheckableAction( - QIcon(":/ico/32x32/format-text-italic.png"), + QIcon::fromTheme("format-text-italic"), tr("Texte en italique"), editor, &RichTextEditor::setFontItalic, this); ShortcutManager::instance().registerAction(m_italic_action, "richtext.italic", tr("Éditeur de texte"), Qt::CTRL | Qt::Key_I); addAction(m_italic_action); m_underline_action = createCheckableAction( - QIcon(":/ico/32x32/format-text-underline.png"), + QIcon::fromTheme("format-text-underline"), tr("Texte souligé"), editor, &RichTextEditor::setFontUnderline, this); ShortcutManager::instance().registerAction(m_underline_action, "richtext.underline", tr("Éditeur de texte"), Qt::CTRL | Qt::Key_U); addAction(m_underline_action); @@ -567,13 +567,13 @@ RichTextEditorToolBar::RichTextEditorToolBar(RichTextEditor *editor, // Superscript and subscript buttons m_valign_sup_action = createCheckableAction( - QIcon(":/ico/22x22/format-text-superscript.png"), + QIcon::fromTheme("format-text-superscript"), tr("Superscript"), this, &RichTextEditorToolBar::setVAlignSuper, this); addAction(m_valign_sup_action); m_valign_sub_action = createCheckableAction( - QIcon(":/ico/22x22/format-text-subscript.png"), + QIcon::fromTheme("format-text-subscript"), tr("Subscript"), this, &RichTextEditorToolBar::setVAlignSub, this); addAction(m_valign_sub_action); @@ -601,7 +601,7 @@ RichTextEditorToolBar::RichTextEditorToolBar(RichTextEditor *editor, // Simplify rich text m_simplify_richtext_action = createCheckableAction( - QIcon(":/ico/32x32/simplifyrichtext.png"), + QIcon::fromTheme("simplifyrichtext"), tr("Simplify Rich Text"), editor, &RichTextEditor::setSimplifyRichText, this); m_simplify_richtext_action->setChecked(editor->simplifyRichText()); connect(m_editor, &RichTextEditor::simplifyRichTextChanged, m_simplify_richtext_action, &QAction::setChecked); diff --git a/sources/ui/conductorpropertieswidget.ui b/sources/ui/conductorpropertieswidget.ui index 48ea44ac8..8a31d43a3 100644 --- a/sources/ui/conductorpropertieswidget.ui +++ b/sources/ui/conductorpropertieswidget.ui @@ -233,7 +233,7 @@ - + :/ico/16x16/configure.png:/ico/16x16/configure.png @@ -363,7 +363,7 @@ neutre - + :/ico/16x16/neutral.png:/ico/16x16/neutral.png @@ -377,7 +377,7 @@ terre - + :/ico/16x16/ground.png:/ico/16x16/ground.png @@ -391,7 +391,7 @@ phase - + :/ico/16x16/phase.png:/ico/16x16/phase.png diff --git a/sources/ui/configsaveloaderwidget.ui b/sources/ui/configsaveloaderwidget.ui index f0b69605b..aaa8aa130 100644 --- a/sources/ui/configsaveloaderwidget.ui +++ b/sources/ui/configsaveloaderwidget.ui @@ -29,7 +29,7 @@ - + :/ico/16x16/folder-open.png:/ico/16x16/folder-open.png @@ -40,7 +40,7 @@ - + :/ico/16x16/document-save.png:/ico/16x16/document-save.png diff --git a/sources/ui/dynamicelementtextitemeditor.ui b/sources/ui/dynamicelementtextitemeditor.ui index 66a326d4f..273637c2e 100644 --- a/sources/ui/dynamicelementtextitemeditor.ui +++ b/sources/ui/dynamicelementtextitemeditor.ui @@ -25,7 +25,7 @@ ... - + :/ico/16x16/document-save.png:/ico/16x16/document-save.png @@ -39,7 +39,7 @@ ... - + :/ico/16x16/folder-open.png:/ico/16x16/folder-open.png @@ -66,7 +66,7 @@ ... - + :/ico/22x22/textfield.png:/ico/22x22/textfield.png @@ -80,7 +80,7 @@ ... - + :/ico/16x16/object-group.png:/ico/16x16/object-group.png @@ -94,7 +94,7 @@ ... - + :/ico/16x16/list-remove.png:/ico/16x16/list-remove.png diff --git a/sources/ui/masterpropertieswidget.ui b/sources/ui/masterpropertieswidget.ui index 71eb57dd8..910ac27c0 100644 --- a/sources/ui/masterpropertieswidget.ui +++ b/sources/ui/masterpropertieswidget.ui @@ -136,7 +136,7 @@ - + :/ico/16x16/go-up.png:/ico/16x16/go-up.png @@ -150,7 +150,7 @@ - + :/ico/16x16/go-down.png:/ico/16x16/go-down.png diff --git a/sources/ui/thirdpartybinaryinstalldialog.ui b/sources/ui/thirdpartybinaryinstalldialog.ui index 899666d2c..cbd94b056 100644 --- a/sources/ui/thirdpartybinaryinstalldialog.ui +++ b/sources/ui/thirdpartybinaryinstalldialog.ui @@ -33,7 +33,7 @@ Télechargement - + :/ico/16x16/edit-download.png:/ico/16x16/edit-download.png @@ -44,7 +44,7 @@ Dossier installation - + :/ico/16x16/folder-open.png:/ico/16x16/folder-open.png diff --git a/sources/ui/titleblockpropertieswidget.ui b/sources/ui/titleblockpropertieswidget.ui index 2389eabcf..0be2a3623 100644 --- a/sources/ui/titleblockpropertieswidget.ui +++ b/sources/ui/titleblockpropertieswidget.ui @@ -94,7 +94,7 @@ - + :/ico/22x22/titleblock-bottom.png:/ico/22x22/titleblock-bottom.png @@ -103,7 +103,7 @@ - + :/ico/22x22/titleblock-right.png:/ico/22x22/titleblock-right.png @@ -121,7 +121,7 @@ - + :/ico/22x22/edit-rename.png:/ico/22x22/edit-rename.png @@ -210,7 +210,7 @@ - + :/ico/16x16/configure.png:/ico/16x16/configure.png @@ -305,7 +305,7 @@ - + :/ico/22x22/start.png:/ico/22x22/start.png diff --git a/tests/qttest/CMakeLists.txt b/tests/qttest/CMakeLists.txt index 855a9acd2..638d3961a 100644 --- a/tests/qttest/CMakeLists.txt +++ b/tests/qttest/CMakeLists.txt @@ -82,12 +82,28 @@ target_link_libraries(tst_contactusage PRIVATE Qt::Test) add_executable( tst_qetpalette tst_qetpalette.cpp + inkcontrast.h ${QET_DIR}/sources/qetpalette.cpp ${QET_DIR}/sources/qetpalette.h) add_test(NAME tst_qetpalette COMMAND tst_qetpalette) target_include_directories(tst_qetpalette PRIVATE ${QET_DIR}/sources) target_link_libraries(tst_qetpalette PRIVATE Qt::Test Qt::Widgets) +# qeticons.cpp is compiled in so the test can check the table entries +# themselves (icon sizes in the element panel), not only the theme files. +add_executable( + tst_qeticons + tst_qeticons.cpp + inkcontrast.h + ${QET_DIR}/sources/qeticons.cpp + ${QET_DIR}/sources/qeticons.h + ${QET_DIR}/sources/qetpalette.cpp + ${QET_DIR}/sources/qetpalette.h + ${QET_DIR}/ico/icon-themes.qrc) +add_test(NAME tst_qeticons COMMAND tst_qeticons) +target_include_directories(tst_qeticons PRIVATE ${QET_DIR}/sources) +target_link_libraries(tst_qeticons PRIVATE Qt::Test Qt::Widgets Qt::Svg) + add_executable( tst_smart_device tst_smart_device.cpp diff --git a/tests/qttest/inkcontrast.h b/tests/qttest/inkcontrast.h new file mode 100644 index 000000000..282a4db2e --- /dev/null +++ b/tests/qttest/inkcontrast.h @@ -0,0 +1,77 @@ +/* + Copyright 2006-2026 The QElectroTech Team + This file is part of QElectroTech. + + QElectroTech is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + QElectroTech is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QElectroTech. If not, see . +*/ +#ifndef QET_TEST_INKCONTRAST_H +#define QET_TEST_INKCONTRAST_H + +#include +#include +#include +#include +#include +#include + +#include "qetpalette.h" + +/** + Shared helpers for the rendering tests: paint a widget on the offscreen + platform and measure the ink against the background. +*/ +namespace QET { + namespace Test { + /** + Grab a widget for measuring. With QET_TEST_DUMP_DIR set, the + image is also written there as -.png so a failure + can be looked at. + */ + inline QImage grab(QWidget *widget, const char *name) + { + const QImage image = widget->grab().toImage(); + const QByteArray dir = qgetenv("QET_TEST_DUMP_DIR"); + if (!dir.isEmpty()) + image.save(QString("%1/%2-%3.png").arg(QString::fromLocal8Bit(dir), + QTest::currentDataTag(), name)); + return image; + } + + /** + Contrast between the background of a rendered widget (its most + frequent color) and the ink drawn on it (the pixel whose + luminance differs most from the background), inside rect. + */ + inline double inkContrast(const QImage &image, const QRect &rect) + { + QHash histogram; + for (int y = rect.top(); y <= rect.bottom(); ++y) + for (int x = rect.left(); x <= rect.right(); ++x) + ++histogram[image.pixel(x, y)]; + + QRgb background = 0; + int best = -1; + for (auto it = histogram.cbegin(); it != histogram.cend(); ++it) + if (it.value() > best) { best = it.value(); background = it.key(); } + + double contrast = 1.0; + for (auto it = histogram.cbegin(); it != histogram.cend(); ++it) + contrast = qMax(contrast, QET::Palette::contrastRatio(QColor(background), + QColor(it.key()))); + return contrast; + } + } +} + +#endif diff --git a/tests/qttest/tst_qeticons.cpp b/tests/qttest/tst_qeticons.cpp new file mode 100644 index 000000000..af5966d0d --- /dev/null +++ b/tests/qttest/tst_qeticons.cpp @@ -0,0 +1,166 @@ +/* + Copyright 2006-2026 The QElectroTech Team + This file is part of QElectroTech. + + QElectroTech is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + QElectroTech is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QElectroTech. If not, see . +*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "inkcontrast.h" +#include "qeticons.h" +#include "qetpalette.h" + +/** + Checks on QET's icon theme "qet", generated by misc/make_icon_themes.py + into ico/icon-themes.qrc, and on the icon table that uses it. + + Every icon name must resolve. A toolbar button painted with Fusion must + show its icon at 3:1 (WCAG 1.4.11), with the disabled state reading + weaker than the enabled one. And the icons the elements panel draws in + its 50 px slots must stay small (GitHub #870). +*/ +class tst_qeticons : public QObject +{ + Q_OBJECT + + private slots: + void initTestCase(); + void everyIconResolves(); + void toolbarIconIsReadable_data(); + void toolbarIconIsReadable(); + void panelProjectIconStaysSmall(); +}; + +namespace { + const double kIconRatio = 3.0; + + QStringList iconNames(const QString &theme) + { + QStringList names; + for (const QString &size : {"16x16", "22x22", "32x32", "48x48", "128x128", "scalable"}) + { + QDir dir(QString(":/ico/themes/%1/%2").arg(theme, size)); + for (const QString &file : dir.entryList(QDir::Files)) + names << file.section('.', 0, -2); + } + names.removeDuplicates(); + names.sort(); + return names; + } +} + +void tst_qeticons::initTestCase() +{ + QStringList paths = QIcon::themeSearchPaths(); + paths.prepend(QStringLiteral(":/ico/themes")); + QIcon::setThemeSearchPaths(paths); + QIcon::setThemeName(QStringLiteral("qet")); + QVERIFY2(!iconNames("qet").isEmpty(), "theme has no icons: is ico/icon-themes.qrc compiled in?"); +} + +void tst_qeticons::everyIconResolves() +{ + QVERIFY(QIcon::hasThemeIcon("list-add")); + for (const QString &name : iconNames("qet")) + QVERIFY2(!QIcon::fromTheme(name).isNull(), qPrintable(QString("%1 missing").arg(name))); + // Names the icon table relies on that come from the alias list in + // misc/make_icon_themes.py, not from a file of that name at 22 pixels. + for (const QString &name : {"folio-new", "folio-delete", "folio-properties", "conductor-reset"}) + QVERIFY2(!QIcon::fromTheme(name).pixmap(22).isNull(), qPrintable(name)); +} + +void tst_qeticons::toolbarIconIsReadable_data() +{ + QTest::addColumn("palette"); + QTest::newRow("light") << QET::Palette::fusionLight(); +} + +/** + A line-art icon on a Fusion tool button, as in the diagram editor + toolbar. Enabled must reach 3:1; disabled must be weaker than enabled. +*/ +void tst_qeticons::toolbarIconIsReadable() +{ + QFETCH(QPalette, palette); + QApplication::setStyle(QStyleFactory::create("Fusion")); + QApplication::setPalette(palette); + + QMainWindow window; + QToolBar *toolbar = window.addToolBar("view"); + auto make = [&](const char *name, bool enabled) { + auto *button = new QToolButton; + button->setIcon(QIcon::fromTheme("zoom-fit-best")); + button->setAutoRaise(true); + button->setEnabled(enabled); + button->setObjectName(name); + toolbar->addWidget(button); + return button; + }; + QToolButton *enabled = make("enabled", true); + QToolButton *disabled = make("disabled", false); + window.resize(300, 100); + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + + const double enabled_contrast = QET::Test::inkContrast(QET::Test::grab(enabled, "enabled"), enabled->rect()); + const double disabled_contrast = QET::Test::inkContrast(QET::Test::grab(disabled, "disabled"), disabled->rect()); + QVERIFY2(enabled_contrast >= kIconRatio, + qPrintable(QString("enabled icon: %1").arg(enabled_contrast))); + QVERIFY2(disabled_contrast < enabled_contrast, + qPrintable(QString("disabled %1 reads better than enabled %2") + .arg(disabled_contrast).arg(enabled_contrast))); +} + +/** + The elements panel sets a 50 px icon size so element previews are + large. Its project root item uses ProjectFileGP, whose theme name also + has a 128 px file for the configuration dialog. On a 2x display the + theme loader would pick that file for a 50 px request and fill the + slot, so the entry must not resolve through the theme name. +*/ +void tst_qeticons::panelProjectIconStaysSmall() +{ +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + QSKIP("QIcon::pixmap(size, devicePixelRatio) needs Qt 6"); +#else + QET::Icons::initIcons(); + + const QPixmap panel = QET::Icons::ProjectFileGP.pixmap(QSize(50, 50), 2.0); + QVERIFY(!panel.isNull()); + const int logical = qRound(panel.width() / panel.devicePixelRatio()); + QVERIFY2(logical <= 22, qPrintable(QString("project root icon is %1 px in a 50 px slot").arg(logical))); + + // The configuration dialog's page list still gets the large file. + QCOMPARE(QET::Icons::Projects.pixmap(QSize(128, 128), 1.0).width(), 128); +#endif +} + +int main(int argc, char **argv) +{ + if (qEnvironmentVariableIsEmpty("QT_QPA_PLATFORM")) + qputenv("QT_QPA_PLATFORM", "offscreen"); + QApplication app(argc, argv); + tst_qeticons test; + QTEST_SET_MAIN_SOURCE_PATH + return QTest::qExec(&test, argc, argv); +} + +#include "tst_qeticons.moc" diff --git a/tests/qttest/tst_qetpalette.cpp b/tests/qttest/tst_qetpalette.cpp index 7cc589bdd..ccbe07f15 100644 --- a/tests/qttest/tst_qetpalette.cpp +++ b/tests/qttest/tst_qetpalette.cpp @@ -28,9 +28,12 @@ #include #include +#include "inkcontrast.h" #include "qetpalette.h" using QET::Palette::contrastRatio; +using QET::Test::grab; +using QET::Test::inkContrast; /** Contrast checks for the palettes QET installs under the Fusion style. @@ -94,44 +97,6 @@ namespace { {"Link/Window", QPalette::Link, QPalette::Window}, }; - /** - Grab a widget for measuring. With QET_TEST_DUMP_DIR set, the image - is also written there as -.png so a failure can be - looked at. - */ - QImage grab(QWidget *widget, const char *name) - { - const QImage image = widget->grab().toImage(); - const QByteArray dir = qgetenv("QET_TEST_DUMP_DIR"); - if (!dir.isEmpty()) - image.save(QString("%1/%2-%3.png").arg(QString::fromLocal8Bit(dir), - QTest::currentDataTag(), name)); - return image; - } - - /** - Contrast between the background of a rendered widget (its most - frequent color) and the ink drawn on it (the pixel whose luminance - differs most from the background), inside rect. - */ - double inkContrast(const QImage &image, const QRect &rect) - { - QHash histogram; - for (int y = rect.top(); y <= rect.bottom(); ++y) - for (int x = rect.left(); x <= rect.right(); ++x) - ++histogram[image.pixel(x, y)]; - - QRgb background = 0; - int best = -1; - for (auto it = histogram.cbegin(); it != histogram.cend(); ++it) - if (it.value() > best) { best = it.value(); background = it.key(); } - - double contrast = 1.0; - for (auto it = histogram.cbegin(); it != histogram.cend(); ++it) - contrast = qMax(contrast, contrastRatio(QColor(background), - QColor(it.key()))); - return contrast; - } } void tst_qetpalette::addPaletteRows() From b7f516d485b0bb0fab3e9f98de9d5d691a58be8b Mon Sep 17 00:00:00 2001 From: Kellermorph Date: Wed, 16 Sep 2026 12:09:05 +0200 Subject: [PATCH 12/20] Clean up stale data when duplicating diagram pages When duplicating a diagram page, copied slave elements retain stale data from the source: labels, descriptions, link references, and PLC master information (type, address, function, comment, cross-ref, timer values) remain in the copies. Fix by adding clearPendingLinks() to Element to prevent copies from linking back to source elements via stale UUIDs, and by cleaning up copied element data after fromXml(): - Slaves always lose their label, formula, comment, location, and PLC master data, since their text comes from a master element which is not available on the copy. The displayed text on slave elements is cleared directly via setPlainText(), but UserText items (free text typed by the user) are preserved. - For PLC slaves, setElementInformations() is called without m_block_alignment so that elementInfoChanged() can run finishAlignment() to correctly adjust text positions for the cleared content. - Non-slave elements respect the existing erase-label-on-copy preference, same as PasteDiagramCommand::redo(). - Conductor labels are also reset when erase-label-on-copy is active, matching PasteDiagramCommand::redo() (issue #413). - Text alignment is preserved by wrapping setElementInformations() with m_block_alignment for non-slave elements, same as Element::fromXml(). --- sources/elementspanelwidget.cpp | 100 ++++++++++++++++++++++++++++++ sources/qetgraphicsitem/element.h | 5 ++ 2 files changed, 105 insertions(+) diff --git a/sources/elementspanelwidget.cpp b/sources/elementspanelwidget.cpp index 018064da5..4aa61b7b1 100644 --- a/sources/elementspanelwidget.cpp +++ b/sources/elementspanelwidget.cpp @@ -28,6 +28,8 @@ #include #include #include "qetgraphicsitem/element.h" +#include "qetgraphicsitem/dynamicelementtextitem.h" +#include "qetinformation.h" /* When the ENABLE_PANEL_WIDGET_DND_CHECKS flag is set, the panel @@ -668,6 +670,10 @@ void ElementsPanelWidget::duplicateDiagram() new_diagram->fromXml(diagram_elmt, QPointF(0, 0), false, nullptr); + QSettings settings; + bool erase_labels = settings.value( + "diagramcommands/erase-label-on-copy", true).toBool(); + for (QGraphicsItem *item : new_diagram->items()) { if (Element *elmt = dynamic_cast(item)) { // The XML round-trip kept the source elements' uuids. Give the @@ -688,6 +694,90 @@ void ElementsPanelWidget::duplicateDiagram() { new_diagram->restoreText(elmt); } + + // Clear pending links so copies don't link back to + // the source elements via stale UUIDs. + elmt->clearPendingLinks(); + + // Clean up copied element data: + // 1. Slaves always lose label/formula/comment/location + // and PLC master data — their text comes from a + // master element not available on the copy. + // 2. Non-slaves: honour "erase-label-on-copy". + + DiagramContext dc = elmt->elementInformations(); + bool changed = false; + + // Slaves always lose their label/BMK and PLC data — + // their text comes from the master, which is not + // available on the copied element. + if (elmt->linkType() == Element::Slave) { + dc.addValue("formula", ""); + dc.addValue("label", ""); + dc.addValue("comment", ""); + dc.addValue("location", ""); + changed = true; + + for (const QString &key : { + QETInformation::ELMT_PLC_TYPE, + QETInformation::ELMT_PLC_ADDRESS, + QETInformation::ELMT_PLC_FUNCTION, + QETInformation::ELMT_PLC_COMMENT, + QETInformation::ELMT_PLC_CROSSREF, + QETInformation::ELMT_PLC_TC, + QETInformation::ELMT_PLC_T1, + QETInformation::ELMT_PLC_T2, + QETInformation::ELMT_PLC_T3, + QETInformation::ELMT_PLC_T4, + QStringLiteral("xref")}) { + if (dc.contains(key)) { + dc.remove(key); + changed = true; + } + } + } + // Non-slaves: honour the "erase-label-on-copy" preference. + else if (erase_labels) { + dc.addValue("formula", ""); + dc.addValue("label", ""); + dc.addValue("comment", ""); + dc.addValue("location", ""); + changed = true; + } + + if (changed) { + const bool is_slave = (elmt->linkType() == Element::Slave); + + // Block alignment during setElementInformations + // for non-slaves, same as Element::fromXml() (line 890-896). + // For PLC slaves, don't block — elementInfoChanged() + // fires (elementUseForInfo returns self) and needs + // finishAlignment() to adjust positions for the + // new (empty) text. + if (!is_slave) { + for (auto deti : elmt->dynamicTextItems()) + deti->m_block_alignment = true; + } + elmt->setElementInformations(dc); + for (auto deti : elmt->dynamicTextItems()) + deti->m_block_alignment = false; + + // For non-PLC slaves, elementInfoChanged() doesn't + // fire (no linked master), so the text items keep + // their original text. Clear them directly. + // For PLC slaves, elementInfoChanged() already + // cleared the text above. Skip UserText items + // (free text typed by the user) in both cases. + if (is_slave) { + for (auto deti : elmt->dynamicTextItems()) { + if (deti->textFrom() != DynamicElementTextItem::UserText) { + deti->m_block_alignment = true; + deti->setPlainText(QString()); + deti->m_block_alignment = false; + } + } + } + } } else if (Conductor *cond = dynamic_cast(item)) { // Same reasoning for conductors: conductor.uuid is the PRIMARY @@ -695,6 +785,16 @@ void ElementsPanelWidget::duplicateDiagram() // so a duplicated uuid fails and the wire silently disappears // from the wiring list and the per-element wire count. cond->newUuid(); + + // Reset conductor labels when "erase-label-on-copy" is + // active, matching PasteDiagramCommand::redo() — + // "erase on copy" means erase, not "replace with the + // project's default new-conductor text" (see issue #413). + if (erase_labels) { + ConductorProperties cp = cond->properties(); + cp.text = ""; + cond->setProperties(cp); + } } } } diff --git a/sources/qetgraphicsitem/element.h b/sources/qetgraphicsitem/element.h index 9e94d2c4e..9a5b46c2c 100644 --- a/sources/qetgraphicsitem/element.h +++ b/sources/qetgraphicsitem/element.h @@ -119,6 +119,11 @@ class Element : public QetGraphicsItem QList conductors() const; QList> AlignedFreeTerminals() const; + void clearPendingLinks() { + tmp_uuids_link.clear(); + m_group_index_map.clear(); + } + //METHODS related to information DiagramContext elementInformations()const {return m_data.m_informations;} From 863ac5f0aeb878e940ed4aa169d2b42b7fc5e241 Mon Sep 17 00:00:00 2001 From: Andre Rummler Date: Thu, 17 Sep 2026 13:25:17 +0200 Subject: [PATCH 13/20] avoid that another action triggers while aborting operation --- sources/diagram.cpp | 5 +++++ sources/diagram.h | 2 +- sources/diagramevent/diagrameventaddpaste.cpp | 12 +++++++----- sources/diagramview.cpp | 2 +- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/sources/diagram.cpp b/sources/diagram.cpp index 591f2b885..aa8e92399 100644 --- a/sources/diagram.cpp +++ b/sources/diagram.cpp @@ -801,6 +801,11 @@ void Diagram::clearEventInterface() } } +bool Diagram::eventInterfaceIsRunning() const +{ + return m_event_interface && m_event_interface->isRunning(); +} + /** @brief Diagram::conductorsAutonumName @return the name of autonum to use. diff --git a/sources/diagram.h b/sources/diagram.h index aa9cb1d9f..824b6dc38 100644 --- a/sources/diagram.h +++ b/sources/diagram.h @@ -216,7 +216,7 @@ class Diagram : public QGraphicsScene // methods related to graphics items addition/removal on the diagram virtual void addItem (QGraphicsItem *item); virtual void removeItem (QGraphicsItem *item); - bool hasEventInterface() const { return m_event_interface != nullptr; } + bool eventInterfaceIsRunning() const; // methods related to graphics options ExportProperties applyProperties(const ExportProperties &); diff --git a/sources/diagramevent/diagrameventaddpaste.cpp b/sources/diagramevent/diagrameventaddpaste.cpp index d9be0f159..ccc105192 100644 --- a/sources/diagramevent/diagrameventaddpaste.cpp +++ b/sources/diagramevent/diagrameventaddpaste.cpp @@ -87,12 +87,14 @@ DiagramEventAddPaste::DiagramEventAddPaste(Diagram *diagram, const QPointF &star */ DiagramEventAddPaste::~DiagramEventAddPaste() { - if (!m_finished) { + if (!m_finished && m_diagram) { removeItems(); m_finished = true; m_running = false; } - if (m_status_bar) m_status_bar->clearMessage(); + if (m_status_bar) { + m_status_bar->clearMessage(); + } } /** @@ -164,13 +166,13 @@ void DiagramEventAddPaste::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { if (!m_running) return; + event->setAccepted(true); if (event->button() == Qt::LeftButton) { moveTo(event->scenePos()); commit(); } else if (event->button() == Qt::RightButton) { cancel(); } - event->setAccepted(true); } void DiagramEventAddPaste::keyPressEvent(QKeyEvent *event) @@ -179,15 +181,15 @@ void DiagramEventAddPaste::keyPressEvent(QKeyEvent *event) switch (event->key()) { case Qt::Key_Escape: - cancel(); event->setAccepted(true); + cancel(); break; //Return and Enter drop the paste where it stands, so the whole //operation can be completed without a mouse. case Qt::Key_Return: case Qt::Key_Enter: - commit(); event->setAccepted(true); + commit(); break; default: break; diff --git a/sources/diagramview.cpp b/sources/diagramview.cpp index 3d2a6d340..9e89be6cb 100644 --- a/sources/diagramview.cpp +++ b/sources/diagramview.cpp @@ -733,7 +733,7 @@ void DiagramView::keyPressEvent(QKeyEvent *e) //way off the canvas for someone working without a mouse. //Escape steps back out: first it drops the selection, then it //hands focus to the next widget. - if (m_diagram && m_diagram->hasEventInterface()) { + if (m_diagram && m_diagram->eventInterfaceIsRunning()) { QGraphicsView::keyPressEvent(e); // let the active tool see it return; } From 8c135a009d42ba98966a0328fb07fd54171d8cc3 Mon Sep 17 00:00:00 2001 From: Laurent Trinques Date: Thu, 17 Sep 2026 13:59:32 +0200 Subject: [PATCH 14/20] Clear the tools dock when an editor refuses the selection --- sources/editor/ui/qetelementeditor.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sources/editor/ui/qetelementeditor.cpp b/sources/editor/ui/qetelementeditor.cpp index a8d190eb0..5c1f68f09 100644 --- a/sources/editor/ui/qetelementeditor.cpp +++ b/sources/editor/ui/qetelementeditor.cpp @@ -619,6 +619,8 @@ void QETElementEditor::updateInformations() } else { qDebug() << "Editor refused part."; + clearToolsDock(); + m_tools_dock_stack->setCurrentIndex(0); } } return; @@ -652,6 +654,8 @@ void QETElementEditor::updateInformations() } else { qDebug() << "Editor refused part."; + clearToolsDock(); + m_tools_dock_stack->setCurrentIndex(0); } } return; @@ -678,6 +682,8 @@ void QETElementEditor::updateInformations() } else { qDebug() << "Editor refused part."; + clearToolsDock(); + m_tools_dock_stack->setCurrentIndex(0); } } } From 218e70c759d31f908a593d16291f84d8bba5616a Mon Sep 17 00:00:00 2001 From: Laurent Trinques Date: Thu, 17 Sep 2026 15:27:26 +0200 Subject: [PATCH 15/20] CI: install an icon theme for tst_qeticons The ubuntu:26.04 container has no icon theme, so QIcon::fromTheme() returns null icons and tst_qeticons fails on first CI run. Install breeze-icon-theme so the test runs against a real theme, as it does on developer machines. --- .github/workflows/linux-build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/linux-build.yml b/.github/workflows/linux-build.yml index ff86b31f2..470830a5c 100644 --- a/.github/workflows/linux-build.yml +++ b/.github/workflows/linux-build.yml @@ -37,7 +37,8 @@ jobs: # Qt6{Core,Gui,Widgets,DBus}Tools, failing the configure with an error # that appears to be about QElectroTech and is not. DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ - extra-cmake-modules libkf6coreaddons-dev libkf6widgetsaddons-dev + extra-cmake-modules libkf6coreaddons-dev libkf6widgetsaddons-dev \ + breeze-icon-theme - uses: actions/checkout@v4 with: From 9519b708264884d7373699a08ebba6cdc3fbedf4 Mon Sep 17 00:00:00 2001 From: Laurent Trinques Date: Thu, 17 Sep 2026 15:46:26 +0200 Subject: [PATCH 16/20] CI: skip SignPath signing until the API token is configured --- .github/workflows/windows-msi.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/windows-msi.yml b/.github/workflows/windows-msi.yml index 73d57e7ab..03428963a 100644 --- a/.github/workflows/windows-msi.yml +++ b/.github/workflows/windows-msi.yml @@ -42,6 +42,9 @@ jobs: pages: write id-token: write # Required by SignPath + env: + SIGNPATH_API_TOKEN: ${{ secrets.SIGNPATH_API_TOKEN }} + outputs: qt6_msi: ${{ steps.export.outputs.msi_name_qt6 }} @@ -290,7 +293,8 @@ jobs: # PR/run must never attempt a signing request. # (cf. DieterMayerOSS:fix/msi-signing-fork-guard, d3f60c88) - name: Sign MSI via SignPath - if: github.repository == 'qelectrotech/qelectrotech-source-mirror' + id: sign + if: github.repository == 'qelectrotech/qelectrotech-source-mirror' && env.SIGNPATH_API_TOKEN != '' uses: signpath/github-action-submit-signing-request@v2 with: api-token: ${{ secrets.SIGNPATH_API_TOKEN }} @@ -345,7 +349,7 @@ jobs: run: | Write-Host "=== MSI build summary (${{ matrix.flavor }}) ===" Write-Host "Version : ${{ steps.version.outputs.VERSION_DISPLAY }}" - Write-Host "Signed : true" + Write-Host "Signed : ${{ steps.sign.outcome == 'success' }}" # --------------------------------------------------------------------------- # Job 2 : Génère et déploie la page GitHub Pages une fois le MSI publié, From b4fcef1829ea0699cd761c2329298817ff095036 Mon Sep 17 00:00:00 2001 From: Laurent Trinques Date: Thu, 17 Sep 2026 15:57:05 +0200 Subject: [PATCH 17/20] CI: install the Qt SVG icon engine instead of breeze-icon-theme draw-bezier-curves only exists as SVG in the qet icon theme; on Debian/Ubuntu the SVG icon engine lives in qt6-svg-plugins, which --no-install-recommends leaves out. breeze-icon-theme is not needed: the test uses the qet theme compiled into the binary. --- .github/workflows/linux-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linux-build.yml b/.github/workflows/linux-build.yml index 470830a5c..496970357 100644 --- a/.github/workflows/linux-build.yml +++ b/.github/workflows/linux-build.yml @@ -38,7 +38,7 @@ jobs: # that appears to be about QElectroTech and is not. DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ extra-cmake-modules libkf6coreaddons-dev libkf6widgetsaddons-dev \ - breeze-icon-theme + qt6-svg-plugins - uses: actions/checkout@v4 with: From 26285373a3f9e102fbf618368743d57eee1f9dcb Mon Sep 17 00:00:00 2001 From: Andre Rummler Date: Thu, 17 Sep 2026 17:05:15 +0200 Subject: [PATCH 18/20] Fix alignment when pasting an element. --- sources/diagramevent/diagrameventaddpaste.cpp | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/sources/diagramevent/diagrameventaddpaste.cpp b/sources/diagramevent/diagrameventaddpaste.cpp index ccc105192..4835d805a 100644 --- a/sources/diagramevent/diagrameventaddpaste.cpp +++ b/sources/diagramevent/diagrameventaddpaste.cpp @@ -52,14 +52,25 @@ DiagramEventAddPaste::DiagramEventAddPaste(Diagram *diagram, const QPointF &star m_diagram->fromXml(document_xml, Diagram::snapToGrid(start_pos), false, &m_content); if (!m_content.count()) return; - //Remember where each item sits relative to the group's top left, so a - //move is one assignment per item rather than an accumulated delta. - QRectF group_rect; + //Remember where each item sits relative to the group's top left, so a + //move is one assignment per item rather than an accumulated delta. + //Built from pos(), not boundingRect(): the visual bounding box's + //corner is generally not a grid point even when every item's own + //pos() is, which would offset the whole paste off the grid by that + //fractional remainder once anchor gets snapped in moveTo(). const QList movable = m_content.items(MovableItems); + QPointF top_left; + bool first = true; for (auto *item : movable) { - group_rect = group_rect.united(item->mapToScene(item->boundingRect()).boundingRect()); + const QPointF p = item->pos(); + if (first) { + top_left = p; + first = false; + } else { + if (p.x() < top_left.x()) top_left.setX(p.x()); + if (p.y() < top_left.y()) top_left.setY(p.y()); + } } - const QPointF top_left = group_rect.topLeft(); for (auto *item : movable) { m_relative_pos.insert(item, item->pos() - top_left); } From 53a0f07ca3eedf256a33f2fc6c063c6f71b8716a Mon Sep 17 00:00:00 2001 From: Kellermorph Date: Thu, 17 Sep 2026 17:54:57 +0200 Subject: [PATCH 19/20] Fix copy-paste bugs in DiagramEventAddPaste and PasteDiagramCommand Fix multiple issues introduced by commit 55c2c0df9 (interactive paste): Paste placement (diagrameventaddpaste): - Items now load at their original XML coordinates instead of being snapped to the cursor position, preventing jumps on initial placement - Use delta-based movement: record the actual grid-snapped cursor position on first mouseMoveEvent as baseline, then compute grid-snapped deltas from there - Bypass Diagram::snapToGrid() in moveTo() to avoid Ctrl modifier causing pixel-snapping instead of grid-snapping - Remove moveTo() from mouseReleaseEvent to prevent a final jump on click - Warp cursor to group bounding rect origin for visual feedback Paste command (diagramcommands): - Always clear PLC slave data (type, address, function, comment, cross-ref, label, TC, T1-T4) on paste regardless of the erase-label-on-copy user preference - Block alignment (m_block_alignment / blockAlignmentUpdate) before setElementInformations() for both slaves and non-slaves, preventing finishAlignment() from shifting right/center-aligned text items - Clear non-UserText items directly after setElementInformations() for slaves as a safety net --- sources/diagramcommands.cpp | 87 +++++++++++++++---- sources/diagramevent/diagrameventaddpaste.cpp | 86 ++++++++++++++---- sources/diagramevent/diagrameventaddpaste.h | 20 +++-- 3 files changed, 153 insertions(+), 40 deletions(-) diff --git a/sources/diagramcommands.cpp b/sources/diagramcommands.cpp index 3bf1be437..8c19636f6 100644 --- a/sources/diagramcommands.cpp +++ b/sources/diagramcommands.cpp @@ -19,6 +19,7 @@ #include "diagram.h" #include "qetgraphicsitem/conductortextitem.h" +#include "qetgraphicsitem/dynamicelementtextitem.h" #include "qetgraphicsitem/element.h" #include "qetgraphicsitem/elementtextitemgroup.h" #include "qetinformation.h" @@ -90,6 +91,62 @@ void PasteDiagramCommand::redo() //make new uuid, because old uuid are the uuid of the copied element e -> newUuid(); + // PLC slaves carry master-specific data (type, address, + // function, cross-ref, etc.) in their elementInformations. + // Always clear those on paste so the duplicate starts clean, + // regardless of the user's erase-label-on-copy preference. + const bool is_slave = (e->linkType() == Element::Slave); + if (is_slave) { + DiagramContext dc = e->elementInformations(); + dc.remove(QETInformation::ELMT_PLC_TYPE); + dc.remove(QETInformation::ELMT_PLC_ADDRESS); + dc.remove(QETInformation::ELMT_PLC_FUNCTION); + dc.remove(QETInformation::ELMT_PLC_COMMENT); + dc.remove(QETInformation::ELMT_PLC_CROSSREF); + dc.remove(QETInformation::ELMT_LABEL); + dc.remove(QETInformation::ELMT_PLC_TC); + dc.remove(QETInformation::ELMT_PLC_T1); + dc.remove(QETInformation::ELMT_PLC_T2); + dc.remove(QETInformation::ELMT_PLC_T3); + dc.remove(QETInformation::ELMT_PLC_T4); + dc.remove(QStringLiteral("xref")); + + // Block alignment before setElementInformations so + // that elementInfoChanged() resolves texts without + // finishAlignment() shifting right/center-aligned items. + for (DynamicElementTextItem *deti : e->dynamicTextItems()) + deti->m_block_alignment = true; + for (auto *group : e->textGroups()) + group->blockAlignmentUpdate(true); + + e->setElementInformations(dc); + + for (DynamicElementTextItem *deti : e->dynamicTextItems()) + deti->m_block_alignment = false; + for (auto *group : e->textGroups()) + group->blockAlignmentUpdate(false); + + // After setElementInformations, elementInfoChanged() + // resolves composite text with cleaned dc. Clear + // all non-UserText items directly as a safety net. + for (DynamicElementTextItem *deti : e->dynamicTextItems()) { + if (deti->textFrom() != DynamicElementTextItem::UserText) { + deti->m_block_alignment = true; + deti->setPlainText(QString()); + deti->m_block_alignment = false; + } + } + for (auto *group : e->textGroups()) { + for (DynamicElementTextItem *deti : group->texts()) { + if (deti->textFrom() != DynamicElementTextItem::UserText) { + deti->m_block_alignment = true; + deti->setPlainText(QString()); + deti->m_block_alignment = false; + } + } + } + } + if (settings.value("diagramcommands/erase-label-on-copy", true).toBool()) { //Reset the information about the label, the comment and location @@ -99,27 +156,21 @@ void PasteDiagramCommand::redo() dc.addValue("comment", ""); dc.addValue("location", ""); - // PLC slaves store master data (type, address, comment, - // cross-ref, etc.) in their own elementInformations. - // Remove them the same way MasterElement::unlinkElement() - // does, so pasted PLC slaves start clean like regular - // slaves. - if (e->linkType() == Element::Slave) { - dc.remove(QETInformation::ELMT_PLC_TYPE); - dc.remove(QETInformation::ELMT_PLC_ADDRESS); - dc.remove(QETInformation::ELMT_PLC_FUNCTION); - dc.remove(QETInformation::ELMT_PLC_COMMENT); - dc.remove(QETInformation::ELMT_PLC_CROSSREF); - dc.remove(QETInformation::ELMT_LABEL); - dc.remove(QETInformation::ELMT_PLC_TC); - dc.remove(QETInformation::ELMT_PLC_T1); - dc.remove(QETInformation::ELMT_PLC_T2); - dc.remove(QETInformation::ELMT_PLC_T3); - dc.remove(QETInformation::ELMT_PLC_T4); - dc.remove(QStringLiteral("xref")); + // Block alignment during setElementInformations + // for non-slaves, same as Element::fromXml() (line 890-896). + if (!is_slave) { + for (DynamicElementTextItem *deti : e->dynamicTextItems()) + deti->m_block_alignment = true; + for (auto *group : e->textGroups()) + group->blockAlignmentUpdate(true); } e->setElementInformations(dc); + + for (DynamicElementTextItem *deti : e->dynamicTextItems()) + deti->m_block_alignment = false; + for (auto *group : e->textGroups()) + group->blockAlignmentUpdate(false); //Reset the text of conductors, the same way the label/comment/ //location above are reset to "" rather than to some other diff --git a/sources/diagramevent/diagrameventaddpaste.cpp b/sources/diagramevent/diagrameventaddpaste.cpp index ccc105192..44fe49e24 100644 --- a/sources/diagramevent/diagrameventaddpaste.cpp +++ b/sources/diagramevent/diagrameventaddpaste.cpp @@ -23,6 +23,8 @@ #include "../qetdiagrameditor.h" #include "../qetgraphicsitem/conductor.h" +#include + #include #include #include @@ -35,7 +37,7 @@ @param start_pos : where the pasted items first appear, in scene coordinates -- normally the cursor */ -DiagramEventAddPaste::DiagramEventAddPaste(Diagram *diagram, const QPointF &start_pos) : + DiagramEventAddPaste::DiagramEventAddPaste(Diagram *diagram, const QPointF &start_pos) : DiagramEventInterface(diagram) { //DiagramEventInterface::init() is called by Diagram::setEventInterface @@ -49,20 +51,37 @@ DiagramEventAddPaste::DiagramEventAddPaste(Diagram *diagram, const QPointF &star QDomDocument document_xml; if (!document_xml.setContent(clipboard_text)) return; - m_diagram->fromXml(document_xml, Diagram::snapToGrid(start_pos), false, &m_content); + //Load items at their original XML coordinates. + m_diagram->fromXml(document_xml, QPointF(), false, &m_content); if (!m_content.count()) return; - //Remember where each item sits relative to the group's top left, so a - //move is one assignment per item rather than an accumulated delta. - QRectF group_rect; const QList movable = m_content.items(MovableItems); + if (movable.isEmpty()) return; + + //Compute the bounding rect centre — this is where the cursor + //will start. Items stay at their original XML positions; + //moveTo() handles grid-snapped movement via deltas. + QRectF group_rect; for (auto *item : movable) { group_rect = group_rect.united(item->mapToScene(item->boundingRect()).boundingRect()); } - const QPointF top_left = group_rect.topLeft(); + + QSettings settings; + const int xGrid = settings.value(QStringLiteral("diagrameditor/Xgrid"), + Diagram::xGrid).toInt(); + const int yGrid = settings.value(QStringLiteral("diagrameditor/Ygrid"), + Diagram::yGrid).toInt(); + const QPointF grid_origin( + qRound(group_rect.topLeft().x() / xGrid) * xGrid, + qRound(group_rect.topLeft().y() / yGrid) * yGrid); + + //Store each item's position. moveTo() applies a grid-snapped + //delta from the baseline, so items preserve their layout and + //move in whole grid steps. for (auto *item : movable) { - m_relative_pos.insert(item, item->pos() - top_left); + m_relative_pos.insert(item, item->pos()); } + m_group_origin = grid_origin; m_diagram->clearSelection(); for (auto *item : movable) { @@ -70,8 +89,15 @@ DiagramEventAddPaste::DiagramEventAddPaste(Diagram *diagram, const QPointF &star } if (!m_diagram->views().isEmpty()) { - if (const auto qde = QETApp::diagramEditorAncestorOf(m_diagram->views().at(0))) { - m_status_bar = qde->statusBar(); + if (auto *view = m_diagram->views().at(0)) { + if (const auto qde = QETApp::diagramEditorAncestorOf(view)) { + m_status_bar = qde->statusBar(); + } + //Warp the cursor close to the group origin so the + //first mouseMoveEvent captures the correct baseline. + const QPoint view_pos = view->mapFromScene(m_group_origin); + const QPoint global_pos = view->viewport()->mapToGlobal(view_pos); + QCursor::setPos(global_pos); } } showHint(); @@ -131,19 +157,48 @@ void DiagramEventAddPaste::showHint() /** @brief DiagramEventAddPaste::moveTo - Put the group's top left corner at @a scene_pos, snapped to the grid. + Compute a grid-snapped delta from the initial cursor position and + apply it to every item's grid-shifted position. This keeps all + items exactly on grid points regardless of modifier keys or + sub-pixel cursor-warp rounding. */ void DiagramEventAddPaste::moveTo(const QPointF &scene_pos) { - const QPointF anchor = Diagram::snapToGrid(scene_pos); + QSettings settings; + const int xGrid = settings.value(QStringLiteral("diagrameditor/Xgrid"), + Diagram::xGrid).toInt(); + const int yGrid = settings.value(QStringLiteral("diagrameditor/Ygrid"), + Diagram::yGrid).toInt(); + + const auto snapGrid = [xGrid, yGrid](const QPointF &p) -> QPointF { + return QPointF( + qRound(p.x() / xGrid) * xGrid, + qRound(p.y() / yGrid) * yGrid); + }; + + //On the very first call, record the actual grid-snapped + //cursor position as baseline. The cursor warp in the + //constructor goes through integer rounding (mapFromScene → + //QPoint) so the real position may differ slightly from + //m_initial_cursor. Using the actual scene position avoids + //a one-grid-unit jump on the first mouse movement. + if (m_initial_cursor.isNull()) { + m_initial_cursor = snapGrid(scene_pos); + return; + } + + const QPointF delta = snapGrid(scene_pos) - m_initial_cursor; + for (auto it = m_relative_pos.constBegin() ; it != m_relative_pos.constEnd() ; ++it) { if (it.key()) { - it.key()->setPos(anchor + it.value()); + it.key()->setPos(it.value() + delta); } } - const auto conductors = m_content.conductors(); // AnyConductor by default - for (auto *cond : conductors) { - cond->updatePath(); + + //Update conductor paths so they follow the moved terminals. + const QList conductors = m_content.conductors(DiagramContent::AnyConductor); + for (auto *conductor : conductors) { + conductor->updatePath(); } } @@ -168,7 +223,6 @@ void DiagramEventAddPaste::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) event->setAccepted(true); if (event->button() == Qt::LeftButton) { - moveTo(event->scenePos()); commit(); } else if (event->button() == Qt::RightButton) { cancel(); diff --git a/sources/diagramevent/diagrameventaddpaste.h b/sources/diagramevent/diagrameventaddpaste.h index 8c7bcf86f..28ae3854b 100644 --- a/sources/diagramevent/diagrameventaddpaste.h +++ b/sources/diagramevent/diagrameventaddpaste.h @@ -75,12 +75,20 @@ class DiagramEventAddPaste : public DiagramEventInterface void showHint(); void removeItems(); - DiagramContent m_content; - ///Each movable item's position relative to the group's top left, - ///taken once so repeated moves cannot accumulate rounding drift. - QHash m_relative_pos; - QPointer m_status_bar; - bool m_finished{false}; + DiagramContent m_content; + ///Each movable item's position relative to the group's top left, + ///taken once so repeated moves cannot accumulate rounding drift. + QHash m_relative_pos; + ///Top-left corner of the bounding rect of all movable items, + ///in scene coordinates, captured when the paste starts. + QPointF m_group_origin; + ///Cursor position (scene coords) at the moment the paste starts, + ///so delta-based movement can compute offsets from the initial point. + QPointF m_initial_cursor; + ///Set to true once the first moveTo() captures the real cursor position. + bool m_baseline_captured{false}; + QPointer m_status_bar; + bool m_finished{false}; }; #endif // DIAGRAMEVENTADDPASTE_H From 520f4245b375ad4a32efddf7c09d7b7fd8212684 Mon Sep 17 00:00:00 2001 From: Kellermorph Date: Thu, 17 Sep 2026 21:33:05 +0200 Subject: [PATCH 20/20] Fix PDF export checkbox state not being remembered - Save and restore the state of m_component_info_cb, m_fit_in_page_cb, and m_use_full_page_cb via QSettings, in addition to the existing ExportProperties-based checkboxes (border, titleblock, terminals, etc.) - Add savePrintProperties() to persist all checkbox states after print/export under the "print/default" settings prefix - Remove unused QPrinter(HighResolution) local variable in launchDialog() that caused an unnecessary ~2s CUPS round-trip on Linux --- sources/print/projectprintwindow.cpp | 17 ++++++++++++++++- sources/print/projectprintwindow.h | 1 + 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/sources/print/projectprintwindow.cpp b/sources/print/projectprintwindow.cpp index 93de03871..e6b86efa5 100644 --- a/sources/print/projectprintwindow.cpp +++ b/sources/print/projectprintwindow.cpp @@ -56,7 +56,6 @@ void ProjectPrintWindow::launchDialog(QETProject *project, QPrinter::OutputFormat format, QWidget *parent) { auto printer_ = new QPrinter(); - QPrinter printer(QPrinter::HighResolution); printer_->setDocName(ProjectPrintWindow::docName(project)); printer_->setPageOrientation(QPageLayout::Landscape); @@ -150,6 +149,11 @@ ProjectPrintWindow::ProjectPrintWindow(QETProject *project, QPrinter *printer, Q ui->m_draw_terminal_names_cb->setChecked(exp.draw_terminal_names); ui->m_keep_conductor_color_cb->setChecked(exp.draw_colored_conductors); + QSettings settings; + ui->m_component_info_cb->setChecked(settings.value("print/default/componentinfo", false).toBool()); + ui->m_fit_in_page_cb->setChecked(settings.value("print/default/fitinpage", true).toBool()); + ui->m_use_full_page_cb->setChecked(settings.value("print/default/fullpage", false).toBool()); + ui->m_date_cb->blockSignals(true); ui->m_date_cb->setDate(QDate::currentDate()); ui->m_date_cb->blockSignals(false); @@ -657,6 +661,16 @@ void ProjectPrintWindow::loadPageSetupForCurrentPrinter() settings.endGroup(); } +void ProjectPrintWindow::savePrintProperties() +{ + QSettings settings; + exportProperties().toSettings(settings, "print/default"); + settings.setValue("print/default/componentinfo", ui->m_component_info_cb->isChecked()); + settings.setValue("print/default/fitinpage", ui->m_fit_in_page_cb->isChecked()); + settings.setValue("print/default/fullpage", ui->m_use_full_page_cb->isChecked()); + settings.sync(); +} + void ProjectPrintWindow::savePageSetupForCurrentPrinter() { QSettings settings; @@ -842,6 +856,7 @@ void ProjectPrintWindow::print() // is created/destroyed inside that call savePageSetupForCurrentPrinter(); + savePrintProperties(); if (isPdf && !pdfFile.isEmpty()) { // Defer post-processing and window close to the next event-loop diff --git a/sources/print/projectprintwindow.h b/sources/print/projectprintwindow.h index 3d16fce91..c023e0080 100644 --- a/sources/print/projectprintwindow.h +++ b/sources/print/projectprintwindow.h @@ -91,6 +91,7 @@ class ProjectPrintWindow : public QMainWindow void setUpDiagramList(); QString settingsSectionName(const QPrinter *printer); void loadPageSetupForCurrentPrinter(); + void savePrintProperties(); void savePageSetupForCurrentPrinter(); void saveReloadDiagramParameters(Diagram *diagram, const ExportProperties &options, bool save); QList selectedDiagram() const;