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 @@ Q_OBJECT 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..37ead1bac 100644 --- a/src/ant/ant/antService.cc +++ b/src/ant/ant/antService.cc @@ -1410,8 +1410,8 @@ Service::begin_move (lay::Editable::MoveMode mode, const db::DPoint &p, lay::ang double dmin = std::numeric_limits ::max (); const ant::Object *robj_min = 0; - for (std::map::const_iterator r = m_selected.begin (); r != m_selected.end (); ++r) { - const ant::Object *robj = dynamic_cast ((*r->first).ptr ()); + for (auto r = m_selected.begin (); r != m_selected.end (); ++r) { + const ant::Object *robj = dynamic_cast ((*r)->ptr ()); if (robj) { double d; if (is_selected (*robj, p, l, d)) { @@ -1425,9 +1425,9 @@ Service::begin_move (lay::Editable::MoveMode mode, const db::DPoint &p, lay::ang // further investigate what part to drag - for (std::map::const_iterator r = m_selected.begin (); r != m_selected.end (); ++r) { + for (auto r = m_selected.begin (); r != m_selected.end (); ++r) { - obj_iterator ri = r->first; + obj_iterator ri = *r; const ant::Object *robj = dynamic_cast ((*ri).ptr ()); if (robj && (! robj_min || robj == robj_min)) { @@ -1435,7 +1435,7 @@ Service::begin_move (lay::Editable::MoveMode mode, const db::DPoint &p, lay::ang // found anything: make the moved ruler the selection clear_selection (); - m_selected.insert (std::make_pair (ri, 0)); + m_selected.insert (ri); m_current = *robj; m_original = m_current; m_rulers.push_back (new ant::View (this, &m_current, true)); @@ -1492,7 +1492,7 @@ Service::begin_move (lay::Editable::MoveMode mode, const db::DPoint &p, lay::ang // found anything: make the moved ruler the selection clear_selection (); - m_selected.insert (std::make_pair (mp_view->annotation_shapes ().iterator_from_pointer (&*r), 0)); + m_selected.insert (mp_view->annotation_shapes ().iterator_from_pointer (&*r)); m_current = *robj; m_original = m_current; m_rulers.push_back (new ant::View (this, &m_current, true)); @@ -1667,16 +1667,16 @@ Service::end_move (const db::DPoint &, lay::angle_constraint_type) if (m_move_mode == MoveSelected) { // replace the rulers that were moved: - for (std::map::const_iterator s = m_selected.begin (); s != m_selected.end (); ++s) { + for (auto s = m_selected.begin (); s != m_selected.end (); ++s) { - const ant::Object *robj = dynamic_cast (s->first->ptr ()); + const ant::Object *robj = dynamic_cast ((*s)->ptr ()); if (robj) { // compute moved object and replace ant::Object *rnew = new ant::Object (*robj); rnew->transform (m_trans); int new_id = rnew->id (); - mp_view->annotation_shapes ().replace (s->first, db::DUserObject (rnew)); + mp_view->annotation_shapes ().replace (*s, db::DUserObject (rnew)); annotation_changed_event (new_id); } @@ -1690,7 +1690,7 @@ Service::end_move (const db::DPoint &, lay::angle_constraint_type) // replace the ruler that was moved m_current.clean_points (); - mp_view->annotation_shapes ().replace (m_selected.begin ()->first, db::DUserObject (new ant::Object (m_current))); + mp_view->annotation_shapes ().replace (*m_selected.begin (), db::DUserObject (new ant::Object (m_current))); annotation_changed_event (m_current.id ()); // clear the selection (that was artifically created before) @@ -1717,9 +1717,8 @@ Service::selection_to_view () } m_rulers.clear (); m_rulers.reserve (m_selected.size ()); - for (std::map::iterator r = m_selected.begin (); r != m_selected.end (); ++r) { - r->second = (unsigned int) m_rulers.size (); - const ant::Object *robj = dynamic_cast (r->first->ptr ()); + for (auto r = m_selected.begin (); r != m_selected.end (); ++r) { + const ant::Object *robj = dynamic_cast ((*r)->ptr ()); m_rulers.push_back (new ant::View (this, robj, true /*selected*/)); } } @@ -1728,8 +1727,8 @@ db::DBox Service::selection_bbox () { db::DBox box; - for (std::map::iterator r = m_selected.begin (); r != m_selected.end (); ++r) { - const ant::Object *robj = dynamic_cast (r->first->ptr ()); + for (auto r = m_selected.begin (); r != m_selected.end (); ++r) { + const ant::Object *robj = dynamic_cast ((*r)->ptr ()); if (robj) { box += robj->box (); } @@ -1741,16 +1740,16 @@ void Service::transform (const db::DCplxTrans &trans) { // replace the rulers that were transformed: - for (std::map::const_iterator s = m_selected.begin (); s != m_selected.end (); ++s) { + for (auto s = m_selected.begin (); s != m_selected.end (); ++s) { - const ant::Object *robj = dynamic_cast (s->first->ptr ()); + const ant::Object *robj = dynamic_cast ((*s)->ptr ()); if (robj) { // compute transformed object and replace int id = robj->id (); ant::Object *rnew = new ant::Object (*robj); rnew->transform (trans); - mp_view->annotation_shapes ().replace (s->first, db::DUserObject (rnew)); + mp_view->annotation_shapes ().replace (*s, db::DUserObject (rnew)); annotation_changed_event (id); } @@ -2173,9 +2172,8 @@ void Service::copy_selected () { // extract all selected rulers and paste in "micron" space - for (std::map::iterator r = m_selected.begin (); r != m_selected.end (); ++r) { - r->second = (unsigned int) m_rulers.size (); - const ant::Object *robj = dynamic_cast (r->first->ptr ()); + for (auto r = m_selected.begin (); r != m_selected.end (); ++r) { + const ant::Object *robj = dynamic_cast ((*r)->ptr ()); if (robj) { db::Clipboard::instance () += new db::ClipboardValue (*robj); } @@ -2212,7 +2210,7 @@ Service::paste () if (! new_objects.empty ()) { for (auto r = new_objects.begin (); r != new_objects.end (); ++r) { - m_selected.insert (std::make_pair (mp_view->annotation_shapes ().iterator_from_pointer (*r), 0)); + m_selected.insert (mp_view->annotation_shapes ().iterator_from_pointer (*r)); } selection_to_view (); @@ -2240,8 +2238,8 @@ Service::del_selected () // positions will hold a set of iterators that are to be erased std::vector positions; positions.reserve (m_selected.size ()); - for (std::map::iterator r = m_selected.begin (); r != m_selected.end (); ++r) { - positions.push_back (r->first); + for (auto r = m_selected.begin (); r != m_selected.end (); ++r) { + positions.push_back (*r); } // clear selection @@ -2276,7 +2274,7 @@ Service::select (obj_iterator obj, lay::Editable::SelectionMode mode) if (mode == lay::Editable::Replace || mode == lay::Editable::Add) { // select if (m_selected.find (obj) == m_selected.end ()) { - m_selected.insert (std::make_pair (obj, 0)); + m_selected.insert (obj); return true; } } else if (mode == lay::Editable::Reset) { @@ -2290,7 +2288,7 @@ Service::select (obj_iterator obj, lay::Editable::SelectionMode mode) if (m_selected.find (obj) != m_selected.end ()) { m_selected.erase (obj); } else { - m_selected.insert (std::make_pair (obj, 0)); + m_selected.insert (obj); } return true; } @@ -2312,7 +2310,7 @@ Service::click_proximity (const db::DPoint &pos, lay::Editable::SelectionMode mo // for single-point selections either exclude the current selection or the // accumulated previous selection from the search. - const std::map *exclude = 0; + const std::set *exclude = 0; if (mode == lay::Editable::Replace) { exclude = &m_previous_selection; } else if (mode == lay::Editable::Add) { @@ -2493,7 +2491,7 @@ Service::transient_to_selection () for (lay::AnnotationShapes::iterator r = mp_view->annotation_shapes ().begin (); r != mp_view->annotation_shapes ().end (); ++r) { const ant::Object *robj = dynamic_cast (r->ptr ()); if (robj == mp_transient_ruler->ruler ()) { - m_selected.insert (std::make_pair (r, 0)); + m_selected.insert (r); selection_to_view (); return; } @@ -2523,7 +2521,7 @@ Service::select (const db::DBox &box, lay::Editable::SelectionMode mode) // for single-point selections either exclude the current selection or the // accumulated previous selection from the search. - const std::map *exclude = 0; + const std::set *exclude = 0; if (mode == lay::Editable::Replace) { exclude = &m_previous_selection; } else if (mode == lay::Editable::Add) { @@ -2605,7 +2603,7 @@ Service::select (const db::DBox &box, lay::Editable::SelectionMode mode) // select the one that was found if (any_selected) { select (mp_view->annotation_shapes ().iterator_from_pointer (&*rmin), mode); - m_previous_selection.insert (std::make_pair (mp_view->annotation_shapes ().iterator_from_pointer (&*rmin), mode)); + m_previous_selection.insert (mp_view->annotation_shapes ().iterator_from_pointer (&*rmin)); needs_update = true; } @@ -2667,11 +2665,22 @@ Service::get_selection (std::vector &sel) const sel.reserve (m_selected.size ()); // positions will hold a set of iterators that are to be erased - for (std::map::const_iterator r = m_selected.begin (); r != m_selected.end (); ++r) { - sel.push_back (r->first); + for (auto r = m_selected.begin (); r != m_selected.end (); ++r) { + sel.push_back (*r); } } +void +Service::set_selection (const std::vector &selection) +{ + m_selected.clear (); + for (auto i = selection.begin (); i != selection.end (); ++i) { + m_selected.insert (*i); + } + + 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..509d00289 100644 --- a/src/ant/ant/antService.h +++ b/src/ant/ant/antService.h @@ -381,14 +381,19 @@ Q_OBJECT #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 */ - const std::map &selection () const + const std::set &selection () const { return m_selected; } @@ -558,9 +563,9 @@ public slots: // and the moved rules in move mode std::vector m_rulers; // The selection - std::map m_selected; + std::set m_selected; // The previous selection - std::map m_previous_selection; + std::set m_previous_selection; // The reference point in move mode db::DPoint m_p1; // The transformation in MoveSelection mode diff --git a/src/ant/ant/gsiDeclAnt.cc b/src/ant/ant/gsiDeclAnt.cc index a86f84ae7..e5087c2e6 100644 --- a/src/ant/ant/gsiDeclAnt.cc +++ b/src/ant/ant/gsiDeclAnt.cc @@ -1263,7 +1263,7 @@ class AnnotationSelectionIterator { public: typedef AnnotationRef value_type; - typedef std::map::const_iterator iterator_type; + typedef std::set::const_iterator iterator_type; typedef void pointer; typedef value_type reference; typedef std::forward_iterator_tag iterator_category; @@ -1292,7 +1292,7 @@ class AnnotationSelectionIterator reference operator* () const { - return value_type (*(static_cast (m_iter->first->ptr ())), m_services[m_service]->view ()); + return value_type (*(static_cast ((*m_iter)->ptr ())), m_services[m_service]->view ()); } private: diff --git a/src/db/db/dbCompoundOperation.cc b/src/db/db/dbCompoundOperation.cc index 80445387f..393010cd1 100644 --- a/src/db/db/dbCompoundOperation.cc +++ b/src/db/db/dbCompoundOperation.cc @@ -308,7 +308,10 @@ CompoundRegionMultiInputOperationNode::CompoundRegionMultiInputOperationNode (co (*c)->keep (); m_children.push_back (*c); } - init (); + + if (! no_init) { + init (); + } } CompoundRegionMultiInputOperationNode::CompoundRegionMultiInputOperationNode () 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 @@ Q_OBJECT 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 @@ Q_OBJECT 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/ImagePropertiesPage.ui b/src/img/img/ImagePropertiesPage.ui index 2ea522bdd..fbb6b8a88 100644 --- a/src/img/img/ImagePropertiesPage.ui +++ b/src/img/img/ImagePropertiesPage.ui @@ -72,7 +72,6 @@ Sans Serif 12 - 75 false true false @@ -108,200 +107,251 @@ 6 - + + + + degree + + + + h = - - + + - - + + - x = + degree + + + + + + + w = Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - + + - - - - Perspective + + + + Qt::Vertical - - - - + + QSizePolicy::Fixed + + + + 507 + 10 + + + - - + + + + Qt::Vertical + + + + 20 + 5 + + + - - + + - degree + (use them to align the image with the layout) - - + + - - + + - - + + - Shear angle + Rotation angle - - + + - Pixel Size + x = + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + degree - - + + - micron + Landmarks - - + + - Rotation angle + micron - - + + micron - - + + - w = - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Perspective - + micron - - + + + + + - Center + y = - - + + - x = + Define - - + + - Mirrored (at X-axis) + x = - - + + - micron + micron - - - - - + + - degree + Shear angle - + Qt::Vertical + + QSizePolicy::Fixed + - 20 + 507 5 - - + + + + + - y = + Pixel Size - - + + - degree + Mirrored (at X-axis) - - - - Qt::Vertical + + + + degree - - QSizePolicy::Fixed + + + + + + + + + Center - - - 507 - 16 - + + + + + + y = - + - - + + - - + + + + Layer binding + + + + + Qt::Vertical @@ -310,38 +360,69 @@ - 507 + 20 5 - - - - y = + + + + + 0 + 0 + - - - - - - Landmarks + + QFrame::NoFrame - - - - - - Define - - - - - - - (use them to align the image with the layout) + + QFrame::Raised + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Visibility of the image follows that layer's visibility + + + QComboBox::AdjustToContents + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + @@ -1111,6 +1192,11 @@ p, li { white-space: pre-wrap; }
imgWidgets.h
1 + + lay::LayerSelectionComboBox + QComboBox +
layWidgets.h
+
width_le diff --git a/src/img/img/gsiDeclImg.cc b/src/img/img/gsiDeclImg.cc index 3ec2e3cd8..13c4e5987 100644 --- a/src/img/img/gsiDeclImg.cc +++ b/src/img/img/gsiDeclImg.cc @@ -811,13 +811,13 @@ gsi::Class decl_Image (decl_BasicImage, "lay", "Image", "@brief Sets the mask from a array of boolean values\n" "The order of the boolean values is line first, from bottom to top and left to right and is the same as the order in the data array.\n" "\n" - "This method has been introduced in version 0.27.\n" + "This attribute has been introduced in version 0.27.\n" ) + gsi::method_ext ("mask_data", &get_mask_data, "@brief Gets the mask from a array of boolean values\n" "See \\set_mask_data for a description of the data field.\n" "\n" - "This method has been introduced in version 0.27.\n" + "This attribute has been introduced in version 0.27.\n" ) + gsi::method_ext ("pixel_width=", &img_set_pixel_width, gsi::arg ("w"), "@brief Sets the pixel width\n" @@ -826,7 +826,7 @@ gsi::Class decl_Image (decl_BasicImage, "lay", "Image", "micron space with the transformation.\n" "\n" "Starting with version 0.22, this property is incorporated into the transformation matrix.\n" - "This property is provided for convenience only." + "This attribute is provided for convenience only." ) + gsi::method_ext ("pixel_width", &img_get_pixel_width, "@brief Gets the pixel width\n" @@ -834,7 +834,7 @@ gsi::Class decl_Image (decl_BasicImage, "lay", "Image", "See \\pixel_width= for a description of that property.\n" "\n" "Starting with version 0.22, this property is incorporated into the transformation matrix.\n" - "This property is provided for convenience only." + "This attribute is provided for convenience only." ) + gsi::method_ext ("pixel_height=", &img_set_pixel_height, gsi::arg ("h"), "@brief Sets the pixel height\n" @@ -843,7 +843,7 @@ gsi::Class decl_Image (decl_BasicImage, "lay", "Image", "micron space with the transformation.\n" "\n" "Starting with version 0.22, this property is incorporated into the transformation matrix.\n" - "This property is provided for convenience only." + "This attribute is provided for convenience only." ) + gsi::method_ext ("pixel_height", &img_get_pixel_height, "@brief Gets the pixel height\n" @@ -851,21 +851,35 @@ gsi::Class decl_Image (decl_BasicImage, "lay", "Image", "See \\pixel_height= for a description of that property.\n" "\n" "Starting with version 0.22, this property is incorporated into the transformation matrix.\n" - "This property is provided for convenience only." + "This attribute is provided for convenience only." ) + gsi::method ("z_position", &ImageRef::z_position, "@brief Gets the z position of the image\n" "Images with a higher z position are painted in front of images with lower z position.\n" "The z value is an integer that controls the position relative to other images.\n" "\n" - "This method was introduced in version 0.25." + "This attribute was introduced in version 0.25." ) + gsi::method ("z_position=", &ImageRef::set_z_position, gsi::arg ("z"), "@brief Sets the z position of the image\n" "\n" "See \\z_position for details about the z position attribute.\n" "\n" - "This method was introduced in version 0.25." + "This attribute was introduced in version 0.25." + ) + + gsi::method ("layer_binding", &ImageRef::layer_binding, + "@brief Gets the layer binding of the image\n" + "If this attribute is set to a non-null layer properties object, the images visibility " + "is associated with that of this layer. If the layer becomes invisible, the image is hidden as well.\n" + "\n" + "This attribute was introduced in version 0.30.4." + ) + + gsi::method ("layer_binding=", &ImageRef::set_layer_binding, gsi::arg ("lp"), + "@brief Sets the layer binding of the image\n" + "\n" + "See \\layer_binding for details about the layer_binding attribute.\n" + "\n" + "This attribute was introduced in version 0.30.4." ) + gsi::method ("matrix=", &ImageRef::set_matrix, gsi::arg ("t"), "@brief Sets the transformation matrix\n" @@ -876,7 +890,7 @@ gsi::Class decl_Image (decl_BasicImage, "lay", "Image", "The matrix is more general than the transformation used before and supports shear and perspective transformation. This property replaces the \\trans property which is " "still functional, but deprecated.\n" "\n" - "This method has been introduced in version 0.22." + "This attribute has been introduced in version 0.22." ) + gsi::method ("matrix", &ImageRef::matrix, "@brief Returns the pixel-to-micron transformation matrix\n" @@ -887,7 +901,7 @@ gsi::Class decl_Image (decl_BasicImage, "lay", "Image", "The matrix is more general than the transformation used before and supports shear and perspective transformation. This property replaces the \\trans property which is " "still functional, but deprecated.\n" "\n" - "This method has been introduced in version 0.22." + "This attribute has been introduced in version 0.22." ) + gsi::method_ext ("trans", &img_get_trans, "@brief Returns the pixel-to-micron transformation\n" @@ -941,14 +955,14 @@ gsi::Class decl_Image (decl_BasicImage, "lay", "Image", "\n" "See the \\is_visible? method for a description of this property.\n" "\n" - "This method has been introduced in version 0.20.\n" + "This attribute has been introduced in version 0.20.\n" ) + gsi::method ("is_visible?", &ImageRef::is_visible, "@brief Gets a flag indicating whether the image object is visible\n" "\n" "An image object can be made invisible by setting the visible property to false.\n" "\n" - "This method has been introduced in version 0.20.\n" + "This attribute has been introduced in version 0.20.\n" ) + gsi::method ("id", (size_t (ImageRef::*) () const) &ImageRef::id, "@brief Gets the Id\n" @@ -1272,7 +1286,7 @@ class SelectionIterator { public: typedef ImageRef value_type; - typedef std::map::const_iterator iterator_type; + typedef std::set::const_iterator iterator_type; typedef void pointer; typedef value_type reference; typedef std::forward_iterator_tag iterator_category; @@ -1301,7 +1315,7 @@ class SelectionIterator value_type operator* () const { - return value_type (*(dynamic_cast (m_iter->first->ptr ())), m_services[m_service]->view ()); + return value_type (*(dynamic_cast ((*m_iter)->ptr ())), m_services[m_service]->view ()); } private: diff --git a/src/img/img/imgObject.cc b/src/img/img/imgObject.cc index ab76374f3..ae0b508c2 100644 --- a/src/img/img/imgObject.cc +++ b/src/img/img/imgObject.cc @@ -1020,6 +1020,7 @@ Object::operator= (const img::Object &d) m_visible = d.m_visible; m_z_position = d.m_z_position; + m_layer_binding = d.m_layer_binding; m_min_value = d.m_min_value; m_min_value_set = d.m_min_value_set; @@ -1052,6 +1053,10 @@ Object::less (const db::DUserObjectBase *d) const return m_z_position < img_object->m_z_position; } + if (m_layer_binding != img_object->m_layer_binding) { + return m_layer_binding < img_object->m_layer_binding; + } + double epsilon = (std::abs (m_min_value) + std::abs (m_max_value)) * 1e-6; if (std::abs (m_min_value - img_object->m_min_value) > epsilon) { return m_min_value < img_object->m_min_value; @@ -1102,7 +1107,11 @@ Object::operator== (const img::Object &d) const return false; } - // operator== is all fuzzy compare - + if (m_layer_binding != d.m_layer_binding) { + return false; + } + + // operator== is all fuzzy compare - double epsilon = (std::abs (m_min_value) + std::abs (m_max_value)) * 1e-6; if (std::abs (m_min_value - d.m_min_value) > epsilon) { return false; @@ -1424,6 +1433,8 @@ Object::from_string (const char *str, const char *base_dir) ex.read (h); } else if (ex.test ("is_visible=")) { ex.read (m_visible); + } else if (ex.test ("layer_binding=")) { + ex.read (m_layer_binding); } else if (ex.test ("z_position=")) { ex.read (m_z_position); } else if (ex.test ("min_value=")) { @@ -1814,6 +1825,12 @@ Object::to_string () const os << tl::to_string (m_z_position); os << ";"; + if (m_layer_binding != db::LayerProperties ()) { + os << "layer_binding="; + os << m_layer_binding.to_string (); + os << ";"; + } + os << "brightness="; os << tl::to_string (data_mapping ().brightness); os << ";"; @@ -1953,6 +1970,7 @@ Object::swap (Object &other) std::swap (mp_pixel_data, other.mp_pixel_data); m_landmarks.swap (other.m_landmarks); std::swap (m_z_position, other.m_z_position); + std::swap (m_layer_binding, other.m_layer_binding); std::swap (m_updates_enabled, other.m_updates_enabled); } diff --git a/src/img/img/imgObject.h b/src/img/img/imgObject.h index 214ea6c17..db1e719c8 100644 --- a/src/img/img/imgObject.h +++ b/src/img/img/imgObject.h @@ -31,6 +31,7 @@ #include "dbTrans.h" #include "dbMatrix.h" #include "dbPolygon.h" +#include "dbLayerProperties.h" #include "tlDataMapping.h" #include "tlColor.h" #include "tlPixelBuffer.h" @@ -924,6 +925,30 @@ class IMG_PUBLIC Object return m_z_position; } + /** + * @brief Sets the layer binding + * + * If the image is bound to a layer, it becomes hidden when the layer is hidden + * and visible if the layer is visible too + */ + void set_layer_binding (const db::LayerProperties &lp) + { + if (m_layer_binding != lp) { + m_layer_binding = lp; + if (m_updates_enabled) { + property_changed (); + } + } + } + + /** + * @brief Gets the layer binding + */ + const db::LayerProperties &layer_binding () const + { + return m_layer_binding; + } + /** * @brief Get the RGB pixel data sets obtained by applying the LUT's */ @@ -1021,6 +1046,7 @@ class IMG_PUBLIC Object mutable const tl::color_t *mp_pixel_data; std::vector m_landmarks; int m_z_position; + db::LayerProperties m_layer_binding; bool m_updates_enabled; void release (); diff --git a/src/img/img/imgPropertiesPage.cc b/src/img/img/imgPropertiesPage.cc index 056e7e5ce..77da34a13 100644 --- a/src/img/img/imgPropertiesPage.cc +++ b/src/img/img/imgPropertiesPage.cc @@ -109,6 +109,8 @@ PropertiesPage::init () colors->setEnabled (false); value_le->setEnabled (false); + attach_service (mp_service); + connect (browse_pb, SIGNAL (clicked ()), this, SLOT (browse ())); connect (colors, SIGNAL (color_changed (std::pair)), false_color_control, SLOT (set_current_color (std::pair))); connect (false_color_control, SIGNAL (selection_changed (std::pair)), colors, SLOT (set_color (std::pair))); @@ -147,6 +149,20 @@ PropertiesPage::init () connect (define_landmarks_pb, SIGNAL (clicked ()), this, SLOT (define_landmarks_pressed ())); } +void +PropertiesPage::attach_service (img::Service *service) +{ + layer_binding_cbx->set_new_layer_enabled (false); + layer_binding_cbx->set_no_layer_available (true); + + if (service && service->view ()) { + int cv_index = service->view ()->active_cellview_index (); + layer_binding_cbx->set_view (service->view (), cv_index, true); + } else { + layer_binding_cbx->set_view (0, -1); + } +} + void PropertiesPage::invalidate () { @@ -192,6 +208,23 @@ PropertiesPage::description () const return tl::to_string (tr ("Images")); } +void +PropertiesPage::confine_selection (const std::vector &remaining_entries) +{ + if (! mp_service) { + return; + } + + 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 () { @@ -446,6 +479,8 @@ PropertiesPage::update () to_le->setText (tl::to_qstring (tl::to_string (mp_direct_image->max_value ()))); to_le->setCursorPosition (0); + layer_binding_cbx->set_current_layer (mp_direct_image->layer_binding ()); + false_color_control->set_nodes (mp_direct_image->data_mapping ().false_color_nodes); brightness_slider->setValue (int (floor (mp_direct_image->data_mapping ().brightness * 100 + 0.5))); @@ -895,6 +930,8 @@ PropertiesPage::apply (bool /*commit*/) mp_direct_image->set_min_value (xmin); mp_direct_image->set_max_value (xmax); + mp_direct_image->set_layer_binding (layer_binding_cbx->current_layer_props ()); + img::DataMapping dm (mp_direct_image->data_mapping ()); dm.brightness = brightness_sb->value () * 0.01; dm.contrast = contrast_sb->value () * 0.01; diff --git a/src/img/img/imgPropertiesPage.h b/src/img/img/imgPropertiesPage.h index e715f99cd..dbfd3525b 100644 --- a/src/img/img/imgPropertiesPage.h +++ b/src/img/img/imgPropertiesPage.h @@ -54,12 +54,14 @@ Q_OBJECT 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 (); virtual void apply (bool commit); void set_direct_image (img::Object *image); + void attach_service (img::Service *service); private slots: void browse (); diff --git a/src/img/img/imgService.cc b/src/img/img/imgService.cc index 3b1c85589..fc45ffe99 100644 --- a/src/img/img/imgService.cc +++ b/src/img/img/imgService.cc @@ -58,10 +58,11 @@ class AddNewImageDialog public Ui::AddNewImageDialog { public: - AddNewImageDialog (QWidget *parent, img::Object *image_object) + AddNewImageDialog (QWidget *parent, img::Service *service, img::Object *image_object) : QDialog (parent), mp_image_object (image_object) { setupUi (this); + properties_frame->attach_service (service); properties_frame->set_direct_image (image_object); properties_frame->update (); } @@ -290,7 +291,7 @@ void View::render (const lay::Viewport &vp, lay::ViewObjectCanvas &canvas) { const img::Object *image = image_object (); - if (! image) { + if (! image || ! image->is_visible ()) { return; } @@ -419,12 +420,16 @@ Service::Service (db::Manager *manager, lay::LayoutViewBase *view) m_move_mode (Service::move_none), m_moved_landmark (0), m_keep_selection_for_move (false), - m_images_visible (true) + m_images_visible (true), + m_visibility_cache_valid (false) { // place images behind the grid z_order (-1); mp_view->annotations_changed_event.add (this, &Service::annotations_changed); + mp_view->layer_list_changed_event.add (this, &Service::layer_list_changed); + mp_view->active_cellview_changed_event.add (this, &Service::layer_visibilty_changed); + mp_view->current_layer_list_changed_event.add (this, &Service::current_layer_list_changed); } Service::~Service () @@ -436,9 +441,20 @@ Service::~Service () clear_transient_selection (); } +void +Service::layer_visibilty_changed () +{ + if (m_visibility_cache_valid && ! m_visibility_cache.empty ()) { + view ()->redraw_deco_layer (); + } + m_visibility_cache_valid = false; +} + void Service::annotations_changed () { + m_visibility_cache_valid = false; + // NOTE: right now, we don't differentiate: every annotation change may be a change in an image too. // We just forward this event as a potential image changed event images_changed_event (); @@ -449,7 +465,7 @@ Service::show_images (bool f) { if (m_images_visible != f) { m_images_visible = f; - view ()->redraw (); + view ()->redraw_deco_layer (); } } @@ -609,11 +625,11 @@ Service::begin_move (lay::Editable::MoveMode mode, const db::DPoint &p, lay::ang } else if (mode == lay::Editable::Partial) { // test, whether we are moving a handle of one selected object - for (std::map::const_iterator s = m_selected.begin (); s != m_selected.end (); ++s) { + for (auto s = m_selected.begin (); s != m_selected.end (); ++s) { MoveMode mm = move_none; size_t ml = 0; - obj_iterator si = s->first; + obj_iterator si = *s; const img::Object *iobj = dynamic_cast ((*si).ptr ()); if (iobj && dragging_what (iobj, search_dbox, mm, ml, m_p1) && mm != move_all) { @@ -624,7 +640,7 @@ Service::begin_move (lay::Editable::MoveMode mode, const db::DPoint &p, lay::ang // found a handle of one of the selected object: make the moved image the selection clear_selection (); - m_selected.insert (std::make_pair (si, 0)); + m_selected.insert (si); m_current = *iobj; m_initial = m_current; m_selected_image_views.push_back (new img::View (this, &m_current, img::View::mode_transient_move)); @@ -661,7 +677,7 @@ Service::begin_move (lay::Editable::MoveMode mode, const db::DPoint &p, lay::ang // found anything: make the moved image the selection clear_selection (); - m_selected.insert (std::make_pair (mp_view->annotation_shapes ().iterator_from_pointer (robj), 0)); + m_selected.insert (mp_view->annotation_shapes ().iterator_from_pointer (robj)); m_current = *iobj; m_initial = m_current; m_selected_image_views.push_back (new img::View (this, &m_current, img::View::mode_transient_move)); @@ -865,15 +881,15 @@ Service::end_move (const db::DPoint &, lay::angle_constraint_type) if (m_move_mode == move_selected) { // replace the images that were moved: - for (std::map::const_iterator s = m_selected.begin (); s != m_selected.end (); ++s) { + for (auto s = m_selected.begin (); s != m_selected.end (); ++s) { - const img::Object *iobj = dynamic_cast (s->first->ptr ()); + const img::Object *iobj = dynamic_cast ((*s)->ptr ()); // compute moved object and replace // KLUDGE: this creates a copy of the data! img::Object *inew = new img::Object (*iobj); inew->transform (m_trans); - int id = obj2id (mp_view->annotation_shapes ().replace (s->first, db::DUserObject (inew))); + int id = obj2id (mp_view->annotation_shapes ().replace (*s, db::DUserObject (inew))); image_changed_event (id); @@ -886,7 +902,7 @@ Service::end_move (const db::DPoint &, lay::angle_constraint_type) // replace the image that was moved img::Object *inew = new img::Object (m_current); - int id = obj2id (mp_view->annotation_shapes ().replace (m_selected.begin ()->first, db::DUserObject (inew))); + int id = obj2id (mp_view->annotation_shapes ().replace (*m_selected.begin (), db::DUserObject (inew))); image_changed_event (id); // clear the selection (that was artificially created before) @@ -900,7 +916,7 @@ Service::end_move (const db::DPoint &, lay::angle_constraint_type) // replace the image that was moved img::Object *inew = new img::Object (m_current); - int id = obj2id (mp_view->annotation_shapes ().replace (m_selected.begin ()->first, db::DUserObject (inew))); + int id = obj2id (mp_view->annotation_shapes ().replace (*m_selected.begin (), db::DUserObject (inew))); image_changed_event (id); // clear the selection (that was artificially created before) @@ -918,8 +934,63 @@ Service::end_move (const db::DPoint &, lay::angle_constraint_type) m_move_mode = move_none; } +bool +Service::image_is_visible (const img::Object *image) +{ + if (! image->is_visible ()) { + return false; + } + + if (! m_visibility_cache_valid) { + + std::vector images_with_bindings; + + for (obj_iterator user_object = mp_view->annotation_shapes ().begin (); user_object != mp_view->annotation_shapes ().end (); ++user_object) { + const img::Object *i = dynamic_cast ((*user_object).ptr ()); + if (i && ! i->layer_binding ().is_null ()) { + images_with_bindings.push_back (i); + } + } + + m_visibility_cache.clear (); + + if (! images_with_bindings.empty ()) { + + for (auto img = images_with_bindings.begin (); img != images_with_bindings.end (); ++img) { + m_visibility_cache.insert (std::make_pair (*img, true)); + } + + int cv_index = view ()->active_cellview_index (); + if (cv_index < 0) { + cv_index = 0; + } + + const lay::LayerPropertiesList &lp = view ()->get_properties (); + for (auto i = lp.begin_const_recursive (); ! i.at_end (); ++i) { + if (! i->has_children ()) { + const lay::ParsedLayerSource &source = i->source (true); + if (source.cv_index () == cv_index) { + for (auto img = images_with_bindings.begin (); img != images_with_bindings.end (); ++img) { + if (source.layer_props ().log_equal ((*img)->layer_binding ())) { + m_visibility_cache [*img] = i->visible (true); + } + } + } + } + } + + } + + m_visibility_cache_valid = true; + + } + + auto i = m_visibility_cache.find (image); + return i != m_visibility_cache.end () ? i->second : true; +} + const db::DUserObject * -Service::find_image (const db::DPoint &p, const db::DBox &search_box, double l, double &dmin, const std::map *exclude) +Service::find_image (const db::DPoint &p, const db::DBox &search_box, double l, double &dmin, const std::set *exclude) { if (! m_images_visible) { return 0; @@ -931,7 +1002,7 @@ Service::find_image (const db::DPoint &p, const db::DBox &search_box, double l, lay::AnnotationShapes::touching_iterator r = mp_view->annotation_shapes ().begin_touching (search_box); while (! r.at_end ()) { const img::Object *image = dynamic_cast ((*r).ptr ()); - if (image && image->is_visible () && (! exclude || exclude->find (mp_view->annotation_shapes ().iterator_from_pointer (&*r)) == exclude->end ())) { + if (image && image_is_visible (image) && (! exclude || exclude->find (mp_view->annotation_shapes ().iterator_from_pointer (&*r)) == exclude->end ())) { images.push_back (&*r); } ++r; @@ -967,9 +1038,8 @@ Service::selection_to_view (img::View::Mode mode) m_selected_image_views.clear (); m_selected_image_views.reserve (m_selected.size ()); - for (std::map::iterator r = m_selected.begin (); r != m_selected.end (); ++r) { - r->second = (unsigned int) m_selected_image_views.size (); - m_selected_image_views.push_back (new img::View (this, r->first, mode)); + for (auto r = m_selected.begin (); r != m_selected.end (); ++r) { + m_selected_image_views.push_back (new img::View (this, *r, mode)); } } @@ -977,8 +1047,8 @@ db::DBox Service::selection_bbox () { db::DBox box; - for (std::map::iterator r = m_selected.begin (); r != m_selected.end (); ++r) { - const img::Object *iobj = dynamic_cast (r->first->ptr ()); + for (auto r = m_selected.begin (); r != m_selected.end (); ++r) { + const img::Object *iobj = dynamic_cast ((*r)->ptr ()); if (iobj) { box += iobj->box (); } @@ -990,14 +1060,14 @@ void Service::transform (const db::DCplxTrans &trans) { // replace the images that were transformed: - for (std::map::const_iterator s = m_selected.begin (); s != m_selected.end (); ++s) { + for (auto s = m_selected.begin (); s != m_selected.end (); ++s) { - const img::Object *iobj = dynamic_cast (s->first->ptr ()); + const img::Object *iobj = dynamic_cast ((*s)->ptr ()); // compute transformed object and replace img::Object *inew = new img::Object (*iobj); inew->transform (trans); - int id = obj2id (mp_view->annotation_shapes ().replace (s->first, db::DUserObject (inew))); + int id = obj2id (mp_view->annotation_shapes ().replace (*s, db::DUserObject (inew))); image_changed_event (id); } @@ -1037,9 +1107,8 @@ void Service::copy_selected () { // extract all selected images and paste in "micron" space - for (std::map::iterator r = m_selected.begin (); r != m_selected.end (); ++r) { - r->second = (unsigned int) m_selected_image_views.size (); - const img::Object *iobj = dynamic_cast (r->first->ptr ()); + for (auto r = m_selected.begin (); r != m_selected.end (); ++r) { + const img::Object *iobj = dynamic_cast ((*r)->ptr ()); db::Clipboard::instance () += new db::ClipboardValue (*iobj); } } @@ -1077,8 +1146,8 @@ Service::del_selected () // positions will hold a set of iterators that are to be erased std::vector positions; positions.reserve (m_selected.size ()); - for (std::map::iterator r = m_selected.begin (); r != m_selected.end (); ++r) { - positions.push_back (r->first); + for (auto r = m_selected.begin (); r != m_selected.end (); ++r) { + positions.push_back (*r); } // clear selection @@ -1117,7 +1186,7 @@ void Service::transient_to_selection () { if (mp_transient_view) { - m_selected.insert (std::make_pair (mp_transient_view->image_ref (), 0)); + m_selected.insert (mp_transient_view->image_ref ()); selection_to_view (); } } @@ -1128,7 +1197,7 @@ Service::select (obj_iterator obj, lay::Editable::SelectionMode mode) if (mode == lay::Editable::Replace || mode == lay::Editable::Add) { // select if (m_selected.find (obj) == m_selected.end ()) { - m_selected.insert (std::make_pair (obj, 0)); + m_selected.insert (obj); return true; } } else if (mode == lay::Editable::Reset) { @@ -1142,7 +1211,7 @@ Service::select (obj_iterator obj, lay::Editable::SelectionMode mode) if (m_selected.find (obj) != m_selected.end ()) { m_selected.erase (obj); } else { - m_selected.insert (std::make_pair (obj, 0)); + m_selected.insert (obj); } return true; } @@ -1179,7 +1248,7 @@ Service::click_proximity (const db::DPoint &pos, lay::Editable::SelectionMode mo // for single-point selections either exclude the current selection or the // accumulated previous selection from the search. - const std::map *exclude = 0; + const std::set *exclude = 0; if (mode == lay::Editable::Replace) { exclude = &m_previous_selection; } else if (mode == lay::Editable::Add) { @@ -1273,7 +1342,7 @@ Service::select (const db::DBox &box, lay::Editable::SelectionMode mode) // for single-point selections either exclude the current selection or the // accumulated previous selection from the search. - const std::map *exclude = 0; + const std::set *exclude = 0; if (mode == lay::Editable::Replace) { exclude = &m_previous_selection; } else if (mode == lay::Editable::Add) { @@ -1319,7 +1388,7 @@ Service::select (const db::DBox &box, lay::Editable::SelectionMode mode) lay::AnnotationShapes::touching_iterator r = mp_view->annotation_shapes ().begin_touching (search_dbox); while (! r.at_end ()) { const img::Object *iobj = dynamic_cast ((*r).ptr ()); - if (iobj && iobj->is_visible () && (! exclude || exclude->find (mp_view->annotation_shapes ().iterator_from_pointer (&*r)) == exclude->end ())) { + if (iobj && image_is_visible (iobj) && (! exclude || exclude->find (mp_view->annotation_shapes ().iterator_from_pointer (&*r)) == exclude->end ())) { if (is_selected (*iobj, box)) { any_selected = true; if (select (mp_view->annotation_shapes ().iterator_from_pointer (&*r), mode)) { @@ -1339,7 +1408,7 @@ Service::select (const db::DBox &box, lay::Editable::SelectionMode mode) // select the one that was found if (robj) { select (mp_view->annotation_shapes ().iterator_from_pointer (robj), mode); - m_previous_selection.insert (std::make_pair (mp_view->annotation_shapes ().iterator_from_pointer (robj), mode)); + m_previous_selection.insert (mp_view->annotation_shapes ().iterator_from_pointer (robj)); needs_update = true; } @@ -1397,9 +1466,20 @@ Service::get_selection (std::vector &sel) const sel.reserve (m_selected.size ()); // positions will hold a set of iterators that are to be erased - for (std::map::const_iterator r = m_selected.begin (); r != m_selected.end (); ++r) { - sel.push_back (r->first); + for (auto r = m_selected.begin (); r != m_selected.end (); ++r) { + sel.push_back (*r); + } +} + +void +Service::set_selection (const std::vector &selection) +{ + m_selected.clear (); + for (auto i = selection.begin (); i != selection.end (); ++i) { + m_selected.insert (*i); } + + selection_to_view (); } void @@ -1454,7 +1534,7 @@ Service::render_bg (const lay::Viewport &vp, lay::ViewObjectCanvas &canvas) lay::AnnotationShapes::touching_iterator user_object = mp_view->annotation_shapes ().begin_touching (vp.box ()); while (! user_object.at_end ()) { const img::Object *image = dynamic_cast ((*user_object).ptr ()); - if (image && image->is_visible ()) { + if (image && image_is_visible (image)) { images.push_back (image); } ++user_object; @@ -1597,7 +1677,7 @@ Service::add_image () #if defined(HAVE_QT) img::Object *new_image = new img::Object (); - AddNewImageDialog dialog (QApplication::activeWindow (), new_image); + AddNewImageDialog dialog (QApplication::activeWindow (), this, new_image); if (dialog.exec ()) { clear_selection (); diff --git a/src/img/img/imgService.h b/src/img/img/imgService.h index 42b1cabea..9e5d1cc14 100644 --- a/src/img/img/imgService.h +++ b/src/img/img/imgService.h @@ -377,14 +377,19 @@ class IMG_PUBLIC Service #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 */ - const std::map &selection () const + const std::set &selection () const { return m_selected; } @@ -452,6 +457,11 @@ class IMG_PUBLIC Service return m_images_visible; } + /** + * @brief Returns a value indicating whether the given image is visible + */ + bool image_is_visible (const img::Object *image); + /** * @brief Implement the menu response function */ @@ -486,11 +496,10 @@ class IMG_PUBLIC Service // The view objects representing the selection and the moved images in move mode std::vector m_selected_image_views; - // The present views - only used for issueing a proper // The selection - std::map m_selected; + std::set m_selected; // The previous selection - std::map m_previous_selection; + std::set m_previous_selection; // The reference point in move mode db::DPoint m_p1; // The image object representing the image being moved as it was before it was moved @@ -509,6 +518,9 @@ class IMG_PUBLIC Service bool m_keep_selection_for_move; // Flag indicating whether images are visible bool m_images_visible; + // The visibility cache for the layer bindings + bool m_visibility_cache_valid; + std::map m_visibility_cache; void show_message (); @@ -541,7 +553,7 @@ class IMG_PUBLIC Service /** * @brief Finds an image object from the given point */ - const db::DUserObject *find_image (const db::DPoint &p, const db::DBox &search_box, double l, double &dmin, const std::map *exclude = 0); + const db::DUserObject *find_image (const db::DPoint &p, const db::DBox &search_box, double l, double &dmin, const std::set *exclude = 0); /** * @brief Update m_selected_image_views to reflect the selection @@ -574,6 +586,27 @@ class IMG_PUBLIC Service * @brief Event handler for changes in the annotations */ void annotations_changed (); + + /** + * @brief Event handler for changes in the layer visibility + */ + void layer_list_changed (int) + { + layer_visibilty_changed (); + } + + /** + * @brief Event handler for a change of the current layer list + */ + void current_layer_list_changed (int) + { + layer_visibilty_changed (); + } + + /** + * @brief Common handler for a potential change in layer visibility + */ + void layer_visibilty_changed (); }; } diff --git a/src/lay/lay/layMainConfigPages.cc b/src/lay/lay/layMainConfigPages.cc index 41afe1814..67f793e23 100644 --- a/src/lay/lay/layMainConfigPages.cc +++ b/src/lay/lay/layMainConfigPages.cc @@ -85,6 +85,7 @@ class MainPluginDeclaration options.push_back (std::pair (cfg_reader_options_show_always, "false")); options.push_back (std::pair (cfg_assistant_bookmarks, "")); options.push_back (std::pair (cfg_always_exit_without_saving, "false")); + options.push_back (std::pair (cfg_properties_dialog_relative_mode, "true")); } virtual std::vector > config_pages (QWidget *parent) const diff --git a/src/laybasic/laybasic/layLayoutViewBase.cc b/src/laybasic/laybasic/layLayoutViewBase.cc index f870f7dc5..a11ddf17a 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.cc +++ b/src/laybasic/laybasic/layLayoutViewBase.cc @@ -2642,7 +2642,9 @@ LayoutViewBase::signal_apply_technology (lay::LayoutHandle *layout_handle) // remove all references to the cellview in the layer properties for (unsigned int lindex = 0; lindex < layer_lists (); ++lindex) { - m_layer_properties_lists [lindex]->remove_cv_references (i); + lay::LayerPropertiesList props = *m_layer_properties_lists [lindex]; + props.remove_cv_references (i); + set_properties (lindex, props); } // if a layer properties file is set, create the layer properties now 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 @@ Q_OBJECT 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/laybasic/laybasic/laybasicConfig.h b/src/laybasic/laybasic/laybasicConfig.h index eb99c9d4b..47ec7a75f 100644 --- a/src/laybasic/laybasic/laybasicConfig.h +++ b/src/laybasic/laybasic/laybasicConfig.h @@ -158,6 +158,8 @@ static const std::string cfg_guiding_shape_color ("guiding-shape-color"); static const std::string cfg_guiding_shape_line_width ("guiding-shape-line-width"); static const std::string cfg_guiding_shape_vertex_size ("guiding-shape-vertex-size"); +static const std::string cfg_properties_dialog_relative_mode ("properties-dialog-relative-mode"); + } #endif 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 edd85844b..4e29f628d 100644 --- a/src/layui/layui/layPropertiesDialog.cc +++ b/src/layui/layui/layPropertiesDialog.cc @@ -27,6 +27,8 @@ #include "tlLog.h" #include "layEditable.h" #include "layProperties.h" +#include "layDispatcher.h" +#include "laybasicConfig.h" #include "tlExceptions.h" #include "ui_PropertiesDialog.h" @@ -158,6 +160,16 @@ class PropertiesTreeModel 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; @@ -200,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); @@ -223,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 @@ -233,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 (); } @@ -246,11 +256,14 @@ PropertiesDialog::PropertiesDialog (QWidget * /*parent*/, db::Manager *manager, mp_ui->apply_to_all_cbx->setChecked (false); mp_ui->relative_cbx->setChecked (true); + fetch_config (); + connect (mp_ui->ok_button, SIGNAL (clicked ()), this, SLOT (ok_pressed ())); connect (mp_ui->cancel_button, SIGNAL (clicked ()), this, SLOT (cancel_pressed ())); 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 ())); } @@ -263,6 +276,26 @@ PropertiesDialog::~PropertiesDialog () disconnect (); } +void +PropertiesDialog::fetch_config () +{ + if (! lay::Dispatcher::instance ()) { + return; + } + + bool rm = true; + lay::Dispatcher::instance ()->config_get (cfg_properties_dialog_relative_mode, rm); + mp_ui->relative_cbx->setChecked (rm); +} + +void +PropertiesDialog::store_config () +{ + if (lay::Dispatcher::instance ()) { + lay::Dispatcher::instance ()->config_set (cfg_properties_dialog_relative_mode, mp_ui->relative_cbx->isChecked ()); + } +} + void PropertiesDialog::disconnect () { @@ -287,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 () { @@ -617,6 +742,7 @@ PropertiesDialog::cancel_pressed () } + store_config (); // make sure that the property pages are no longer used .. disconnect (); // close the dialog @@ -641,6 +767,7 @@ BEGIN_PROTECTED } + store_config (); // make sure that the property pages are no longer used .. disconnect (); QDialog::accept (); @@ -651,6 +778,7 @@ END_PROTECTED void PropertiesDialog::reject () { + store_config (); // make sure that the property pages are no longer used .. disconnect (); QDialog::reject (); diff --git a/src/layui/layui/layPropertiesDialog.h b/src/layui/layui/layPropertiesDialog.h index 01e09db45..88836860c 100644 --- a/src/layui/layui/layPropertiesDialog.h +++ b/src/layui/layui/layPropertiesDialog.h @@ -99,6 +99,8 @@ Q_OBJECT const std::vector &properties_pages () { return mp_properties_pages; } void disconnect (); + void fetch_config (); + void store_config (); bool any_prev () const; bool any_next () const; void update_title (); @@ -111,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 (); diff --git a/testdata/ruby/imgObject.rb b/testdata/ruby/imgObject.rb index 38d9b85e7..c7a716eb6 100644 --- a/testdata/ruby/imgObject.rb +++ b/testdata/ruby/imgObject.rb @@ -212,6 +212,14 @@ def test_2 image = copy1 assert_equal(image.to_s, "color:matrix=(0,-2.5,-2.75) (2.5,0,7.5) (0,0,1);min_value=0;max_value=1;is_visible=true;z_position=0;brightness=0;contrast=0;gamma=1;red_gain=1;green_gain=1;blue_gain=1;color_mapping=[0,'#000000';1,'#ffffff';];width=2;height=3;data=[0,1,0;0.5,1.5,0;1.5,2.5,0;2.5,3.5,0;10,0,0;20,0,0;]") + assert_equal(image.layer_binding.to_s, "") + image.layer_binding = RBA::LayerInfo::new(17, 42) + assert_equal(image.layer_binding.to_s, "17/42") + assert_equal(image.to_s, "color:matrix=(0,-2.5,-2.75) (2.5,0,7.5) (0,0,1);min_value=0;max_value=1;is_visible=true;z_position=0;layer_binding=17/42;brightness=0;contrast=0;gamma=1;red_gain=1;green_gain=1;blue_gain=1;color_mapping=[0,'#000000';1,'#ffffff';];width=2;height=3;data=[0,1,0;0.5,1.5,0;1.5,2.5,0;2.5,3.5,0;10,0,0;20,0,0;]") + assert_equal(RBA::Image::from_s(image.to_s).to_s, image.to_s) + image.layer_binding = RBA::LayerInfo::new + assert_equal(image.layer_binding.to_s, "") + dm = image.data_mapping.dup dm.brightness=0.5 image.data_mapping = dm