From f653764dc0a43bec099815559900536ec9041292 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 4 Aug 2024 18:55:53 +0200 Subject: [PATCH 1/9] Bugfix (typo): ParameterState#icon was named 'tooltip' --- src/db/db/gsiDeclDbLibrary.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/db/gsiDeclDbLibrary.cc b/src/db/db/gsiDeclDbLibrary.cc index a476dfba0..69eb095bc 100644 --- a/src/db/db/gsiDeclDbLibrary.cc +++ b/src/db/db/gsiDeclDbLibrary.cc @@ -339,7 +339,7 @@ Class decl_PCellParameterState ("db", "PCellParameterState", gsi::method("icon=", &db::ParameterState::set_icon, gsi::arg ("i"), "@brief Sets the icon for the parameter\n" ) + - gsi::method("tooltip", &db::ParameterState::tooltip, + gsi::method("icon", &db::ParameterState::icon, "@brief Gets the icon for the parameter\n" ), "@brief Provides access to the attributes of a single parameter within \\PCellParameterStates.\n" From 35bd336d3a1003d3f561f1e6eebe593761f0ea23 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 4 Aug 2024 23:36:44 +0200 Subject: [PATCH 2/9] Added test for PCellParameterState --- testdata/ruby/dbPCells.rb | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/testdata/ruby/dbPCells.rb b/testdata/ruby/dbPCells.rb index 36fa4d5ce..0c192726c 100644 --- a/testdata/ruby/dbPCells.rb +++ b/testdata/ruby/dbPCells.rb @@ -900,5 +900,41 @@ def test_12 end +class DBPCellParameterStates_TestClass < TestBase + + def test_1 + + ps = RBA::PCellParameterState::new + + ps.value = 17 + assert_equal(ps.value, 17) + ps.value = "u" + assert_equal(ps.value, "u") + + ps.visible = true + assert_equal(ps.is_visible?, true) + ps.visible = false + assert_equal(ps.is_visible?, false) + + ps.enabled = true + assert_equal(ps.is_enabled?, true) + ps.enabled = false + assert_equal(ps.is_enabled?, false) + + ps.readonly = true + assert_equal(ps.is_readonly?, true) + ps.readonly = false + assert_equal(ps.is_readonly?, false) + + ps.tooltip = "uvw" + assert_equal(ps.tooltip, "uvw") + + ps.icon = RBA::PCellParameterState::InfoIcon + assert_equal(ps.icon, RBA::PCellParameterState::InfoIcon) + + end + +end + load("test_epilogue.rb") From 3068dd2798da245ab1739091306b833dfea0cb4e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 6 Aug 2024 00:14:02 +0200 Subject: [PATCH 3/9] Allowing RBA::Net objects for DRCLayer#nets as well --- src/drc/drc/built-in-macros/_drc_layer.rb | 15 +++++++++++---- testdata/drc/drcSimpleTests_70.drc | 5 ++++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index b06b04e77..5cf3ecd8b 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -5186,6 +5186,7 @@ def map_props(arg) # @brief Pulls net shapes from selected or all nets, optionally annotating nets with properties # @synopsis layer.nets # @synopsis layer.nets(net_filter) + # @synopsis layer.nets(net_object) # @synopsis layer.nets(circuit_filter, net_filter) # @synopsis layer.nets(netter, ...) # @synopsis layer.nets(prop(key), ...) @@ -5202,7 +5203,7 @@ def map_props(arg) # complete - subnets from subcircuits are not selected. The net name is taken from # the net's home circuit (to topmost location where all net connections are formed). # You can specify a circuit filter to select nets from certain circuits only or - # give a RBA::Circuit object explicitly. + # give a RBA::Circuit object explicitly. You can also specify RBA::Net objects directly. # # @code # connect(metal1, via1) @@ -5230,6 +5231,8 @@ def nets(*args) @engine._context("nets") do + nets = nil + # parse arguments filters = nil circuits = nil @@ -5241,6 +5244,9 @@ def nets(*args) filters << a elsif a.is_a?(1.class) prop_id = a + elsif a.is_a?(RBA::Net) + nets ||= [] + nets << a elsif a.is_a?(RBA::Circuit) circuits ||= [] circuits << a @@ -5272,13 +5278,14 @@ def nets(*args) circuits ||= [] circuits += netlist.circuits_by_name(circuit_filter) end - nets = nil if !circuits if filters - nets = filters.collect { |f| netlist.nets_by_name(f) }.flatten + nets ||= [] + nets += filters.collect { |f| netlist.nets_by_name(f) }.flatten end else - nets = circuits.collect do |circuit| + nets ||= [] + nets += circuits.collect do |circuit| (filters || ["*"]).collect { |f| circuit.nets_by_name(f) }.flatten end.flatten end diff --git a/testdata/drc/drcSimpleTests_70.drc b/testdata/drc/drcSimpleTests_70.drc index c39b4556f..b46ad66e4 100644 --- a/testdata/drc/drcSimpleTests_70.drc +++ b/testdata/drc/drcSimpleTests_70.drc @@ -68,11 +68,14 @@ l1.nets(self._netter).output(101, 0) l1.nets(prop(1)).output(102, 0) l1.nets(prop(nil)).output(103, 0) -l1.nets("X").output(110, 0) +x1 = l1.nets("X") +x1.output(110, 0) l1.nets("TOP", "X").output(111, 0) l1.nets("TOP", "NOTEXIST").output(112, 0) l1.nets("NOTEXIST", "NOTEXIST").output(113, 0) +x2 = l1.nets(*netlist.nets_by_name("X")) +(x1 ^ x2).output(114, 0) # checks with property constraints From 82b30303525a2cb516f67b37b977cdf3aabc1ee5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 6 Aug 2024 18:19:31 +0200 Subject: [PATCH 4/9] const versions of Layout#cell and Layout#top_cell(s) --- src/db/db/gsiDeclDbLayout.cc | 78 ++++++++++++++++++++++++++++++++- testdata/python/dbLayoutTest.py | 14 ++++++ testdata/ruby/dbLayoutTests2.rb | 10 +++++ 3 files changed, 101 insertions(+), 1 deletion(-) diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc index 751c4152e..3edf80298 100644 --- a/src/db/db/gsiDeclDbLayout.cc +++ b/src/db/db/gsiDeclDbLayout.cc @@ -854,6 +854,11 @@ static db::Cell *cell_from_index (db::Layout *ly, db::cell_index_type ci) return &ly->cell (ci); } +static const db::Cell *cell_from_index_const (const db::Layout *layout, db::cell_index_type ci) +{ + return cell_from_index (const_cast (layout), ci); +} + static db::Cell *cell_from_name (db::Layout *ly, const std::string &name) { std::pair cn = ly->cell_by_name (name.c_str ()); @@ -864,6 +869,11 @@ static db::Cell *cell_from_name (db::Layout *ly, const std::string &name) } } +static const db::Cell *cell_from_name_const (const db::Layout *layout, const std::string &name) +{ + return cell_from_name (const_cast (layout), name); +} + static std::vector cells_from_name (db::Layout *layout, const std::string &filter) { tl::GlobPattern gp (filter); @@ -880,6 +890,12 @@ static std::vector cells_from_name (db::Layout *layout, const std::s return result; } +static std::vector cells_from_name_const (const db::Layout *layout, const std::string &filter) +{ + std::vector tcs = cells_from_name (const_cast (layout), filter); + return std::vector (tcs.begin (), tcs.end ()); +} + static std::vector top_cells (db::Layout *layout) { std::vector tc; @@ -891,6 +907,12 @@ static std::vector top_cells (db::Layout *layout) return tc; } +static std::vector top_cells_const (const db::Layout *layout) +{ + std::vector tcs = top_cells (const_cast (layout)); + return std::vector (tcs.begin (), tcs.end ()); +} + static db::Cell *top_cell (db::Layout *layout) { db::Cell *tc = 0; @@ -906,6 +928,11 @@ static db::Cell *top_cell (db::Layout *layout) return tc; } +static const db::Cell *top_cell_const (const db::Layout *layout) +{ + return top_cell (const_cast (layout)); +} + static db::Cell *create_cell (db::Layout *layout, const std::string &name) { return &layout->cell (layout->add_cell (name.c_str ())); @@ -1328,7 +1355,16 @@ Class decl_Layout ("db", "Layout", "\n" "This method has been introduced in version 0.23." ) + - gsi::method_ext ("top_cells", &top_cells, + gsi::method_ext ("top_cell", &top_cell_const, + "@brief Returns the top cell object (const version)\n" + "@return The \\Cell object of the top cell\n" + "If the layout has a single top cell, this method returns the top cell's \\Cell object.\n" + "If the layout does not have a top cell, this method returns \"nil\". If the layout has multiple\n" + "top cells, this method raises an error.\n" + "\n" + "This variant has been introduced in version 0.29.6." + ) + + gsi::method_ext ("top_cells", &top_cells, "@brief Returns the top cell objects\n" "@return The \\Cell objects of the top cells\n" "This method returns and array of \\Cell objects representing the top cells of the layout.\n" @@ -1336,6 +1372,14 @@ Class decl_Layout ("db", "Layout", "\n" "This method has been introduced in version 0.23." ) + + gsi::method_ext ("top_cells", &top_cells_const, + "@brief Returns the top cell objects (const version)\n" + "@return The \\Cell objects of the top cells\n" + "This method returns and array of \\Cell objects representing the top cells of the layout.\n" + "This array can be empty, if the layout does not have a top cell (i.e. no cell at all).\n" + "\n" + "This variant has been introduced in version 0.29.6." + ) + gsi::method ("has_cell?", &db::Layout::has_cell, gsi::arg ("name"), "@brief Returns true if a cell with a given name exists\n" "Returns true, if the layout has a cell with the given name" @@ -1780,6 +1824,16 @@ Class decl_Layout ("db", "Layout", "\n" "This method has been introduced in version 0.27.3.\n" ) + + gsi::method_ext ("cells", &cells_from_name_const, gsi::arg ("name_filter"), + "@brief Gets the cell objects for a given name filter (const version)\n" + "\n" + "@param name_filter The cell name filter (glob pattern)\n" + "@return A list of \\Cell object of the cells matching the pattern\n" + "\n" + "This method has been introduced in version 0.27.3.\n" + "\n" + "This variant has been introduced in version 0.29.6." + ) + gsi::method_ext ("cell", &cell_from_name, gsi::arg ("name"), "@brief Gets a cell object from the cell name\n" "\n" @@ -1789,6 +1843,17 @@ Class decl_Layout ("db", "Layout", "If name is not a valid cell name, this method will return \"nil\".\n" "This method has been introduced in version 0.23 and replaces \\cell_by_name.\n" ) + + gsi::method_ext ("cell", &cell_from_name_const, gsi::arg ("name"), + "@brief Gets a cell object from the cell name (const version)\n" + "\n" + "@param name The cell name\n" + "@return A reference to the cell (a \\Cell object)\n" + "\n" + "If name is not a valid cell name, this method will return \"nil\".\n" + "This method has been introduced in version 0.23 and replaces \\cell_by_name.\n" + "\n" + "This variant has been introduced in version 0.29.6." + ) + gsi::method_ext ("cell", &cell_from_index, gsi::arg ("i"), "@brief Gets a cell object from the cell index\n" "\n" @@ -1798,6 +1863,17 @@ Class decl_Layout ("db", "Layout", "If the cell index is not a valid cell index, this method will raise an error. " "Use \\is_valid_cell_index? to test whether a given cell index is valid.\n" ) + + gsi::method_ext ("cell", &cell_from_index_const, gsi::arg ("i"), + "@brief Gets a cell object from the cell index (const version)\n" + "\n" + "@param i The cell index\n" + "@return A reference to the cell (a \\Cell object)\n" + "\n" + "If the cell index is not a valid cell index, this method will raise an error. " + "Use \\is_valid_cell_index? to test whether a given cell index is valid.\n" + "\n" + "This variant has been introduced in version 0.29.6." + ) + gsi::iterator ("each_cell", (db::Layout::iterator (db::Layout::*) ()) &db::Layout::begin, (db::Layout::iterator (db::Layout::*) ()) &db::Layout::end, "@brief Iterates the unsorted cell list\n" ) + diff --git a/testdata/python/dbLayoutTest.py b/testdata/python/dbLayoutTest.py index b0e535b7a..1e01a3533 100644 --- a/testdata/python/dbLayoutTest.py +++ b/testdata/python/dbLayoutTest.py @@ -75,13 +75,20 @@ def test_2_Layout(self): self.assertEqual( ly.cell_name(ci), "new_cell" ) self.assertEqual( ly.cell_by_name("new_cell"), ci ) + self.assertEqual( ly.cell(ci).cell_index(), ci ) self.assertEqual( ly.cell("new_cell").name, "new_cell" ) self.assertEqual( repr(ly.cell("x")), "None" ) + lyc = ly.dup() + self.assertEqual( lyc._to_const_object().cell("new_cell").name, "new_cell" ) + self.assertEqual( lyc._to_const_object().cell(ci).cell_index(), ci ) ci2 = ly.add_cell( "new_cell_b" ) self.assertEqual( ly.cells(), 2 ) self.assertEqual( ly.cell_by_name("new_cell_b"), ci2 ) + self.assertEqual( sorted([ c.name for c in ly.cells("new*") ]), ['new_cell', 'new_cell_b'] ) self.assertEqual( ci != ci2, True ) + lyc = ly.dup() + self.assertEqual( sorted([ c.name for c in lyc._to_const_object().cells("new*") ]), ['new_cell', 'new_cell_b'] ) ly.rename_cell( ci2, "x" ) self.assertEqual( ly.cell_by_name("x"), ci2 ) @@ -918,6 +925,8 @@ def test_8(self): tc.append(l.cell(t).name) self.assertEqual(",".join(tc), "c0") self.assertEqual(l.top_cell().name, "c0") + lc = l.dup() + self.assertEqual(lc._to_const_object().top_cell().name, "c0") # const version tc = [] for t in l.top_cells(): tc.append(t.name) @@ -939,6 +948,11 @@ def test_8(self): for t in l.top_cells(): tc.append(t.name) self.assertEqual(",".join(tc), "c0,c1") + tc = [] + lc = l.dup() + for t in lc._to_const_object().top_cells(): # const version + tc.append(t.name) + self.assertEqual(",".join(tc), "c0,c1") c2 = l.create_cell("c1") self.assertEqual(c2.name, "c1$1") diff --git a/testdata/ruby/dbLayoutTests2.rb b/testdata/ruby/dbLayoutTests2.rb index fc1be5ac6..111d0e9f8 100644 --- a/testdata/ruby/dbLayoutTests2.rb +++ b/testdata/ruby/dbLayoutTests2.rb @@ -83,14 +83,21 @@ def test_2_Layout assert_equal( ly.cell_name(ci), "new_cell" ) assert_equal( ly.cell_by_name("new_cell"), ci ) + assert_equal( ly.cell(ci).cell_index, ci ) assert_equal( ly.cells("A*"), [] ) assert_equal( ly.cell("new_cell").name, "new_cell" ) assert_equal( ly.cell("x").inspect, "nil" ) + lyc = ly.dup + assert_equal( lyc._to_const_object.cell("new_cell").name, "new_cell" ) + assert_equal( lyc._to_const_object.cell(ci).cell_index, ci ) ci2 = ly.add_cell( "new_cell_b" ) assert_equal( ly.cells, 2 ) assert_equal( ly.cell_by_name("new_cell_b"), ci2 ) + assert_equal( ly.cells("new*").collect { |c| c.name }.sort, ['new_cell', 'new_cell_b'] ) assert_equal( ci != ci2, true ) + lyc = ly.dup + assert_equal( lyc._to_const_object.cells("new*").collect { |c| c.name }.sort, ['new_cell', 'new_cell_b'] ) ly.rename_cell( ci2, "x" ) assert_equal( ly.cell_by_name("x"), ci2 ) @@ -900,6 +907,9 @@ def test_8 assert_equal(tc.collect { |s| s.to_s }.join(","), "c0") assert_equal(l.top_cell.name, "c0") assert_equal(l.top_cells.collect { |t| t.name }.join(","), "c0") + lc = l.dup + assert_equal(lc._to_const_object.top_cell.name, "c0") + assert_equal(lc._to_const_object.top_cells.collect { |t| t.name }.join(","), "c0") c1 = l.create_cell("c1") assert_equal(c1.name, "c1") From fd1dc842e0c9e3f04c0c6778d9856158dbcf4705 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 7 Aug 2024 00:11:48 +0200 Subject: [PATCH 5/9] Generic GSI methods #_to_const_object (for testing) and #_const_cast. Fixed a class initialization issue - sub classes should not be registered by name at top level in Expressions --- src/gsi/gsi/gsiClassBase.cc | 45 +++++++++++++++++++ src/gsi/gsi/gsiExpression.cc | 24 +++++++--- src/gsi/gsi/gsiMethods.h | 4 +- src/gsi/unit_tests/gsiExpressionTests.cc | 34 ++++++++++++++ src/pya/pya/pyaCallables.cc | 48 ++++++++++++++++++++ src/rba/rba/rba.cc | 57 +++++++++++++++++++----- src/tl/tl/tlExpression.cc | 22 +++++++++ src/tl/tl/tlExpression.h | 36 +++++++++++++++ src/tl/tl/tlVariant.cc | 11 +++++ src/tl/tl/tlVariant.h | 11 +++++ src/tl/unit_tests/tlExpressionTests.cc | 3 ++ testdata/python/basic.py | 38 ++++++++++++++++ testdata/ruby/basic_testcore.rb | 41 +++++++++++++++++ 13 files changed, 357 insertions(+), 17 deletions(-) diff --git a/src/gsi/gsi/gsiClassBase.cc b/src/gsi/gsi/gsiClassBase.cc index c9824a46d..6e75b461e 100644 --- a/src/gsi/gsi/gsiClassBase.cc +++ b/src/gsi/gsi/gsiClassBase.cc @@ -353,6 +353,48 @@ sm_is_const (const char *name) return sm; } +static SpecialMethod * +sm_to_const (const char *name, const gsi::ClassBase *cls) +{ + SpecialMethod *sm = new SpecialMethod (name, + tl::to_string (tr ("@hide")), // provided for test purposes mainly + true, // const + false, // non-static + MethodBase::ToConst); + + gsi::ArgType ret; + ret.set_is_cptr (true); + ret.set_type (gsi::T_object); + ret.set_pass_obj (false); + ret.set_cls (cls); + sm->set_return (ret); + + return sm; +} + +static SpecialMethod * +sm_const_cast (const char *name, const gsi::ClassBase *cls) +{ + SpecialMethod *sm = new SpecialMethod (name, + tl::to_string (tr ("@brief Returns a non-const reference to self.\n" + "Basically, this method allows turning a const object reference to a non-const one. " + "This method is provided as last resort to remove the constness from an object. Usually there is a good reason for a const object reference, so using this method may have undesired side effects.\n" + "\n" + "This method has been introduced in version 0.29.6.")), + true, // const + false, // non-static + MethodBase::ConstCast); + + gsi::ArgType ret; + ret.set_is_ptr (true); + ret.set_type (gsi::T_object); + ret.set_pass_obj (false); + ret.set_cls (cls); + sm->set_return (ret); + + return sm; +} + static SpecialMethod * sm_destroyed (const char *name) { @@ -598,6 +640,9 @@ ClassBase::merge_declarations () non_const_decl->add_method (sm_is_const ("_is_const_object?")); } + non_const_decl->add_method (sm_to_const ("_to_const_object", &*c)); + non_const_decl->add_method (sm_const_cast ("_const_cast", &*c)); + } // finally merge the new classes into the existing ones diff --git a/src/gsi/gsi/gsiExpression.cc b/src/gsi/gsi/gsiExpression.cc index 46ca33374..58795abd4 100644 --- a/src/gsi/gsi/gsiExpression.cc +++ b/src/gsi/gsi/gsiExpression.cc @@ -305,13 +305,17 @@ initialize_expressions () // install the method table: ExpressionMethodTable::initialize_class (*c); - // register a function that creates a class object (use a function to avoid issues with - // late destruction of global variables which the class object is already gone) - const tl::VariantUserClassBase *cc = (*c)->var_cls_cls (); - if (cc) { - tl::Eval::define_global_function ((*c)->name (), new EvalClassFunction (cc)); - } + // Note: skip non-top-level classes + if ((*c)->parent () == 0) { + + // register a function that creates a class object (use a function to avoid issues with + // late destruction of global variables which the class object is already gone) + const tl::VariantUserClassBase *cc = (*c)->var_cls_cls (); + if (cc) { + tl::Eval::define_global_function ((*c)->name (), new EvalClassFunction (cc)); + } + } } } @@ -630,6 +634,14 @@ special_method_impl (gsi::MethodBase::special_method_type smt, tl::Variant &self // nothing to do here for GSI objects } else if (smt == gsi::MethodBase::IsConst) { return tl::Variant (self.user_is_const ()); + } else if (smt == gsi::MethodBase::ToConst) { + tl::Variant res (self); + res.user_change_constness (true); + return res; + } else if (smt == gsi::MethodBase::ConstCast) { + tl::Variant res (self); + res.user_change_constness (false); + return res; } else if (smt == gsi::MethodBase::Destroyed) { if (self.type_code () == tl::Variant::t_user) { diff --git a/src/gsi/gsi/gsiMethods.h b/src/gsi/gsi/gsiMethods.h index e807ad955..d399ff309 100644 --- a/src/gsi/gsi/gsiMethods.h +++ b/src/gsi/gsi/gsiMethods.h @@ -83,7 +83,9 @@ class GSI_PUBLIC MethodBase Destroy, Create, IsConst, - Destroyed, + ConstCast, + ToConst, + Destroyed, Assign, Dup }; diff --git a/src/gsi/unit_tests/gsiExpressionTests.cc b/src/gsi/unit_tests/gsiExpressionTests.cc index d85f566fe..16c7f4dfd 100644 --- a/src/gsi/unit_tests/gsiExpressionTests.cc +++ b/src/gsi/unit_tests/gsiExpressionTests.cc @@ -829,3 +829,37 @@ TEST(15) v = e.parse("var bb = BB.new; bb.d4(1, 'a', 2.0, BB.E.E3B, 42)").execute(); EXPECT_EQ (v.to_string (), "1,a,2,101,42"); } + +// constness +TEST(16) +{ + tl::Eval e; + tl::Variant v; + v = e.parse ("var b=B.new(); b._is_const_object").execute (); + EXPECT_EQ (v.to_string (), std::string ("false")); + try { + v = e.parse ("var b=B.new(); var bc=b._to_const_object; bc.set_str('abc')").execute (); + EXPECT_EQ (1, 0); + } catch (tl::Exception &ex) { + EXPECT_EQ (ex.msg (), "Cannot call non-const method set_str, class B on a const reference at position 44 (...set_str('abc'))"); + } + v = e.parse ("var e=E.new(); var ec=e.dup; [e._is_const_object, ec._to_const_object._is_const_object]").execute (); + EXPECT_EQ (v.to_string (), std::string ("false,true")); + v = e.parse ("var e=E.new(); var ec=e._to_const_object; e.x=17; [e.x, ec.x]").execute (); + EXPECT_EQ (v.to_string (), std::string ("17,17")); + v = e.parse ("var e=E.new(); var ec=e._to_const_object; ec._is_const_object").execute (); + EXPECT_EQ (v.to_string (), std::string ("true")); + v = e.parse ("var e=E.new(); var ec=e._to_const_object; ec=ec._const_cast; ec._is_const_object").execute (); + EXPECT_EQ (v.to_string (), std::string ("false")); + v = e.parse ("var e=E.new(); var ec=e._to_const_object; ec=ec._const_cast; ec.x=42; e.x").execute (); + EXPECT_EQ (v.to_string (), std::string ("42")); + v = e.parse ("var e=E.new(); var ec=e._to_const_object; e.x=17; ec.x").execute (); + EXPECT_EQ (v.to_string (), std::string ("17")); + try { + v = e.parse ("var e=E.new(); var ec=e._to_const_object; e.x=17; e._destroy; ec.x").execute (); + EXPECT_EQ (1, 0); + } catch (tl::Exception &ex) { + EXPECT_EQ (ex.msg (), "Object has been destroyed already at position 64 (...x)"); + } +} + diff --git a/src/pya/pya/pyaCallables.cc b/src/pya/pya/pyaCallables.cc index 5422c9968..5dfc3e4f1 100644 --- a/src/pya/pya/pyaCallables.cc +++ b/src/pya/pya/pyaCallables.cc @@ -721,6 +721,50 @@ object_is_const (PyObject *self, PyObject *args) return c2python (PYAObjectBase::from_pyobject (self)->const_ref ()); } +/** + * @brief Implements to_const and const_cast + */ +static PyObject * +object_change_const (PyObject *self, PyObject *args, bool to_const) +{ + if (PYAObjectBase::from_pyobject (self)->const_ref () == to_const) { + return self; + } + + const gsi::ClassBase *cls_decl_self = PythonModule::cls_for_type (Py_TYPE (self)); + tl_assert (cls_decl_self != 0); + + if (! PyArg_ParseTuple (args, "")) { + return NULL; + } + + PyObject *new_object = Py_TYPE (self)->tp_alloc (Py_TYPE (self), 0); + PythonRef obj (new_object); + PYAObjectBase *new_pya_base = PYAObjectBase::from_pyobject_unsafe (new_object); + new (new_pya_base) PYAObjectBase (cls_decl_self, new_object); + new_pya_base->set (PYAObjectBase::from_pyobject (self)->obj (), false, to_const, false); + + return obj.release (); +} + +/** + * @brief Implements to_const + */ +static PyObject * +object_to_const (PyObject *self, PyObject *args) +{ + return object_change_const (self, args, true); +} + +/** + * @brief Implements const_cast + */ +static PyObject * +object_const_cast (PyObject *self, PyObject *args) +{ + return object_change_const (self, args, false); +} + static PyObject * special_method_impl (gsi::MethodBase::special_method_type smt, PyObject *self, PyObject *args) { @@ -734,6 +778,10 @@ special_method_impl (gsi::MethodBase::special_method_type smt, PyObject *self, P return object_create (self, args); } else if (smt == gsi::MethodBase::IsConst) { return object_is_const (self, args); + } else if (smt == gsi::MethodBase::ToConst) { + return object_to_const (self, args); + } else if (smt == gsi::MethodBase::ConstCast) { + return object_const_cast (self, args); } else if (smt == gsi::MethodBase::Destroyed) { return object_destroyed (self, args); } else if (smt == gsi::MethodBase::Assign) { diff --git a/src/rba/rba/rba.cc b/src/rba/rba/rba.cc index 21743409b..efda4fce5 100644 --- a/src/rba/rba/rba.cc +++ b/src/rba/rba/rba.cc @@ -913,6 +913,16 @@ handle_exception (const std::string &where) handle_exception ((where)); \ } +static void free_proxy (void *p) +{ + delete ((Proxy *) p); +} + +static void mark_proxy (void *p) +{ + ((Proxy *) p)->mark (); +} + static VALUE destroy (VALUE self) { @@ -971,6 +981,37 @@ is_const (VALUE self) return c2ruby (p->const_ref ()); } +static VALUE +to_const (VALUE self) +{ + Proxy *p = 0; + Data_Get_Struct (self, Proxy, p); + if (! p->const_ref ()) { + // promote to const object + // NOTE: there is only ONE instance we're going to change this instance + // to const here. This has a global effect and this is the reason why this + // method is not public. It is provided for testing purposes mainly. + p->set_const_ref (true); + } + + return self; +} + +static VALUE +const_cast_ (VALUE self) +{ + Proxy *p = 0; + Data_Get_Struct (self, Proxy, p); + if (p->const_ref ()) { + // promote to non-const object + // NOTE: this is a global change of constness and will affect all references + // that exist for this object. + p->set_const_ref (false); + } + + return self; +} + static VALUE assign (VALUE self, VALUE src) { @@ -1024,6 +1065,12 @@ special_method_impl (const gsi::MethodBase *meth, int argc, VALUE *argv, VALUE s } else if (smt == gsi::MethodBase::IsConst) { tl_assert (!ctor); return is_const (self); + } else if (smt == gsi::MethodBase::ToConst) { + tl_assert (!ctor); + return to_const (self); + } else if (smt == gsi::MethodBase::ConstCast) { + tl_assert (!ctor); + return const_cast_ (self); } else if (smt == gsi::MethodBase::Destroyed) { tl_assert (!ctor); return destroyed (self); @@ -1043,16 +1090,6 @@ special_method_impl (const gsi::MethodBase *meth, int argc, VALUE *argv, VALUE s } } -static void free_proxy (void *p) -{ - delete ((Proxy *) p); -} - -static void mark_proxy (void *p) -{ - ((Proxy *) p)->mark (); -} - static VALUE alloc_proxy (VALUE klass) { tl_assert (TYPE (klass) == T_CLASS); diff --git a/src/tl/tl/tlExpression.cc b/src/tl/tl/tlExpression.cc index 0db330f5c..35eaa01a4 100644 --- a/src/tl/tl/tlExpression.cc +++ b/src/tl/tl/tlExpression.cc @@ -3192,6 +3192,17 @@ Eval::set_var (const std::string &name, const tl::Variant &var) m_local_vars.insert (std::make_pair (name, tl::Variant ())).first->second = var; } +tl::Variant * +Eval::var (const std::string &name) +{ + auto f = m_local_vars.find (name); + if (f != m_local_vars.end ()) { + return &f->second; + } else { + return 0; + } +} + void Eval::define_function (const std::string &name, EvalFunction *function) { @@ -3202,6 +3213,17 @@ Eval::define_function (const std::string &name, EvalFunction *function) f = function; } +EvalFunction * +Eval::function (const std::string &name) +{ + auto f = m_local_functions.find (name); + if (f != m_local_functions.end ()) { + return f->second; + } else { + return 0; + } +} + void Eval::eval_top (ExpressionParserContext &ex, std::unique_ptr &n) { diff --git a/src/tl/tl/tlExpression.h b/src/tl/tl/tlExpression.h index 9c7d84d4e..3912d0a5b 100644 --- a/src/tl/tl/tlExpression.h +++ b/src/tl/tl/tlExpression.h @@ -448,6 +448,12 @@ class TL_PUBLIC Eval */ void define_function (const std::string &name, EvalFunction *function); + /** + * @brief Gets the function for the given name + * Returns 0 if there is no such function. + */ + EvalFunction *function (const std::string &name); + /** * @brief Define a global variable for use within an expression */ @@ -461,6 +467,12 @@ class TL_PUBLIC Eval */ void set_var (const std::string &name, const tl::Variant &var); + /** + * @brief Gets the function for the given name + * Returns 0 if there is no such function. + */ + tl::Variant *var (const std::string &name); + /** * @brief Parse an expression from the extractor * @@ -543,6 +555,30 @@ class TL_PUBLIC Eval return m_match_substrings; } + /** + * @brief Gets the global context + */ + static tl::Eval &global_context () + { + return m_global; + } + + /** + * @brief Gets the global context for this context + */ + tl::Eval *global () + { + return mp_global; + } + + /** + * @brief Gets the parent context for this context + */ + tl::Eval *parent () + { + return mp_parent; + } + private: friend class Expression; diff --git a/src/tl/tl/tlVariant.cc b/src/tl/tl/tlVariant.cc index 5bdca8fec..68f7240e9 100644 --- a/src/tl/tl/tlVariant.cc +++ b/src/tl/tl/tlVariant.cc @@ -2738,6 +2738,17 @@ void *Variant::user_unshare () const return const_cast (to_user ()); } +void Variant::user_change_constness (bool constness) +{ + tl_assert (is_user ()); + + if (m_type == t_user) { + m_var.mp_user.cls = m_var.mp_user.cls->change_constness (constness); + } else if (m_type == t_user_ref) { + m_var.mp_user_ref.cls = m_var.mp_user_ref.cls->change_constness (constness); + } +} + void Variant::user_assign (const tl::Variant &other) { tl_assert (is_user ()); diff --git a/src/tl/tl/tlVariant.h b/src/tl/tl/tlVariant.h index 9a81c329d..8669f5c82 100644 --- a/src/tl/tl/tlVariant.h +++ b/src/tl/tl/tlVariant.h @@ -89,6 +89,7 @@ class TL_PUBLIC VariantUserClassBase virtual const gsi::ClassBase *gsi_cls () const = 0; virtual const tl::EvalClass *eval_cls () const = 0; virtual void *deref_proxy (tl::Object *proxy) const = 0; + virtual const tl::VariantUserClassBase *change_constness (bool constness) const = 0; const void *deref_proxy_const (const tl::Object *proxy) const { @@ -126,6 +127,11 @@ class TL_PUBLIC_TEMPLATE VariantUserClass return VariantUserClassBase::instance (typeid (T), is_const); } + const tl::VariantUserClassBase *change_constness (bool constness) const + { + return instance (constness); + } + private: static const tl::VariantUserClassBase *ms_instances[4]; @@ -1001,6 +1007,11 @@ class TL_PUBLIC Variant */ void *user_unshare () const; + /** + * @brief Changes the constness of the user object + */ + void user_change_constness (bool constness); + /** * @brief Assigns the object stored in other to self * diff --git a/src/tl/unit_tests/tlExpressionTests.cc b/src/tl/unit_tests/tlExpressionTests.cc index 3f4710ad4..f00ed10be 100644 --- a/src/tl/unit_tests/tlExpressionTests.cc +++ b/src/tl/unit_tests/tlExpressionTests.cc @@ -465,6 +465,7 @@ class BoxClassClass : public tl::VariantUserClassBase, private tl::EvalClass virtual bool is_ref () const { return false; } virtual void *deref_proxy (tl::Object *) const { return 0; } virtual const gsi::ClassBase*gsi_cls() const { return 0; } + virtual const tl::VariantUserClassBase *change_constness (bool) const { return this; } static BoxClassClass instance; }; @@ -533,6 +534,7 @@ class EdgeClassClass : public tl::VariantUserClassBase, private tl::EvalClass virtual bool is_ref () const { return false; } virtual void *deref_proxy (tl::Object *) const { return 0; } virtual const gsi::ClassBase*gsi_cls() const { return 0; } + virtual const tl::VariantUserClassBase *change_constness (bool) const { return this; } static EdgeClassClass instance; }; @@ -1209,3 +1211,4 @@ TEST(20) v = e.parse ("i2.dtrans.disp.y").execute (); EXPECT_EQ (v.to_string (), std::string ("0.3")); } + diff --git a/testdata/python/basic.py b/testdata/python/basic.py index fe720a451..238e6822d 100644 --- a/testdata/python/basic.py +++ b/testdata/python/basic.py @@ -3280,6 +3280,44 @@ def test_92(self): self.assertEqual(bb.d4(1, "a", d=pya.BB.E.E3B, c=2.5), "1,a,2.5,101,nil") self.assertEqual(bb.d4(1, "a", 2.0, pya.BB.E.E3B, 42), "1,a,2,101,42") + # constness + def test_93(self): + + b = pya.B() + self.assertEqual(b.is_const_object(), False) + + bc = b._to_const_object() + self.assertEqual(bc.is_const_object(), True) + + m = "" + try: + bc.set_str("abc") + self.assertEqual(1, 0) + except Exception as ex: + m = str(ex) + + self.assertEqual(m, "Cannot call non-const method on a const reference in B.set_str") + + b = pya.B() + bc = b + self.assertEqual(b._is_const_object(), False) + self.assertEqual(bc._is_const_object(), False) + bc = bc._to_const_object() + b.set_str("abc") + self.assertEqual(b._is_const_object(), False) + self.assertEqual(bc._is_const_object(), True) + self.assertEqual(b.str(), "abc") + self.assertEqual(bc.str(), "abc") + + bnc = bc._const_cast() + self.assertEqual(b._is_const_object(), False) + self.assertEqual(bc._is_const_object(), True) + self.assertEqual(bnc._is_const_object(), False) + bnc.set_str("xyz") + self.assertEqual(b.str(), "xyz") + self.assertEqual(bc.str(), "xyz") + self.assertEqual(bnc.str(), "xyz") + # run unit tests if __name__ == '__main__': suite = unittest.TestSuite() diff --git a/testdata/ruby/basic_testcore.rb b/testdata/ruby/basic_testcore.rb index 6183a77d4..ec073d8b5 100644 --- a/testdata/ruby/basic_testcore.rb +++ b/testdata/ruby/basic_testcore.rb @@ -3233,4 +3233,45 @@ def test_81 end + # constness + def test_82 + + b = RBA::B::new + assert_equal(b.is_const_object, false) + + bc = b._to_const_object + assert_equal(bc.is_const_object, true) + + m = "" + begin + bc.set_str("abc") + assert_equal(1, 0) + rescue => ex + m = ex.to_s + end + + assert_equal(m, "Cannot call non-const method on a const reference in B::set_str") + + b = RBA::B::new + bc = b + assert_equal(b._is_const_object, false) + assert_equal(bc._is_const_object, false) + b.set_str("abc") + bc._to_const_object + assert_equal(b._is_const_object, true) # special + assert_equal(bc._is_const_object, true) + assert_equal(b.str, "abc") + assert_equal(bc.str, "abc") + + bnc = bc._const_cast + assert_equal(b._is_const_object, false) # special + assert_equal(bc._is_const_object, false) # special + assert_equal(bnc._is_const_object, false) + bnc.set_str("xyz") + assert_equal(b.str, "xyz") + assert_equal(bc.str, "xyz") + assert_equal(bnc.str, "xyz") + + end + end From 33a3a1579140c13e417cabe7a1853d2c45826ef6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 7 Aug 2024 00:26:10 +0200 Subject: [PATCH 6/9] Bugfix: 'netter' function in DRC wasn't working as described - it raised an error --- src/drc/drc/built-in-macros/_drc_engine.rb | 2 +- src/drc/unit_tests/drcSimpleTests.cc | 10 +++++ testdata/drc/drcSimpleTests_71.drc | 41 +++++++++++++++++++++ testdata/drc/drcSimpleTests_71.gds | Bin 0 -> 1212 bytes testdata/drc/drcSimpleTests_au71.gds | Bin 0 -> 6004 bytes testdata/drc/drcSimpleTests_au71d.gds | Bin 0 -> 4418 bytes 6 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 testdata/drc/drcSimpleTests_71.drc create mode 100644 testdata/drc/drcSimpleTests_71.gds create mode 100644 testdata/drc/drcSimpleTests_au71.gds create mode 100644 testdata/drc/drcSimpleTests_au71d.gds diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index bcf27580b..176bf9aaa 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -2308,7 +2308,7 @@ def #{f}(*args) def netter self._context("netter") do - DRC::DRCNetter::new + DRC::DRCNetter::new(self) end end diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index a561ad241..31ae49654 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -1568,6 +1568,16 @@ TEST(70d_props) run_test (_this, "70", true); } +TEST(71_netter) +{ + run_test (_this, "71", false); +} + +TEST(71d_netter) +{ + run_test (_this, "71", true); +} + TEST(80_deep_with_mag_width) { run_test (_this, "80", true); diff --git a/testdata/drc/drcSimpleTests_71.drc b/testdata/drc/drcSimpleTests_71.drc new file mode 100644 index 000000000..e5d6dbf65 --- /dev/null +++ b/testdata/drc/drcSimpleTests_71.drc @@ -0,0 +1,41 @@ + +# Moved implementation + +source($drc_test_source) +target($drc_test_target) + +if $drc_test_deep + deep +end + +l1 = input(1, 0) +l2 = input(2, 0) +l3 = input(3, 0) + +# dump to output + +l1.output(1, 0) +l2.output(2, 0) +l3.output(3, 0) + +ctx1 = netter +ctx1.connect(l1, l2) + +connect(l1, l3) + +l1.nets.output(100, 0) +begin + l2.nets.output(101, 0) + raise "internal error - l2 was not used in connectivity" +rescue => ex +end +l3.nets.output(102, 0) + +l1.nets(ctx1).output(110, 0) +l2.nets(ctx1).output(111, 0) +begin + l3.nets(ctx1).output(112, 0) + raise "internal error - l3 was not used in connectivity" +rescue => ex +end + diff --git a/testdata/drc/drcSimpleTests_71.gds b/testdata/drc/drcSimpleTests_71.gds new file mode 100644 index 0000000000000000000000000000000000000000..d2cac040afc722c40d2ce3d05f1cf8f65c2ef715 GIT binary patch literal 1212 zcma)5yG{a85IuJvvkR^dgkUr@8cGry6SXlyXpAHpLqVu4EGaDc01E9WO>8Vo`~?eZ zLup}U`~XS{YuA~3cVQ*FA_*M!&YYRK=gfj&SshfA<&ELP15iLU`5(STt&;{&vfZQl z?f&WO)8w%Ey!&w9ZKL95^N6SpI6gqj78Gd3;_9_En|~7@x6cnFAjU+Bjc0P5QJvUDCmhrwM z{NxC2DZY77aT{Gm`RLr@4WMc!H_a`tXWWt5)X?18V0^Ao_e$fteUm3!FFaAtV0`{J z-yWDedti9tnees$yggAov%eY|zHsC#ei)3;XZ<5*_zj+SQFfYDN;ng3o1MCCO}%aG lf_(wxCo^2y0m>ghjJZpoSOpA*VR!(D;*9!6{GK}wegJw{l{^3d literal 0 HcmV?d00001 diff --git a/testdata/drc/drcSimpleTests_au71.gds b/testdata/drc/drcSimpleTests_au71.gds new file mode 100644 index 0000000000000000000000000000000000000000..389d5ccac2f2de5d43babec62ca13e36ad2540de GIT binary patch literal 6004 zcmb_gJ!o7<5T1K?_wGqL%d&NrVxvL{uk%5`QT$g3Tc~h|z{aQ$!bOUZ$~a93E>ffr zMgbQp14-e+;MRpPE{suOLJAiuQiPDwg{t5pW$<~vot^ii)7v*sk>U8{+5NuVot>SX zeH2p^ZO}q1>g`iPJtCT;tHr-5iQ=^{R)_}e?(+4YE`9milXq8EUwr!Lm(6ur=mvOk zyngc*Q70jaPejzBcsgSJ;Y^#TcXu>;{|At$wL_FFDXQIJ=Trs1dtdr>?+Y)!rl|HX zzr7{>+FQcwtSPEJ%x~>TKg=CJ_Mu0$hxzn7d&2*~pZH4nK(#Ygt{;7(m(n-)ou7lZ zb9*HpT>XI$KPaj_#P2*6J`gk5cvG9T5Ar2J5_wAMZ)xH=WzdI0q z_iy26sP;ja-%Etw+Y^3K8zLzy3mZWs?#AoEOi0r0k$) zD%dk`*snXaz6!88chq@0z&`rAF>}rv|6^><8U^-*#A)R3srX|=YjTorE6%nl;uUd) zQvyWBK|Cn3-9>OvW#FQoBLgbfbrZ{tJ^h{AC+fe=k$p$7GaAQ6Z{%7a&%Ai%c-;rj zC(efW=s;v0(${70y`n$+nLi7<6_J2dNgM@7s$gR^obku^vVG`Yu3{H(gSKxIO}@)< z>Ju)1{vuMu-p;17$Fu2ZG`bA$Idq=mjYMVhb+NKB-VE`1MAzgn-WW=iRH$^u^so=m z&2ET&$+($)k<5C=o6oF=c^Ph^MtX*usga)ENP0sls-4lSXRKV!dWb{)1u8+ap5~_- zH0v2FZL^-S@;2)kD{-?P_5u|o5IGHdqNsL8vmQA5gHA=IZPx2N6+IyIa0e)29N%u% zGgjheJ!54a()-5e(>uu1r+3+B&joRA=f3BB>~Zpy*cGQ95X~=h{`QFTjmlI}br>z< zyO-+eUC#Hv=JS9abY{2J?$E54=MUkl4p3gi4$XR#5BVBlKLD|A_)AgkjAlI-|Nlt* zW1Um$vR+{1?TltU%nLd?Pv~G?s_WZBdiRR=Ywo}D*-Vzq{wwKe?gbt(|CaP>@~+m8 z=Jg&!{w>Y#81ipP?-=rLNzcU}=P_WiRNiCcH{YK5FTLOjsA4@<(rcP8>-3uD%k;DG z{F>&=I=!a#XSx)gU(@=|qSvIpOFyrlpVo0&*D0!VxddQ>l7<2!QpU2X8{U2Fl z$e%74@x{u$Ulj*LCvM1l0*EhaXwT5|n4ga~9=|%CyT1`@c<}td9UtQhpCD>8*EGBm7TL<$ zmo=?lR#l-159&1W_c~3opKGe4F&zHpV-hibBTBamsPc}Svnm=>J(B-WfP`@Iy|I^^+^IKTze2wf1>M4u`|n*gr?i za;LAgpWc=AdT#g+Ka|JmYwg=FWjzpaf!~VYar%+=SdVpxBm7WQIb+d&tvG4(^GhXR z>Z7vKdFgwnA5~E$z7y!k65k1QWQp$tIV|3&ZK1^-!=DCoZsGQL|AF$Cc z_%I(7Ro;Xz+7Uh=cx=5zDW&Oe=-iiP=E$;baSL=pV~o19?q literal 0 HcmV?d00001 From 4631a806efc37662f8751dbd5d2848b7d8a2d8e4 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 7 Aug 2024 00:48:42 +0200 Subject: [PATCH 7/9] Provide a warning message when closing the window while an operation is ongoing. --- src/lay/lay/layMainWindow.cc | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index dce7dd237..c60b09921 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -1331,6 +1331,18 @@ MainWindow::update_dock_widget_state () void MainWindow::exit () { + if (m_busy) { + if (QMessageBox::warning (this, + QObject::tr ("Application Busy - Close Anyway?"), + QObject::tr ("The application is busy.\nYou can close the application now, but this will terminate any running operations.\nDo you want to close anyway?\n\n" + "Press 'Yes' to end the application now."), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::Yes) != QMessageBox::Yes) + { + return; + } + } + m_exited = true; // If there is a operation ongoing, request a break and delay execution of the exit operation. @@ -1394,20 +1406,6 @@ MainWindow::dirty_files (std::string &dirty_files) bool MainWindow::can_close () { - if (m_busy) { - - bool can_close = false; - - can_close = (QMessageBox::warning (this, - QObject::tr ("Application Busy"), - QObject::tr ("The application is busy.\nYou can close the application now, but any unsaved data will be lost.\n\nPress 'Yes' to end the application now."), - QMessageBox::Yes | QMessageBox::No, - QMessageBox::Yes) == QMessageBox::Yes); - - return can_close; - - } - for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) { lay::PluginDeclaration *pd = const_cast (&*cls); if (! pd->can_exit (dispatcher ())) { From d31c9dbf6cf35f28fa1ed3b1db86a0896a6d04f9 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 7 Aug 2024 22:38:18 +0200 Subject: [PATCH 8/9] Bugfix: avoid a segfault in the property editor due to invalid array access (difficult to reproduce). --- src/layui/layui/layPropertiesDialog.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/layui/layui/layPropertiesDialog.cc b/src/layui/layui/layPropertiesDialog.cc index 8a0fbe274..773dcf1c5 100644 --- a/src/layui/layui/layPropertiesDialog.cc +++ b/src/layui/layui/layPropertiesDialog.cc @@ -379,7 +379,7 @@ PropertiesDialog::update_controls () mp_ui->apply_to_all_cbx->setChecked (m_object_indexes.size () > 1); - if (m_index < 0) { + if (m_index < 0 || m_index >= int (mp_properties_pages.size ())) { mp_stack->setCurrentWidget (mp_none); @@ -552,6 +552,10 @@ PropertiesDialog::apply () { BEGIN_PROTECTED + if (m_index < 0 || m_index >= int (mp_properties_pages.size ())) { + return; + } + db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes")), m_transaction_id); try { From a601447d5f5092bc8fa2b538ea166370aa579a71 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 8 Aug 2024 20:46:22 +0200 Subject: [PATCH 9/9] Properties dialog: stop handling 'Enter' key - use 'Ok' to close it --- src/layui/layui/layPropertiesDialog.cc | 6 ++++++ src/layui/layui/layPropertiesDialog.h | 1 + 2 files changed, 7 insertions(+) diff --git a/src/layui/layui/layPropertiesDialog.cc b/src/layui/layui/layPropertiesDialog.cc index 773dcf1c5..0e794983f 100644 --- a/src/layui/layui/layPropertiesDialog.cc +++ b/src/layui/layui/layPropertiesDialog.cc @@ -637,6 +637,12 @@ PropertiesDialog::reject () QDialog::reject (); } +void +PropertiesDialog::accept () +{ + // stop handling "Enter" key. +} + } #endif diff --git a/src/layui/layui/layPropertiesDialog.h b/src/layui/layui/layPropertiesDialog.h index 514f0d502..67792595f 100644 --- a/src/layui/layui/layPropertiesDialog.h +++ b/src/layui/layui/layPropertiesDialog.h @@ -116,6 +116,7 @@ public slots: protected: void reject (); + void accept (); private: Ui::PropertiesDialog *mp_ui;