From 5bd74b73bb609c9092fcb26f5ba57da85e8d6754 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 15 Aug 2025 17:06:33 +0200 Subject: [PATCH] Enhancement: 'select highlighted objects' from properties dialog (object tree context menu) --- src/ant/ant/antPropertiesPage.cc | 13 +++ src/ant/ant/antPropertiesPage.h | 1 + src/ant/ant/antService.cc | 11 +++ src/ant/ant/antService.h | 7 +- src/edt/edt/edtInstPropertiesPage.cc | 20 +++++ src/edt/edt/edtInstPropertiesPage.h | 1 + src/edt/edt/edtPropertiesPages.cc | 23 +++++- src/edt/edt/edtPropertiesPages.h | 1 + src/img/img/imgPropertiesPage.cc | 13 +++ src/img/img/imgPropertiesPage.h | 1 + src/img/img/imgService.cc | 11 +++ src/img/img/imgService.h | 7 +- src/laybasic/laybasic/layProperties.h | 8 ++ src/layui/layui/PropertiesDialog.ui | 11 +++ src/layui/layui/layPropertiesDialog.cc | 109 ++++++++++++++++++++++++- src/layui/layui/layPropertiesDialog.h | 1 + 16 files changed, 231 insertions(+), 7 deletions(-) diff --git a/src/ant/ant/antPropertiesPage.cc b/src/ant/ant/antPropertiesPage.cc index 15e19315e..964deaf08 100644 --- a/src/ant/ant/antPropertiesPage.cc +++ b/src/ant/ant/antPropertiesPage.cc @@ -419,6 +419,19 @@ PropertiesPage::description () const return tl::to_string (tr ("Rulers and Annotations")); } +void +PropertiesPage::confine_selection (const std::vector &remaining_entries) +{ + std::vector org_selection; + m_selection.swap (org_selection); + for (auto i = remaining_entries.begin (); i != remaining_entries.end (); ++i) { + m_selection.push_back (org_selection [*i]); + } + + mp_rulers->set_selection (m_selection); + mp_rulers->clear_highlights (); +} + void PropertiesPage::leave () { diff --git a/src/ant/ant/antPropertiesPage.h b/src/ant/ant/antPropertiesPage.h index c98c42d91..e239b1fa5 100644 --- a/src/ant/ant/antPropertiesPage.h +++ b/src/ant/ant/antPropertiesPage.h @@ -47,6 +47,7 @@ public: virtual void select_entries (const std::vector &entries); virtual std::string description (size_t entry) const; virtual std::string description () const; + virtual void confine_selection (const std::vector &remaining_entries); virtual void update (); virtual void leave (); virtual bool readonly (); diff --git a/src/ant/ant/antService.cc b/src/ant/ant/antService.cc index b1ea0441d..bccdb0a8d 100644 --- a/src/ant/ant/antService.cc +++ b/src/ant/ant/antService.cc @@ -2672,6 +2672,17 @@ Service::get_selection (std::vector &sel) const } } +void +Service::set_selection (const std::vector &selection) +{ + m_selected.clear (); + for (auto i = selection.begin (); i != selection.end (); ++i) { + m_selected.insert (std::make_pair (*i, 0)); + } + + selection_to_view (); +} + void Service::delete_ruler (obj_iterator pos) { diff --git a/src/ant/ant/antService.h b/src/ant/ant/antService.h index 3bbee87a8..89fe680a2 100644 --- a/src/ant/ant/antService.h +++ b/src/ant/ant/antService.h @@ -381,10 +381,15 @@ public: #endif /** - * @brief Get the selection for the properties page + * @brief Gets the selection for the properties page */ void get_selection (std::vector &selection) const; + /** + * @brief Sets the selection for the properties page + */ + void set_selection (const std::vector &selection); + /** * @brief Direct access to the selection */ diff --git a/src/edt/edt/edtInstPropertiesPage.cc b/src/edt/edt/edtInstPropertiesPage.cc index de27a4d90..f910d307f 100644 --- a/src/edt/edt/edtInstPropertiesPage.cc +++ b/src/edt/edt/edtInstPropertiesPage.cc @@ -330,6 +330,26 @@ InstPropertiesPage::description () const return tl::to_string (tr ("Instances")); } +void +InstPropertiesPage::confine_selection (const std::vector &remaining_entries) +{ + std::vector new_selection; + for (auto i = remaining_entries.begin (); i != remaining_entries.end (); ++i) { + new_selection.push_back (*m_selection_ptrs [*i]); + } + + mp_service->set_selection (new_selection.begin (), new_selection.end ()); + + m_selection_ptrs.clear (); + m_selection_ptrs.reserve (mp_service->selection_size ()); + for (edt::EditableSelectionIterator s = mp_service->begin_selection (); ! s.at_end (); ++s) { + m_selection_ptrs.push_back (s.operator-> ()); + } + + m_prop_id = 0; + mp_service->clear_highlights (); +} + void InstPropertiesPage::leave () { diff --git a/src/edt/edt/edtInstPropertiesPage.h b/src/edt/edt/edtInstPropertiesPage.h index f00a1a659..694627a9d 100644 --- a/src/edt/edt/edtInstPropertiesPage.h +++ b/src/edt/edt/edtInstPropertiesPage.h @@ -51,6 +51,7 @@ public: virtual void select_entries (const std::vector &entries); virtual std::string description (size_t entry) const; virtual std::string description () const; + virtual void confine_selection (const std::vector &remaining_entries); virtual void leave (); private: diff --git a/src/edt/edt/edtPropertiesPages.cc b/src/edt/edt/edtPropertiesPages.cc index 26b1def30..5cdbeb85d 100644 --- a/src/edt/edt/edtPropertiesPages.cc +++ b/src/edt/edt/edtPropertiesPages.cc @@ -137,12 +137,33 @@ ShapePropertiesPage::icon (size_t entry, int w, int h) const return QIcon (); } + std::string ShapePropertiesPage::description () const { return m_description; } +void +ShapePropertiesPage::confine_selection (const std::vector &remaining_entries) +{ + std::vector new_selection; + for (auto i = remaining_entries.begin (); i != remaining_entries.end (); ++i) { + new_selection.push_back (*m_selection_ptrs [*i]); + } + + mp_service->set_selection (new_selection.begin (), new_selection.end ()); + + m_selection_ptrs.clear (); + m_selection_ptrs.reserve (mp_service->selection_size ()); + for (edt::EditableSelectionIterator s = mp_service->begin_selection (); ! s.at_end (); ++s) { + m_selection_ptrs.push_back (s.operator-> ()); + } + + m_prop_id = 0; + mp_service->clear_highlights (); +} + void ShapePropertiesPage::leave () { @@ -661,7 +682,7 @@ BoxPropertiesPage::description (size_t entry) const return ShapePropertiesPage::description (entry) + " - " + tl::sprintf (tl::to_string (tr ("Box%s")), (dbu_trans * sh.box ()).to_string ()); } -void +void BoxPropertiesPage::do_update (const db::Shape &shape, double dbu, const std::string &lname) { m_dbu = dbu; diff --git a/src/edt/edt/edtPropertiesPages.h b/src/edt/edt/edtPropertiesPages.h index 60d844f50..5e575cb16 100644 --- a/src/edt/edt/edtPropertiesPages.h +++ b/src/edt/edt/edtPropertiesPages.h @@ -54,6 +54,7 @@ public: virtual void select_entries (const std::vector &entries); virtual std::string description (size_t entry) const; virtual std::string description () const; + virtual void confine_selection (const std::vector &remaining_entries); virtual QIcon icon (size_t entry, int w, int h) const; virtual QIcon icon (int w, int h) const { return lay::PropertiesPage::icon (w, h); } virtual void leave (); diff --git a/src/img/img/imgPropertiesPage.cc b/src/img/img/imgPropertiesPage.cc index 056e7e5ce..b5663d8aa 100644 --- a/src/img/img/imgPropertiesPage.cc +++ b/src/img/img/imgPropertiesPage.cc @@ -192,6 +192,19 @@ PropertiesPage::description () const return tl::to_string (tr ("Images")); } +void +PropertiesPage::confine_selection (const std::vector &remaining_entries) +{ + std::vector org_selection; + m_selection.swap (org_selection); + for (auto i = remaining_entries.begin (); i != remaining_entries.end (); ++i) { + m_selection.push_back (org_selection [*i]); + } + + mp_service->set_selection (m_selection); + mp_service->clear_highlights (); +} + void PropertiesPage::leave () { diff --git a/src/img/img/imgPropertiesPage.h b/src/img/img/imgPropertiesPage.h index e715f99cd..32f453a1e 100644 --- a/src/img/img/imgPropertiesPage.h +++ b/src/img/img/imgPropertiesPage.h @@ -54,6 +54,7 @@ public: virtual void select_entries (const std::vector &entries); virtual std::string description (size_t entry) const; virtual std::string description () const; + virtual void confine_selection (const std::vector &remaining_entries); virtual void update (); virtual void leave (); virtual bool readonly (); diff --git a/src/img/img/imgService.cc b/src/img/img/imgService.cc index 3b1c85589..9e5f6b072 100644 --- a/src/img/img/imgService.cc +++ b/src/img/img/imgService.cc @@ -1402,6 +1402,17 @@ Service::get_selection (std::vector &sel) const } } +void +Service::set_selection (const std::vector &selection) +{ + m_selected.clear (); + for (auto i = selection.begin (); i != selection.end (); ++i) { + m_selected.insert (std::make_pair (*i, 0)); + } + + selection_to_view (); +} + void Service::erase_image (obj_iterator pos) { diff --git a/src/img/img/imgService.h b/src/img/img/imgService.h index 42b1cabea..4e637c987 100644 --- a/src/img/img/imgService.h +++ b/src/img/img/imgService.h @@ -377,10 +377,15 @@ public: #endif /** - * @brief Get the selection for the properties page + * @brief Gets the selection for the properties page */ void get_selection (std::vector &selection) const; + /** + * @brief Sets the selection for the properties page + */ + void set_selection (const std::vector &selection); + /** * @brief Direct access to the selection */ diff --git a/src/laybasic/laybasic/layProperties.h b/src/laybasic/laybasic/layProperties.h index 09b16c2b9..fc0e7cb08 100644 --- a/src/laybasic/laybasic/layProperties.h +++ b/src/laybasic/laybasic/layProperties.h @@ -119,6 +119,14 @@ public: return QIcon (); } + /** + * @brief Confines the selection to the given selection indexes + * + * After this operation, the selection entries are renumbered with only + * the remaining ones present. + */ + virtual void confine_selection (const std::vector &remaining_entries) = 0; + /** * @brief Update the display * diff --git a/src/layui/layui/PropertiesDialog.ui b/src/layui/layui/PropertiesDialog.ui index 10f1b72b1..a38872469 100644 --- a/src/layui/layui/PropertiesDialog.ui +++ b/src/layui/layui/PropertiesDialog.ui @@ -26,6 +26,9 @@ 0 + + Qt::ActionsContextMenu + QAbstractItemView::ExtendedSelection @@ -198,6 +201,14 @@ + + + Select highlighted rows + + + Reduce selection to highlighted rows + + diff --git a/src/layui/layui/layPropertiesDialog.cc b/src/layui/layui/layPropertiesDialog.cc index 7f6a938c8..4e29f628d 100644 --- a/src/layui/layui/layPropertiesDialog.cc +++ b/src/layui/layui/layPropertiesDialog.cc @@ -160,6 +160,16 @@ public: emit dataChanged (index (0, 0, QModelIndex ()), index (rowCount (QModelIndex ()) - 1, columnCount (QModelIndex ()) - 1, QModelIndex ())); } + void begin_reset_model () + { + beginResetModel (); + } + + void end_reset_model () + { + endResetModel (); + } + private: PropertiesDialog *mp_dialog; int m_icon_width, m_icon_height; @@ -202,9 +212,6 @@ PropertiesDialog::PropertiesDialog (QWidget * /*parent*/, db::Manager *manager, connect (mp_properties_pages [i], SIGNAL (edited ()), this, SLOT (properties_edited ())); } - // Necessary to maintain the page order for UI regression testing of 0.18 vs. 0.19 (because tl::Collection has changed to order) .. - std::reverse (mp_properties_pages.begin (), mp_properties_pages.end ()); - // Add a label as a dummy mp_none = new QLabel (QObject::tr ("No object with properties to display"), mp_ui->content_frame); mp_none->setAlignment (Qt::AlignHCenter | Qt::AlignVCenter); @@ -225,7 +232,6 @@ PropertiesDialog::PropertiesDialog (QWidget * /*parent*/, db::Manager *manager, update_title (); - // if at end disable the "Next" button and return (this may only happen at the first call) mp_tree_model = new PropertiesTreeModel (this, mp_ui->tree->iconSize ().width (), mp_ui->tree->iconSize ().height ()); mp_ui->tree->setModel (mp_tree_model); #if QT_VERSION >= 0x50000 @@ -235,6 +241,8 @@ PropertiesDialog::PropertiesDialog (QWidget * /*parent*/, db::Manager *manager, #endif mp_ui->tree->expandAll (); + mp_ui->tree->addAction (mp_ui->action_reduce_selection); + if (mp_properties_pages.empty ()) { mp_ui->tree->hide (); } @@ -255,6 +263,7 @@ PropertiesDialog::PropertiesDialog (QWidget * /*parent*/, db::Manager *manager, connect (mp_ui->prev_button, SIGNAL (clicked ()), this, SLOT (prev_pressed ())); connect (mp_ui->next_button, SIGNAL (clicked ()), this, SLOT (next_pressed ())); connect (mp_ui->apply_to_all_cbx, SIGNAL (clicked ()), this, SLOT (apply_to_all_pressed ())); + connect (mp_ui->action_reduce_selection, SIGNAL (triggered ()), this, SLOT (reduce_selection ())); connect (mp_ui->tree->selectionModel (), SIGNAL (currentChanged (const QModelIndex &, const QModelIndex &)), this, SLOT (current_index_changed (const QModelIndex &, const QModelIndex &))); connect (mp_ui->tree->selectionModel (), SIGNAL (selectionChanged(const QItemSelection &, const QItemSelection &)), this, SLOT (selection_changed ())); } @@ -311,6 +320,98 @@ PropertiesDialog::apply_to_all_pressed () m_signals_enabled = true; } +void +PropertiesDialog::reduce_selection () +{ +BEGIN_PROTECTED + + // apply pending changes + if (m_index >= 0 && m_index < int (mp_properties_pages.size ()) && ! mp_properties_pages [m_index]->readonly ()) { + + db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes")), m_transaction_id); + + mp_properties_pages [m_index]->apply (true); + mp_properties_pages [m_index]->update (); + + if (! t.is_empty ()) { + m_transaction_id = t.id (); + } + + } + + // confine the selection + + mp_tree_model->begin_reset_model (); + + auto selection = mp_ui->tree->selectionModel ()->selectedIndexes (); + + for (std::vector ::iterator p = mp_properties_pages.begin (); p != mp_properties_pages.end (); ++p) { + + int page_index = int (p - mp_properties_pages.begin ()); + std::vector object_indexes; + + for (auto i = selection.begin (); i != selection.end (); ++i) { + if (mp_tree_model->parent (*i).isValid () && mp_tree_model->page_index (*i) == page_index) { + object_indexes.push_back (size_t (mp_tree_model->object_index (*i))); + } + } + + (*p)->confine_selection (object_indexes); + + } + + m_signals_enabled = false; + + // rebuild the properties pages as only the ones with remaining selection are shown + + for (std::vector ::iterator p = mp_properties_pages.begin (); p != mp_properties_pages.end (); ++p) { + delete *p; + } + mp_properties_pages.clear (); + m_index = -1; + + for (lay::Editables::iterator e = mp_editables->begin (); e != mp_editables->end (); ++e) { + auto pp = e->properties_pages (mp_manager, mp_ui->content_frame); + for (auto p = pp.begin (); p != pp.end (); ++p) { + if ((*p)->count () == 0) { + delete *p; + } else { + mp_properties_pages.push_back (*p); + } + } + } + for (size_t i = 0; i < mp_properties_pages.size (); ++i) { + mp_stack->addWidget (mp_properties_pages [i]); + connect (mp_properties_pages [i], SIGNAL (edited ()), this, SLOT (properties_edited ())); + } + + // count the total number of objects + m_objects = mp_editables->selection_size (); + m_current_object = 0; + + // look for next usable editable + m_index = 0; + m_object_indexes.clear (); + if (m_index >= int (mp_properties_pages.size ())) { + m_index = -1; + } else { + m_object_indexes.push_back (0); + } + + mp_tree_model->end_reset_model (); + + update_title (); + + mp_ui->tree->expandAll (); + mp_ui->tree->setCurrentIndex (mp_tree_model->index_for (m_index, 0)); + + m_signals_enabled = true; + + update_controls (); + +END_PROTECTED +} + void PropertiesDialog::selection_changed () { diff --git a/src/layui/layui/layPropertiesDialog.h b/src/layui/layui/layPropertiesDialog.h index f9282c0b4..88836860c 100644 --- a/src/layui/layui/layPropertiesDialog.h +++ b/src/layui/layui/layPropertiesDialog.h @@ -113,6 +113,7 @@ public slots: void cancel_pressed (); void ok_pressed (); void apply_to_all_pressed (); + void reduce_selection (); void current_index_changed (const QModelIndex &index, const QModelIndex &previous); void selection_changed ();