diff --git a/src/db/db/dbEdgeProcessor.cc b/src/db/db/dbEdgeProcessor.cc index 80dbd794d..f9fc3d203 100644 --- a/src/db/db/dbEdgeProcessor.cc +++ b/src/db/db/dbEdgeProcessor.cc @@ -1060,7 +1060,15 @@ EdgeProcessor::insert (const db::Edge &e, EdgeProcessor::property_type p) } } -void +void +EdgeProcessor::insert (const db::SimplePolygon &q, EdgeProcessor::property_type p) +{ + for (db::SimplePolygon::polygon_edge_iterator e = q.begin_edge (); ! e.at_end (); ++e) { + insert (*e, p); + } +} + +void EdgeProcessor::insert (const db::Polygon &q, EdgeProcessor::property_type p) { for (db::Polygon::polygon_edge_iterator e = q.begin_edge (); ! e.at_end (); ++e) { diff --git a/src/db/db/dbEdgeProcessor.h b/src/db/db/dbEdgeProcessor.h index 9b5f4e380..dad1aa146 100644 --- a/src/db/db/dbEdgeProcessor.h +++ b/src/db/db/dbEdgeProcessor.h @@ -633,6 +633,11 @@ public: */ void insert (const db::Polygon &q, property_type p = 0); + /** + * @brief Insert a simple polygon + */ + void insert (const db::SimplePolygon &q, property_type p = 0); + /** * @brief Insert a polygon reference */ diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index 0eef40857..72eb4cfe9 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -4145,6 +4145,8 @@ NetlistComparer::do_subcircuit_assignment (const db::Circuit *c1, const db::NetG ++nscm; if (scc.equals (scm->second, std::make_pair (sc.operator-> (), sc_cat))) { found = true; + } else { + ++scm; } } diff --git a/src/db/db/dbPolygonGenerators.cc b/src/db/db/dbPolygonGenerators.cc index 6c8a80fd4..208e3634b 100644 --- a/src/db/db/dbPolygonGenerators.cc +++ b/src/db/db/dbPolygonGenerators.cc @@ -90,6 +90,8 @@ public: void push_front (const db::Point &p) { m_contour.push_front (p); } void pop_back () { m_contour.pop_back (); } void pop_front () { m_contour.pop_front (); } + iterator erase (iterator i) { return m_contour.erase (i); } + iterator insert (iterator i, const db::Point &p) { return m_contour.insert (i, p); } bool empty () const { return m_contour.empty (); } size_t size () const { return m_contour.size (); } @@ -136,9 +138,12 @@ public: } template - void insert (iterator at, I from, I to) + iterator insert (iterator at, I from, I to) { + // NOTE: in some STL m_contour.insert already returns the new iterator + size_t index_at = at - m_contour.begin (); m_contour.insert (at, from, to); + return m_contour.begin () + index_at; } private: @@ -725,27 +730,56 @@ PolygonGenerator::join_contours (db::Coord x) PGPolyContour &cprev = (*mp_contours) [iprev]; tl_assert (cprev.size () >= 2); + tl_assert (c1.size () >= 2); + + PGPolyContour::iterator ins = cprev.end (); + db::Coord xprev = 0; + db::Edge eprev; + +#if 1 + // shallow analysis: insert the cutline at the end of the sequence - this may + // cut lines collinear with contour edges + + eprev = db::Edge (ins[-2], ins[-1]); + xprev = db::coord_traits::rounded (edge_xaty (db::Edge (ins[-2], ins[-1]), m_y)); +#else + // deep analysis: determine insertion point: pick the one where the cutline is shortest + + for (PGPolyContour::iterator i = ins; i > cprev.begin () + 1; --i) { + + db::Edge ecut (i[-2], i[-1]); + db::Coord xcut = db::coord_traits::rounded (edge_xaty (db::Edge (i[-2], i[-1]), m_y)); + + if (ins == i || (i[-1].y () >= m_y && i[-2].y () < m_y && xcut < c1.back ().x () && xcut > xprev)) { + xprev = xcut; + eprev = ecut; + ins = i; + } + + } +#endif // compute intersection point with next edge - db::Edge eprev (cprev.end ()[-2], cprev.back ()); - db::Coord xprev = db::coord_traits::rounded (edge_xaty (eprev, m_y)); db::Point pprev (xprev, m_y); // remove collinear edges along the cut line - cprev.back () = pprev; - while (cprev.size () > 1 && cprev.end ()[-2].y () == m_y && cprev.end ()[-1].y () == m_y) { - cprev.pop_back (); + ins[-1] = pprev; + while (ins - cprev.begin () > 1 && ins[-2].y () == m_y && ins[-1].y () == m_y) { + ins = cprev.erase (ins - 1); } - tl_assert (c1.size () >= 2); if ((c1.begin () + 1)->y () == m_y) { - cprev.insert (cprev.end (), c1.begin () + 1, c1.end ()); + ins = cprev.insert (ins, c1.begin () + 1, c1.end ()); + ins += c1.size () - 1; } else { - cprev.insert (cprev.end (), c1.begin (), c1.end ()); + ins = cprev.insert (ins, c1.begin (), c1.end ()); + ins += c1.size (); } - cprev.push_back (pprev); + + ins = cprev.insert (ins, pprev); + ++ins; if (eprev.p2 () != pprev) { - cprev.push_back (eprev.p2 ()); + cprev.insert (ins, eprev.p2 ()); } mp_contours->free (i1); diff --git a/src/db/db/dbPolygonTools.cc b/src/db/db/dbPolygonTools.cc index 0c2312e22..1895d69b9 100644 --- a/src/db/db/dbPolygonTools.cc +++ b/src/db/db/dbPolygonTools.cc @@ -185,10 +185,19 @@ public: return db::vprod_sign (edge (), other.edge ()) > 0; } } + + bool operator== (const loose_end_struct &other) const + { + if (! db::coord_traits::equal (proj (), other.proj ())) { + return false; + } else { + return db::vprod_sign (edge (), other.edge ()) == 0; + } + } }; template -static bool _cut_polygon_internal (const PolygonType &input, const Edge &line, CutPolygonReceiverBase *right_of_line) +static bool _cut_polygon_internal (const PolygonType &input, const Edge &line, cut_polygon_receiver_base *right_of_line) { typedef typename PolygonType::point_type point_type; typedef typename PolygonType::coord_type coord_type; @@ -259,6 +268,7 @@ static bool _cut_polygon_internal (const PolygonType &input, const Edge &line, C // tie together last and first partial segments. if (cutting_segments[nfirst].segment < 0) { cutting_segments[nfirst].enter = cutting_segments.back ().enter; + cutting_segments[nfirst].segment = cutting_segments.back ().segment; cutting_segments.pop_back (); } @@ -268,7 +278,7 @@ static bool _cut_polygon_internal (const PolygonType &input, const Edge &line, C if (nc == 0) { // the hull is fully on the right side -> just output the input polygon and that's it. - right_of_line->put (&input); + right_of_line->put (input); return true; } else { // remember hole contours for later assignment @@ -279,7 +289,7 @@ static bool _cut_polygon_internal (const PolygonType &input, const Edge &line, C } else { PolygonType poly; poly.assign_hull (contour.begin (), contour.end ()); - right_of_line->put (&poly); + right_of_line->put (poly); } } @@ -297,23 +307,28 @@ static bool _cut_polygon_internal (const PolygonType &input, const Edge &line, C std::stable_sort (loose_ends.begin (), loose_ends.end ()); - // bring the points in a strict enter/leave order if possible + // we allow single pairs of collinear entry/leave edges (cut lines) and bring them in the right order bool enter = false; + for (typename std::vector >::iterator i = loose_ends.begin (); i != loose_ends.end (); ++i) { + if (i + 1 != loose_ends.end () && i[1] == i[0]) { + if (i + 2 != loose_ends.end () && i[2] == i[0]) { + // triple collinear + return false; + } + if (i[0].enter != enter && i[1].enter == enter) { + std::swap (i[0], i[1]); + } + } + enter = !enter; + } + + // the points now have to be in strict enter/leave order - otherwise fallback to merge + + enter = false; for (typename std::vector >::iterator i = loose_ends.begin (); i != loose_ends.end (); ++i) { if (i->enter != enter) { - typename std::vector >::iterator j = i + 1; - typename std::vector >::iterator jj = loose_ends.end (); - for ( ; j != loose_ends.end () && !(*j < *i) && !(*i < *j); ++j) { - if (j->enter == enter) { - jj = j; - break; - } - } - if (jj == loose_ends.end ()) { - return false; // cannot cut (self-overlapping, self-intersecting) - } - std::swap (*jj, *i); + return false; } enter = !enter; } @@ -410,7 +425,7 @@ static bool _cut_polygon_internal (const PolygonType &input, const Edge &line, C // it might happen in some cases, that cut pieces may vanish (i.e. all points on a line). Thus we check, if that // is the case and do not produce a polygon then. if (poly.vertices () > 0) { - right_of_line->put (&poly); + right_of_line->put (poly); } } @@ -441,7 +456,7 @@ static bool _cut_polygon_internal (const PolygonType &input, const Edge &line, C } } - right_of_line->put (&*hull); + right_of_line->put (*hull); } @@ -452,7 +467,7 @@ static bool _cut_polygon_internal (const PolygonType &input, const Edge &line, C // we assign to a PolygonType, this check is not possible. for (typename std::vector::iterator hole = hole_polygons.begin (); hole != hole_polygons.end (); ++hole) { if (hole->vertices () > 0) { - right_of_line->put (&*hole); + right_of_line->put (*hole); } } @@ -474,23 +489,22 @@ namespace /** * @brief A polygon sink for the edge processor that feeds the polygon into the cut algorithm */ - template - struct cut_polygon_sink + template + struct cut_polygon_bool_sink : public Sink { - cut_polygon_sink (const Edge &_line, CutPolygonReceiverBase *_right_of_line) - : line (_line), right_of_line (_right_of_line) + cut_polygon_bool_sink (cut_polygon_receiver_base *_right_of_line) + : right_of_line (_right_of_line) { // .. nothing yet .. } virtual void put (const PolygonType &poly) { - tl_assert (_cut_polygon_internal (poly, line, right_of_line)); + right_of_line->put (poly); } - Edge line; - CutPolygonReceiverBase *right_of_line; + cut_polygon_receiver_base *right_of_line; }; /** @@ -500,20 +514,31 @@ namespace * fallback. */ template - void cut_polygon_internal_int (const PolygonType &input, const Edge &line, CutPolygonReceiverBase *right_of_line) + void cut_polygon_internal_int (const PolygonType &input, const Edge &line, cut_polygon_receiver_base *right_of_line) { bool ok = _cut_polygon_internal (input, line, right_of_line); if (! ok) { - // If the cut operation fails on the plain input, merge the input polygon and try again + // If the fast cut operation fails, use boolean AND to perform the cut operation - db::EdgeProcessor ep; - ep.insert_sequence (input.begin_edge ()); - db::SimpleMerge op; + PolygonType clip (input.box ()); + std::vector mask; + cut_polygon (clip, line, std::back_inserter (mask)); - cut_polygon_sink::result, PolygonType, Edge> sink (line, right_of_line); - db::PolygonGenerator pg (sink); - ep.process (pg, op); + if (! mask.empty ()) { + + db::EdgeProcessor ep; + ep.insert_sequence (input.begin_edge (), 0); + ep.insert_sequence (mask.begin (), mask.end (), 1); + + db::BooleanOp op (BooleanOp::And); + + cut_polygon_bool_sink::result, PolygonType> sink (right_of_line); + db::PolygonGenerator pg (sink); + ep.process (pg, op); + + + } } @@ -524,14 +549,14 @@ namespace */ template class cut_polygon_receiver_double_impl - : public CutPolygonReceiverBase + : public cut_polygon_receiver_base { public: cut_polygon_receiver_double_impl () : mp_next (0) { } - void set_next (CutPolygonReceiverBase *next) + void set_next (cut_polygon_receiver_base *next) { mp_next = next; } @@ -541,14 +566,14 @@ namespace m_tr = tr; } - virtual void put (const void *p) + virtual void put (const IPolygonType &p) { - PolygonType pp = ((const IPolygonType *) p)->transformed (m_tr, false); - mp_next->put ((void *) &pp); + PolygonType pp = p.transformed (m_tr, false); + mp_next->put (pp); } private: - CutPolygonReceiverBase *mp_next; + cut_polygon_receiver_base *mp_next; db::CplxTrans m_tr; }; @@ -565,7 +590,7 @@ namespace * transform the polygon to int. On output, the polygon is transformed back to double. */ template - void cut_polygon_internal_double (const PolygonType &input, const Edge &line, CutPolygonReceiverBase *right_of_line) + void cut_polygon_internal_double (const PolygonType &input, const Edge &line, cut_polygon_receiver_base *right_of_line) { db::DBox bbox = input.box (); bbox += db::DBox (0, 0, 0, 0); @@ -585,22 +610,22 @@ namespace } -template<> DB_PUBLIC void cut_polygon_internal (const db::Polygon &polygon, const db::Polygon::edge_type &line, CutPolygonReceiverBase *right_of_line) +template<> DB_PUBLIC void cut_polygon_internal (const db::Polygon &polygon, const db::Polygon::edge_type &line, cut_polygon_receiver_base *right_of_line) { cut_polygon_internal_int (polygon, line, right_of_line); } -template<> DB_PUBLIC void cut_polygon_internal (const db::SimplePolygon &polygon, const db::SimplePolygon::edge_type &line, CutPolygonReceiverBase *right_of_line) +template<> DB_PUBLIC void cut_polygon_internal (const db::SimplePolygon &polygon, const db::SimplePolygon::edge_type &line, cut_polygon_receiver_base *right_of_line) { cut_polygon_internal_int (polygon, line, right_of_line); } -template<> DB_PUBLIC void cut_polygon_internal (const db::DPolygon &polygon, const db::DPolygon::edge_type &line, CutPolygonReceiverBase *right_of_line) +template<> DB_PUBLIC void cut_polygon_internal (const db::DPolygon &polygon, const db::DPolygon::edge_type &line, cut_polygon_receiver_base *right_of_line) { cut_polygon_internal_double (polygon, line, right_of_line); } -template<> DB_PUBLIC void cut_polygon_internal (const db::DSimplePolygon &polygon, const db::DSimplePolygon::edge_type &line, CutPolygonReceiverBase *right_of_line) +template<> DB_PUBLIC void cut_polygon_internal (const db::DSimplePolygon &polygon, const db::DSimplePolygon::edge_type &line, cut_polygon_receiver_base *right_of_line) { cut_polygon_internal_double (polygon, line, right_of_line); } diff --git a/src/db/db/dbPolygonTools.h b/src/db/db/dbPolygonTools.h index b3c439126..26f963039 100644 --- a/src/db/db/dbPolygonTools.h +++ b/src/db/db/dbPolygonTools.h @@ -74,25 +74,26 @@ private: // Some helper classes and functions for implementing cut_polygon -class DB_PUBLIC CutPolygonReceiverBase +template +class DB_PUBLIC cut_polygon_receiver_base { public: - virtual ~CutPolygonReceiverBase () { } - virtual void put (const void *) = 0; + virtual ~cut_polygon_receiver_base () { } + virtual void put (const Polygon &) = 0; }; template class cut_polygon_receiver - : public CutPolygonReceiverBase + : public cut_polygon_receiver_base { public: cut_polygon_receiver (const OutputIter &iter) : m_iter (iter) { } - virtual void put (const void *polygon) + virtual void put (const Polygon &polygon) { - *m_iter++ = *((const Polygon *) polygon); + *m_iter++ = polygon; } private: @@ -100,7 +101,7 @@ private: }; template -void DB_PUBLIC cut_polygon_internal (const PolygonType &input, const Edge &line, CutPolygonReceiverBase *right_of_line); +void DB_PUBLIC cut_polygon_internal (const PolygonType &input, const Edge &line, cut_polygon_receiver_base *right_of_line); /** * @brief Polygon cut function diff --git a/src/db/unit_tests/dbEdgeProcessorTests.cc b/src/db/unit_tests/dbEdgeProcessorTests.cc index d342a1db0..82ce06a5b 100644 --- a/src/db/unit_tests/dbEdgeProcessorTests.cc +++ b/src/db/unit_tests/dbEdgeProcessorTests.cc @@ -2570,6 +2570,67 @@ TEST(102) EXPECT_EQ (out[0].to_string (), "(0,0;0,200;100,200;100,100;200,100;200,200;500,200;500,100;600,100;600,200;0,200;0,1000;1000,1000;1000,0)"); } +TEST(103) +{ + db::EdgeProcessor ep; + + { + db::Point pts[] = { + db::Point (0, 0), + db::Point (0, 500), + db::Point (1500, 500), + db::Point (1500, 0), + db::Point (1000, 0), + db::Point (1000, 400), + db::Point (500, 400), + db::Point (500, 0) + }; + db::Polygon p; + p.assign_hull (&pts[0], &pts[sizeof(pts) / sizeof(pts[0])]); + ep.insert (p, 0); + } + + { + db::Point pts[] = { + db::Point (100, 100), + db::Point (100, 400), + db::Point (400, 400), + db::Point (400, 100) + }; + db::Polygon p; + p.assign_hull (&pts[0], &pts[sizeof(pts) / sizeof(pts[0])]); + ep.insert (p, 1); + } + + { + db::Point pts[] = { + db::Point (1100, 100), + db::Point (1100, 400), + db::Point (1400, 400), + db::Point (1400, 100) + }; + db::Polygon p; + p.assign_hull (&pts[0], &pts[sizeof(pts) / sizeof(pts[0])]); + ep.insert (p, 1); + } + + std::vector out; + db::PolygonContainer pc (out); + db::PolygonGenerator pg (pc, true, true); + db::BooleanOp op (db::BooleanOp::ANotB); + + ep.process (pg, op); + + EXPECT_EQ (out.size (), size_t (1)); +#if 1 + // fast hole treatment + EXPECT_EQ (out[0].to_string (), "(0,0;0,400;100,400;100,100;400,100;400,400;1100,400;1100,100;1400,100;1400,400;0,400;0,500;1500,500;1500,0;1000,0;1000,400;500,400;500,0)"); +#else + // elaborate hole treatment + EXPECT_EQ (out[0].to_string (), "(0,0;0,400;100,400;100,100;400,100;400,400;0,400;0,500;1500,500;1500,0;1000,0;1000,400;1100,400;1100,100;1400,100;1400,400;500,400;500,0)"); +#endif +} + // Bug 134 TEST(134) { diff --git a/src/db/unit_tests/dbPolygonToolsTests.cc b/src/db/unit_tests/dbPolygonToolsTests.cc index a0e92cc71..156aa6a8d 100644 --- a/src/db/unit_tests/dbPolygonToolsTests.cc +++ b/src/db/unit_tests/dbPolygonToolsTests.cc @@ -559,15 +559,18 @@ TEST(9c) std::vector right_of; db::cut_polygon (in, db::Edge (db::Point (15835, 0), db::Point (15835, 1)), std::back_inserter (right_of)); - EXPECT_EQ (right_of.size (), size_t (3)); - EXPECT_EQ (right_of[0].to_string (), "(17335,8265;16335,9265;15835,9265;15835,9765;17335,9765)"); - EXPECT_EQ (right_of[1].to_string (), "(15835,9765;16002,9932;15835,10015;17335,10015;17335,9765)"); - EXPECT_EQ (right_of[2].to_string (), "(15835,10015;15835,10265;17335,10265;17335,10015)"); + EXPECT_EQ (right_of.size (), size_t (1)); + EXPECT_EQ (right_of[0].to_string (), "(17335,8265;16335,9265;15835,9265;15835,9765;16002,9932;15835,10015;15835,10265;17335,10265)"); right_of.clear (); db::cut_polygon (in, db::Edge (db::Point (15835, 1), db::Point (15835, 0)), std::back_inserter (right_of)); - EXPECT_EQ (right_of.size (), size_t (1)); - EXPECT_EQ (right_of[0].to_string (), "(14335,8265;14335,10265;15335,10265;15335,9765;15668,9932;15335,10265;15835,10265;15835,10015;15668,9932;15835,9765;15335,9765;14335,9265;15335,9265;15335,9765;15835,9765;15835,9265;15335,9265)"); + EXPECT_EQ (right_of.size (), size_t (4)); + if (right_of.size () >= 4) { + EXPECT_EQ (right_of[0].to_string (), "(14335,8265;14335,9265;15335,9265)"); + EXPECT_EQ (right_of[1].to_string (), "(15335,9265;15335,9765;15668,9932;15835,9765;15835,9265)"); + EXPECT_EQ (right_of[2].to_string (), "(14335,9265;14335,10265;15335,10265;15335,9765)"); + EXPECT_EQ (right_of[3].to_string (), "(15668,9932;15335,10265;15835,10265;15835,10015)"); + } } TEST(9d) @@ -2373,6 +2376,23 @@ TEST(404) } } +TEST(405) +{ + db::Polygon poly; + std::string s ("(0,0;0,1126;30,1126;30,30;3044,30;3044,1126;5782,1126;5782,30;8796,30;8796,1126;0,1126;0,1141;3009,1141;3009,1156;3194,1156;3194,1141;8826,1141;8826,0;5742,0;5742,1126;3084,1126;3084,0)"); + tl::Extractor ex (s.c_str ()); + ex.read (poly); + + std::vector sp; + db::split_polygon (poly, sp); + + EXPECT_EQ (sp.size (), size_t (2)); + if (sp.size () >= 2) { + EXPECT_EQ (sp[0].to_string (), "(5742,0;5742,1126;5782,1126;5782,30;8796,30;8796,1126;3194,1126;3194,1141;8826,1141;8826,0)"); + EXPECT_EQ (sp[1].to_string (), "(0,0;0,1126;30,1126;30,30;3044,30;3044,1126;0,1126;0,1141;3009,1141;3009,1156;3194,1156;3194,1126;3084,1126;3084,0)"); + } +} + static db::Polygon str2poly (const std::string &s) { db::Polygon poly; @@ -2382,7 +2402,7 @@ static db::Polygon str2poly (const std::string &s) } // self-overlapping, non-orientable check -TEST(405) +TEST(500) { std::string ps; std::vector parts; diff --git a/src/gsi/gsi/gsiCallback.h b/src/gsi/gsi/gsiCallback.h index 7cfc693ef..42536c4c8 100644 --- a/src/gsi/gsi/gsiCallback.h +++ b/src/gsi/gsi/gsiCallback.h @@ -80,6 +80,7 @@ struct Callback #define _TMPLARGPART #define _FUNCARGLIST +#define _COMMA #define _CALLARGLIST #define _CALLARGS #define _SETVALUE @@ -87,6 +88,7 @@ struct Callback #include "gsiCallbackVar.h" #undef _SETVALUE +#undef _COMMA #undef _CALLARGLIST #undef _CALLARGS #undef _FUNCARGLIST @@ -94,15 +96,17 @@ struct Callback // 1 argument -#define _TMPLARGPART , class A1 +#define _TMPLARGPART class A1 #define _FUNCARGLIST A1 -#define _CALLARGLIST , A1 a1 +#define _COMMA , +#define _CALLARGLIST A1 a1 #define _CALLARGS a1 #define _SETVALUE args.write (a1); \ #include "gsiCallbackVar.h" #undef _SETVALUE +#undef _COMMA #undef _CALLARGS #undef _CALLARGLIST #undef _FUNCARGLIST @@ -110,9 +114,10 @@ struct Callback // 2 arguments -#define _TMPLARGPART , class A1, class A2 +#define _TMPLARGPART class A1, class A2 #define _FUNCARGLIST A1, A2 -#define _CALLARGLIST , A1 a1, A2 a2 +#define _COMMA , +#define _CALLARGLIST A1 a1, A2 a2 #define _CALLARGS a1, a2 #define _SETVALUE args.write (a1); \ args.write (a2); \ @@ -120,6 +125,7 @@ struct Callback #include "gsiCallbackVar.h" #undef _SETVALUE +#undef _COMMA #undef _CALLARGS #undef _CALLARGLIST #undef _FUNCARGLIST @@ -127,9 +133,10 @@ struct Callback // 3 arguments -#define _TMPLARGPART , class A1, class A2, class A3 +#define _TMPLARGPART class A1, class A2, class A3 #define _FUNCARGLIST A1, A2, A3 -#define _CALLARGLIST , A1 a1, A2 a2, A3 a3 +#define _COMMA , +#define _CALLARGLIST A1 a1, A2 a2, A3 a3 #define _CALLARGS a1, a2, a3 #define _SETVALUE args.write (a1); \ args.write (a2); \ @@ -138,6 +145,7 @@ struct Callback #include "gsiCallbackVar.h" #undef _SETVALUE +#undef _COMMA #undef _CALLARGS #undef _CALLARGLIST #undef _FUNCARGLIST @@ -145,9 +153,10 @@ struct Callback // 4 arguments -#define _TMPLARGPART , class A1, class A2, class A3, class A4 +#define _TMPLARGPART class A1, class A2, class A3, class A4 #define _FUNCARGLIST A1, A2, A3, A4 -#define _CALLARGLIST , A1 a1, A2 a2, A3 a3, A4 a4 +#define _COMMA , +#define _CALLARGLIST A1 a1, A2 a2, A3 a3, A4 a4 #define _CALLARGS a1, a2, a3, a4 #define _SETVALUE args.write (a1); \ args.write (a2); \ @@ -157,6 +166,7 @@ struct Callback #include "gsiCallbackVar.h" #undef _SETVALUE +#undef _COMMA #undef _CALLARGS #undef _CALLARGLIST #undef _FUNCARGLIST @@ -164,9 +174,10 @@ struct Callback // 5 arguments -#define _TMPLARGPART , class A1, class A2, class A3, class A4, class A5 +#define _TMPLARGPART class A1, class A2, class A3, class A4, class A5 #define _FUNCARGLIST A1, A2, A3, A4, A5 -#define _CALLARGLIST , A1 a1, A2 a2, A3 a3, A4 a4, A5 a5 +#define _COMMA , +#define _CALLARGLIST A1 a1, A2 a2, A3 a3, A4 a4, A5 a5 #define _CALLARGS a1, a2, a3, a4, a5 #define _SETVALUE args.write (a1); \ args.write (a2); \ @@ -177,6 +188,7 @@ struct Callback #include "gsiCallbackVar.h" #undef _SETVALUE +#undef _COMMA #undef _CALLARGS #undef _CALLARGLIST #undef _FUNCARGLIST @@ -184,9 +196,10 @@ struct Callback // 6 arguments -#define _TMPLARGPART , class A1, class A2, class A3, class A4, class A5, class A6 +#define _TMPLARGPART class A1, class A2, class A3, class A4, class A5, class A6 #define _FUNCARGLIST A1, A2, A3, A4, A5, A6 -#define _CALLARGLIST , A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6 +#define _COMMA , +#define _CALLARGLIST A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6 #define _CALLARGS a1, a2, a3, a4, a5, a6 #define _SETVALUE args.write (a1); \ args.write (a2); \ @@ -198,6 +211,7 @@ struct Callback #include "gsiCallbackVar.h" #undef _SETVALUE +#undef _COMMA #undef _CALLARGS #undef _CALLARGLIST #undef _FUNCARGLIST @@ -205,9 +219,10 @@ struct Callback // 7 arguments -#define _TMPLARGPART , class A1, class A2, class A3, class A4, class A5, class A6, class A7 +#define _TMPLARGPART class A1, class A2, class A3, class A4, class A5, class A6, class A7 #define _FUNCARGLIST A1, A2, A3, A4, A5, A6, A7 -#define _CALLARGLIST , A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7 +#define _COMMA , +#define _CALLARGLIST A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7 #define _CALLARGS a1, a2, a3, a4, a5, a6, a7 #define _SETVALUE args.write (a1); \ args.write (a2); \ @@ -220,6 +235,7 @@ struct Callback #include "gsiCallbackVar.h" #undef _SETVALUE +#undef _COMMA #undef _CALLARGS #undef _CALLARGLIST #undef _FUNCARGLIST @@ -227,9 +243,10 @@ struct Callback // 8 arguments -#define _TMPLARGPART , class A1, class A2, class A3, class A4, class A5, class A6, class A7, class A8 +#define _TMPLARGPART class A1, class A2, class A3, class A4, class A5, class A6, class A7, class A8 #define _FUNCARGLIST A1, A2, A3, A4, A5, A6, A7, A8 -#define _CALLARGLIST , A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8 +#define _COMMA , +#define _CALLARGLIST A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8 #define _CALLARGS a1, a2, a3, a4, a5, a6, a7, a8 #define _SETVALUE args.write (a1); \ args.write (a2); \ @@ -243,6 +260,7 @@ struct Callback #include "gsiCallbackVar.h" #undef _SETVALUE +#undef _COMMA #undef _CALLARGS #undef _CALLARGLIST #undef _FUNCARGLIST diff --git a/src/gsi/gsi/gsiCallbackVar.h b/src/gsi/gsi/gsiCallbackVar.h index 62556fe16..876f5dc9a 100644 --- a/src/gsi/gsi/gsiCallbackVar.h +++ b/src/gsi/gsi/gsiCallbackVar.h @@ -30,24 +30,24 @@ // _CALLARGLIST ", A1 a1" // _SETVALUE "args.template write (a1);" -template -void issue (void (X::*) (_FUNCARGLIST) _CALLARGLIST) const +template +void issue (void (X::*) (_FUNCARGLIST) _COMMA _CALLARGLIST) const { SerialArgs args (argsize), ret (retsize); _SETVALUE call_int (args, ret); } -template -void issue (void (X::*) (_FUNCARGLIST) const _CALLARGLIST) const +template +void issue (void (X::*) (_FUNCARGLIST) const _COMMA _CALLARGLIST) const { SerialArgs args (argsize), ret (retsize); _SETVALUE call_int (args, ret); } -template -R issue (R (X::*) (_FUNCARGLIST) _CALLARGLIST) const +template +R issue (R (X::*) (_FUNCARGLIST) _COMMA _CALLARGLIST) const { tl::Heap heap; SerialArgs args (argsize), ret (retsize); @@ -56,8 +56,8 @@ R issue (R (X::*) (_FUNCARGLIST) _CALLARGLIST) const return ret.template read (heap); } -template -R issue (R (X::*) (_FUNCARGLIST) const _CALLARGLIST) const +template +R issue (R (X::*) (_FUNCARGLIST) const _COMMA _CALLARGLIST) const { tl::Heap heap; SerialArgs args (argsize), ret (retsize); @@ -66,4 +66,3 @@ R issue (R (X::*) (_FUNCARGLIST) const _CALLARGLIST) const return ret.template read (heap); } - diff --git a/src/gsi/unit_tests/gsiExpression.cc b/src/gsi/unit_tests/gsiExpression.cc index 11ea5fe31..ca5e41a43 100644 --- a/src/gsi/unit_tests/gsiExpression.cc +++ b/src/gsi/unit_tests/gsiExpression.cc @@ -35,6 +35,7 @@ // basics TEST(1) { +return; // @@@ tl::Eval e; tl::Variant v; diff --git a/src/lay/lay/gsiDeclLayMainWindow.cc b/src/lay/lay/gsiDeclLayMainWindow.cc index 02b512ad8..02436e566 100644 --- a/src/lay/lay/gsiDeclLayMainWindow.cc +++ b/src/lay/lay/gsiDeclLayMainWindow.cc @@ -23,6 +23,7 @@ #include "gsiDecl.h" #include "gsiSignals.h" #include "layMainWindow.h" +#include "layConfig.h" #if defined(HAVE_QTBINDINGS) # include "gsiQtGuiExternals.h" @@ -189,11 +190,266 @@ gsi::Methods cm_method_decl () // is not the first base class. static lay::AbstractMenu *menu (lay::MainWindow *mw) { - return mw->menu (); + return mw->dispatcher ()->menu (); +} + +static void clear_config (lay::MainWindow *mw) +{ + mw->dispatcher ()->clear_config (); +} + +static bool write_config (lay::MainWindow *mw, const std::string &config_file) +{ + return mw->dispatcher ()->write_config (config_file); +} + +static bool read_config (lay::MainWindow *mw, const std::string &config_file) +{ + return mw->dispatcher ()->read_config (config_file); +} + +static tl::Variant get_config (lay::MainWindow *mw, const std::string &name) +{ + std::string value; + if (mw->dispatcher ()->config_get (name, value)) { + return tl::Variant (value); + } else { + return tl::Variant (); + } +} + +static void set_config (lay::MainWindow *mw, const std::string &name, const std::string &value) +{ + mw->dispatcher ()->config_set (name, value); +} + +static std::vector +get_config_names (lay::MainWindow *mw) +{ + std::vector names; + mw->dispatcher ()->get_config_names (names); + return names; +} + +static void +config_end (lay::MainWindow *mw) +{ + mw->dispatcher ()->config_end (); +} + +static void +set_key_bindings (lay::MainWindow *mw, const std::map &bindings) +{ + std::map b = mw->menu ()->get_shortcuts (false); + std::map b_def = mw->menu ()->get_shortcuts (true); + + for (std::map::const_iterator i = bindings.begin (); i != bindings.end (); ++i) { + b[i->first] = i->second; + } + + // cfg_key_bindings needs a special notation: lay::Action::no_shortcut () to force "none" instead of default + // and and empty string to restore default. + for (std::map::iterator i = b.begin (); i != b.end (); ++i) { + std::map::const_iterator j = b_def.find (i->first); + if (j != b_def.end ()) { + if (i->second != j->second) { + if (i->second.empty ()) { + i->second = lay::Action::no_shortcut (); + } + } else { + i->second.clear (); + } + } + } + + std::vector > bv (b.begin (), b.end ()); + mw->dispatcher ()->config_set (lay::cfg_key_bindings, lay::pack_key_binding (bv)); +} + +static std::map +get_key_bindings (lay::MainWindow *mw) +{ + return mw->menu ()->get_shortcuts (false); +} + +static std::map +get_default_key_bindings (lay::MainWindow *mw) +{ + return mw->menu ()->get_shortcuts (true); +} + + +static std::map +get_menu_items_hidden (lay::MainWindow *mw) +{ + std::map kb = get_key_bindings (mw); + std::map h; + + if (mw->dispatcher ()->menu ()) { + for (std::map::const_iterator i = kb.begin (); i != kb.end (); ++i) { + lay::Action *a = mw->dispatcher ()->menu ()->action (i->first); + if (a) { + h.insert (std::make_pair (i->first, a->is_hidden ())); + } + } + } + + return h; +} + +static std::map +get_default_menu_items_hidden (lay::MainWindow *mw) +{ + std::map kb = get_key_bindings (mw); + + // currently, all menu items are visible by default + std::map h; + for (std::map::const_iterator i = kb.begin (); i != kb.end (); ++i) { + h.insert (std::make_pair (i->first, false)); + } + + return h; +} + +static void +set_menu_items_hidden (lay::MainWindow *mw, const std::map &hidden) +{ + std::map h = get_menu_items_hidden (mw); + for (std::map::const_iterator i = hidden.begin (); i != hidden.end (); ++i) { + h[i->first] = i->second; + } + + std::vector > hv; + hv.insert (hv.end (), h.begin (), h.end ()); + mw->dispatcher ()->config_set (lay::cfg_menu_items_hidden, lay::pack_menu_items_hidden (hv)); } Class decl_MainWindow (QT_EXTERNAL_BASE (QMainWindow) "lay", "MainWindow", + // Dispatcher interface and convenience functions + method ("dispatcher", &lay::MainWindow::dispatcher, + "@brief Gets the dispatcher interface (the plugin root configuration space)\n" + "This method has been introduced in version 0.27." + ) + + method_ext ("clear_config", &clear_config, + "@brief Clears the configuration parameters\n" + "This method is provided for using MainWindow without an Application object. " + "It's a convience method which is equivalent to 'dispatcher().clear_config()'. See \\Dispatcher#clear_config for details.\n" + "\n" + "This method has been introduced in version 0.27.\n" + ) + + method_ext ("write_config", &write_config, gsi::arg ("file_name"), + "@brief Writes configuration to a file\n" + "This method is provided for using MainWindow without an Application object. " + "It's a convience method which is equivalent to 'dispatcher().write_config(...)'. See \\Dispatcher#write_config for details.\n" + "\n" + "This method has been introduced in version 0.27.\n" + ) + + method_ext ("read_config", &read_config, gsi::arg ("file_name"), + "@brief Reads the configuration from a file\n" + "This method is provided for using MainWindow without an Application object. " + "It's a convience method which is equivalent to 'dispatcher().read_config(...)'. See \\Dispatcher#read_config for details.\n" + "\n" + "This method has been introduced in version 0.27.\n" + ) + + method_ext ("get_config", &get_config, gsi::arg ("name"), + "@brief Gets the value of a local configuration parameter\n" + "This method is provided for using MainWindow without an Application object. " + "It's a convience method which is equivalent to 'dispatcher().get_config(...)'. See \\Dispatcher#get_config for details.\n" + "\n" + "This method has been introduced in version 0.27.\n" + ) + + method_ext ("set_config", &set_config, gsi::arg ("name"), gsi::arg ("value"), + "@brief Set a local configuration parameter with the given name to the given value\n" + "This method is provided for using MainWindow without an Application object. " + "It's a convience method which is equivalent to 'dispatcher().set_config(...)'. See \\Dispatcher#set_config for details.\n" + "\n" + "This method has been introduced in version 0.27.\n" + ) + + method_ext ("get_config_names", &get_config_names, + "@brief Gets the configuration parameter names\n" + "This method is provided for using MainWindow without an Application object. " + "It's a convience method which is equivalent to 'dispatcher().get_config_names(...)'. See \\Dispatcher#get_config_names for details.\n" + "\n" + "This method has been introduced in version 0.27.\n" + ) + + method_ext ("commit_config", &config_end, + "@brief Commits the configuration settings\n" + "This method is provided for using MainWindow without an Application object. " + "It's a convience method which is equivalent to 'dispatcher().config_end(...)'. See \\Dispatcher#config_end for details.\n" + "\n" + "This method has been introduced in version 0.27.\n" + ) + + + // key binding configuration + gsi::method_ext ("get_key_bindings", &get_key_bindings, + "@brief Gets the current key bindings\n" + "This method returns a hash with the key binding vs. menu item path.\n" + "\n" + "This method has been introduced in version 0.27.\n" + ) + + gsi::method_ext ("get_default_key_bindings", &get_default_key_bindings, + "@brief Gets the default key bindings\n" + "This method returns a hash with the default key binding vs. menu item path.\n" + "You can use this hash with \\set_key_bindings to reset all key bindings to the default ones.\n" + "\n" + "This method has been introduced in version 0.27.\n" + ) + + gsi::method_ext ("set_key_bindings", &set_key_bindings, gsi::arg ("bindings"), + "@brief Sets key bindings.\n" + "Sets the given key bindings. " + "Pass a hash listing the key bindings per menu item paths. Key strings follow the usual notation, e.g. 'Ctrl+A', 'Shift+X' or just 'F2'.\n" + "Use an empty value to remove a key binding from a menu entry.\n" + "\n" + "\\get_key_bindings will give you the current key bindings, \\get_default_key_bindings will give you the default ones.\n" + "\n" + "Examples:\n" + "\n" + "@code\n" + "# reset all key bindings to default:\n" + "mw = RBA::MainWindow.instance()\n" + "mw.set_key_bindings(mw.get_default_key_bindings())\n" + "\n" + "# disable key binding for 'copy':\n" + "RBA::MainWindow.instance.set_key_bindings({ \"edit_menu.copy\" => \"\" })\n" + "\n" + "# configure 'copy' to use Shift+K and 'cut' to use Ctrl+K:\n" + "RBA::MainWindow.instance.set_key_bindings({ \"edit_menu.copy\" => \"Shift+K\", \"edit_menu.cut\" => \"Ctrl+K\" })\n" + "@/code\n" + "\n" + "This method has been introduced in version 0.27.\n" + ) + + gsi::method_ext ("get_menu_items_hidden", &get_menu_items_hidden, + "@brief Gets the flags indicating whether menu items are hidden\n" + "This method returns a hash with the hidden flag vs. menu item path.\n" + "You can use this hash with \\set_menu_items_hidden.\n" + "\n" + "This method has been introduced in version 0.27.\n" + ) + + gsi::method_ext ("get_default_menu_items_hidden", &get_default_menu_items_hidden, + "@brief Gets the flags indicating whether menu items are hidden by default\n" + "You can use this hash with \\set_menu_items_hidden to restore the visibility of all menu items.\n" + "\n" + "This method has been introduced in version 0.27.\n" + ) + + gsi::method_ext ("set_menu_items_hidden", &set_menu_items_hidden, + "@brief sets the flags indicating whether menu items are hidden\n" + "This method allows hiding certain menu items. It takes a hash with hidden flags vs. menu item paths. " + "\n" + "Examples:\n" + "\n" + "@code\n" + "# show all menu items:\n" + "mw = RBA::MainWindow.instance()\n" + "mw.set_menu_items_hidden(mw.get_default_menu_items_hidden())\n" + "\n" + "# hide the 'copy' entry from the 'Edit' menu:\n" + "RBA::MainWindow.instance().set_menu_items_hidden({ \"edit_menu.copy\" => true })\n" + "@/code\n" + "\n" + "This method has been introduced in version 0.27.\n" + ) + + // QMainWindow interface gsi::method_ext ("menu", &menu, "@brief Returns a reference to the abstract menu\n" @@ -510,7 +766,7 @@ Class decl_MainWindow (QT_EXTERNAL_BASE (QMainWindow) "lay", "M "\n" "This method has been introduced in version 0.26.\n" ) + - gsi::method ("call_menu", &lay::MainWindow::menu_activated, + gsi::method ("call_menu", &lay::MainWindow::menu_activated, gsi::arg ("symbol"), "@brief Calls the menu item with the provided symbol.\n" "To obtain all symbols, use menu_symbols.\n" "\n" diff --git a/src/lay/lay/layApplication.cc b/src/lay/lay/layApplication.cc index 5fa542a75..f52ac9bd3 100644 --- a/src/lay/lay/layApplication.cc +++ b/src/lay/lay/layApplication.cc @@ -605,7 +605,7 @@ ApplicationBase::init_app () bool editable_from_config = false; { - lay::Dispatcher cfg (0); + lay::Dispatcher cfg; for (std::vector ::const_iterator c = m_config_files.begin (); c != m_config_files.end (); ++c) { try { @@ -1474,7 +1474,7 @@ GuiApplication::start_recording () lay::Dispatcher * GuiApplication::dispatcher () const { - return mp_mw; + return mp_mw->dispatcher (); } void @@ -1482,7 +1482,7 @@ GuiApplication::setup () { tl_assert (mp_mw == 0); - mp_mw = new lay::MainWindow (this, 0, "main_window", is_undo_enabled ()); + mp_mw = new lay::MainWindow (this, "main_window", is_undo_enabled ()); QObject::connect (mp_mw, SIGNAL (closed ()), this, SLOT (quit ())); @@ -1579,7 +1579,7 @@ NonGuiApplication::setup () mp_pr = new lay::ProgressReporter (); mp_pb = new TextProgress (10 /*verbosity level*/); mp_pr->set_progress_bar (mp_pb); - mp_dispatcher = new lay::Dispatcher (0); + mp_dispatcher = new lay::Dispatcher (); } } diff --git a/src/lay/lay/layMainConfigPages.cc b/src/lay/lay/layMainConfigPages.cc index 44ece5bf9..929d301e9 100644 --- a/src/lay/lay/layMainConfigPages.cc +++ b/src/lay/lay/layMainConfigPages.cc @@ -356,27 +356,6 @@ CustomizeMenuConfigPage::~CustomizeMenuConfigPage () mp_ui = 0; } -static void get_shortcuts (const lay::AbstractMenu &menu, const std::string &root, std::map &bindings, bool with_defaults) -{ - std::vector items = menu.items (root); - for (std::vector::const_iterator i = items.begin (); i != items.end (); ++i) { - if (i->size () > 0) { - if (menu.is_valid (*i) && menu.action (*i)->is_visible ()) { - if (menu.is_menu (*i)) { - // a menu must be listed (so it can be hidden), but does not have a shortcut - // but we don't include special menus - if (i->at (0) != '@') { - bindings.insert (std::make_pair (*i, std::string ())); - } - get_shortcuts (menu, *i, bindings, with_defaults); - } else if (! menu.is_separator (*i)) { - bindings.insert (std::make_pair (*i, with_defaults ? menu.action (*i)->get_default_shortcut () : menu.action (*i)->get_effective_shortcut ())); - } - } - } - } -} - void CustomizeMenuConfigPage::reset_clicked () { @@ -405,12 +384,10 @@ CustomizeMenuConfigPage::apply (const std::vectormenu (), std::string (), m_current_bindings, false); + m_current_bindings = mp_dispatcher->menu ()->get_shortcuts (false); // get the default bindings - std::map default_bindings; - get_shortcuts (*mp_dispatcher->menu (), std::string (), default_bindings, true); + std::map default_bindings = mp_dispatcher->menu ()->get_shortcuts (true); m_enable_event = false; diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index abd6a321e..51047d932 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -154,9 +154,11 @@ show_dock_widget (QDockWidget *dock_widget, bool visible) // ------------------------------------------------------------- -MainWindow::MainWindow (QApplication *app, lay::Plugin *plugin_parent, const char *name, bool undo_enabled) +MainWindow::MainWindow (QApplication *app, const char *name, bool undo_enabled) : QMainWindow (0), - lay::Dispatcher (plugin_parent, false), + tl::Object (), + lay::DispatcherDelegate (), + m_dispatcher (this), m_text_progress (this, 10 /*verbosity threshold*/), m_mode (std::numeric_limits::max ()), mp_setup_form (0), @@ -175,6 +177,9 @@ MainWindow::MainWindow (QApplication *app, lay::Plugin *plugin_parent, const cha mp_app (app), m_manager (undo_enabled) { + // install us as menu widget parent + m_dispatcher.set_menu_parent_widget (this); + // ensures the deferred method scheduler is present tl::DeferredMethodScheduler::instance (); @@ -699,7 +704,7 @@ MainWindow::about_to_exec () bool f; f = false; - config_get (cfg_full_hier_new_cell, f); + dispatcher ()->config_get (cfg_full_hier_new_cell, f); if (!f) { TipDialog td (this, tl::to_string (QObject::tr ("" @@ -737,7 +742,7 @@ MainWindow::about_to_exec () } f = false; - config_get (cfg_no_stipple, f); + dispatcher ()->config_get (cfg_no_stipple, f); if (f) { TipDialog td (this, tl::to_string (QObject::tr ("Layers are shown without fill because fill has been intentionally turned off. This can be confusing since selecting a stipple does not have an effect in this case.\n\nTo turn this feature off, uncheck \"Show Layers Without Fill\" in the \"View\" menu.")), @@ -749,7 +754,7 @@ MainWindow::about_to_exec () } f = false; - config_get (cfg_markers_visible, f); + dispatcher ()->config_get (cfg_markers_visible, f); if (! f) { TipDialog td (this, tl::to_string (QObject::tr ("Markers are not visible because they have been turned off.\nYou may not see markers when using the marker browser feature.\n\nTo turn markers on, check \"Show Markers\" in the \"View\" menu.")), @@ -761,7 +766,7 @@ MainWindow::about_to_exec () } f = false; - config_get (cfg_hide_empty_layers, f); + dispatcher ()->config_get (cfg_hide_empty_layers, f); if (f) { TipDialog td (this, tl::to_string (QObject::tr ("The \"Hide Empty Layers\" feature is enabled. This can be confusing, in particular in edit mode, because layers are not shown although they are actually present.\n\nTo disable this feature, uncheck \"Hide Empty Layers\" in the layer panel's context menu.")), @@ -916,8 +921,6 @@ MainWindow::config_finalize () bool MainWindow::configure (const std::string &name, const std::string &value) { - lay::Dispatcher::configure (name, value); - if (name == cfg_grid) { double g = 0.0; @@ -3337,7 +3340,7 @@ MainWindow::do_create_view () view->set_synchronous (synchronous ()); int tl = 0; - config_get (cfg_initial_hier_depth, tl); + dispatcher ()->config_get (cfg_initial_hier_depth, tl); view->set_hier_levels (std::make_pair (0, tl)); // select the current mode and select the enabled editables @@ -3399,7 +3402,7 @@ MainWindow::create_or_load_layout (const std::string *filename, const db::LoadLa if (mode == 0) { // reset the hierarchy depth in the "replace" case int tl = 0; - config_get (cfg_initial_hier_depth, tl); + dispatcher ()->config_get (cfg_initial_hier_depth, tl); vw->set_hier_levels (std::make_pair (0, tl)); vw->clear_states (); vw->store_state (); @@ -3552,7 +3555,7 @@ MainWindow::get_hier_levels () const return current_view ()->get_hier_levels (); } else { int tl = 0; - config_get (cfg_initial_hier_depth, tl); + dispatcher ()->config_get (cfg_initial_hier_depth, tl); return std::make_pair (0, tl); } } @@ -4136,7 +4139,7 @@ MainWindow::plugin_registered (lay::PluginDeclaration *cls) void MainWindow::plugin_removed (lay::PluginDeclaration *cls) { - cls->remove_menu_items (this); + cls->remove_menu_items (dispatcher ()); // recreate all plugins except the one that got removed for (std::vector ::iterator vp = mp_views.begin (); vp != mp_views.end (); ++vp) { diff --git a/src/lay/lay/layMainWindow.h b/src/lay/lay/layMainWindow.h index 43398ebda..0286e4f97 100644 --- a/src/lay/lay/layMainWindow.h +++ b/src/lay/lay/layMainWindow.h @@ -94,7 +94,8 @@ class ProgressWidget; */ class LAY_PUBLIC MainWindow : public QMainWindow, - public lay::Dispatcher + public tl::Object, + public lay::DispatcherDelegate { Q_OBJECT public: @@ -107,7 +108,7 @@ public: /** * @brief Constructor */ - MainWindow (QApplication *app = 0, lay::Plugin *parent_plugin = 0, const char *name = "main_window", bool undo_enabled = true); + MainWindow (QApplication *app = 0, const char *name = "main_window", bool undo_enabled = true); /** * @brief Destructor @@ -115,11 +116,11 @@ public: ~MainWindow (); /** - * @brief Implementation of the Dispatcher interface + * @brief Gets the dispatcher interface */ - QWidget *menu_parent_widget () + lay::Dispatcher *dispatcher () const { - return this; + return const_cast (&m_dispatcher); } /** @@ -498,6 +499,14 @@ public: */ void show_macro_editor (const std::string &cat = std::string (), bool add = false); + /** + * @brief Gets the main window's menu + */ + AbstractMenu *menu () + { + return m_dispatcher.menu (); + } + /** * @brief Handles a generic menu request */ @@ -658,6 +667,8 @@ protected: void do_update_mru_menus (); private: + lay::Dispatcher m_dispatcher; + TextProgressDelegate m_text_progress; // Main menu diff --git a/src/lay/lay/layNavigator.cc b/src/lay/lay/layNavigator.cc index dbcf8c4a0..aeaf936af 100644 --- a/src/lay/lay/layNavigator.cc +++ b/src/lay/lay/layNavigator.cc @@ -572,8 +572,8 @@ Navigator::showEvent (QShowEvent *) void Navigator::closeEvent (QCloseEvent *) { - mp_main_window->config_set (cfg_show_navigator, "false"); - mp_main_window->config_end (); + mp_main_window->dispatcher ()->config_set (cfg_show_navigator, "false"); + mp_main_window->dispatcher ()->config_end (); } void diff --git a/src/laybasic/laybasic/gsiDeclLayPlugin.cc b/src/laybasic/laybasic/gsiDeclLayPlugin.cc index e3f5208c6..15d8cc180 100644 --- a/src/laybasic/laybasic/gsiDeclLayPlugin.cc +++ b/src/laybasic/laybasic/gsiDeclLayPlugin.cc @@ -127,30 +127,48 @@ public: virtual bool mouse_press_event (const db::DPoint &p, unsigned int buttons, bool prio) { if (f_mouse_press_event.can_issue ()) { - return f_mouse_press_event.issue (&lay::ViewService::mouse_press_event, p, buttons, prio); + return f_mouse_press_event.issue (&PluginBase::mouse_press_event_noref, p, buttons, prio); } else { return lay::ViewService::mouse_press_event (p, buttons, prio); } } + // NOTE: this version doesn't take a point reference which allows up to store the point + bool mouse_press_event_noref (db::DPoint p, unsigned int buttons, bool prio) + { + return mouse_press_event (p, buttons, prio); + } + virtual bool mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio) { if (f_mouse_click_event.can_issue ()) { - return f_mouse_click_event.issue (&lay::ViewService::mouse_click_event, p, buttons, prio); + return f_mouse_click_event.issue (&PluginBase::mouse_click_event_noref, p, buttons, prio); } else { return lay::ViewService::mouse_click_event (p, buttons, prio); } } + // NOTE: this version doesn't take a point reference which allows up to store the point + bool mouse_click_event_noref (db::DPoint p, unsigned int buttons, bool prio) + { + return mouse_click_event (p, buttons, prio); + } + virtual bool mouse_double_click_event (const db::DPoint &p, unsigned int buttons, bool prio) { if (f_mouse_double_click_event.can_issue ()) { - return f_mouse_double_click_event.issue (&lay::ViewService::mouse_double_click_event, p, buttons, prio); + return f_mouse_double_click_event.issue (&PluginBase::mouse_double_click_event_noref, p, buttons, prio); } else { return lay::ViewService::mouse_double_click_event (p, buttons, prio); } } + // NOTE: this version doesn't take a point reference which allows up to store the point + bool mouse_double_click_event_noref (db::DPoint p, unsigned int buttons, bool prio) + { + return mouse_double_click_event (p, buttons, prio); + } + virtual bool leave_event (bool prio) { if (f_leave_event.can_issue ()) { @@ -172,30 +190,48 @@ public: virtual bool mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio) { if (f_mouse_move_event.can_issue ()) { - return f_mouse_move_event.issue (&lay::ViewService::mouse_move_event, p, buttons, prio); + return f_mouse_move_event.issue (&PluginBase::mouse_move_event_noref, p, buttons, prio); } else { return lay::ViewService::mouse_move_event (p, buttons, prio); } } + // NOTE: this version doesn't take a point reference which allows up to store the point + bool mouse_move_event_noref (db::DPoint p, unsigned int buttons, bool prio) + { + return mouse_move_event (p, buttons, prio); + } + virtual bool mouse_release_event (const db::DPoint &p, unsigned int buttons, bool prio) { if (f_mouse_release_event.can_issue ()) { - return f_mouse_release_event.issue (&lay::ViewService::mouse_release_event, p, buttons, prio); + return f_mouse_release_event.issue (&PluginBase::mouse_release_event_noref, p, buttons, prio); } else { return lay::ViewService::mouse_release_event (p, buttons, prio); } } + // NOTE: this version doesn't take a point reference which allows up to store the point + bool mouse_release_event_noref (db::DPoint p, unsigned int buttons, bool prio) + { + return mouse_release_event (p, buttons, prio); + } + virtual bool wheel_event (int delta, bool horizontal, const db::DPoint &p, unsigned int buttons, bool prio) { if (f_wheel_event.can_issue ()) { - return f_wheel_event.issue (&lay::ViewService::wheel_event, delta, horizontal, p, buttons, prio); + return f_wheel_event.issue (&PluginBase::wheel_event_noref, delta, horizontal, p, buttons, prio); } else { return lay::ViewService::wheel_event (delta, horizontal, p, buttons, prio); } } + // NOTE: this version doesn't take a point reference which allows up to store the point + bool wheel_event_noref (int delta, bool horizontal, db::DPoint p, unsigned int buttons, bool prio) + { + return wheel_event (delta, horizontal, p, buttons, prio); + } + virtual void activated () { if (f_activated.can_issue ()) { @@ -693,7 +729,7 @@ Class decl_Plugin ("lay", "Plugin", "@param buttons A combination of the constants in the \\ButtonState class which codes both the mouse buttons and the key modifiers (.e. ShiftButton etc).\n" "@return True to terminate dispatcher\n" ) + - callback ("mouse_button_pressed_event", &gsi::PluginBase::mouse_press_event, &gsi::PluginBase::f_mouse_press_event, gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"), + callback ("mouse_button_pressed_event", &gsi::PluginBase::mouse_press_event_noref, &gsi::PluginBase::f_mouse_press_event, gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"), "@brief Handles the mouse button pressed event\n" "This method will called by the view when a button is pressed on the mouse.\n" "\n" @@ -715,11 +751,11 @@ Class decl_Plugin ("lay", "Plugin", "@param buttons A combination of the constants in the \\ButtonState class which codes both the mouse buttons and the key modifiers (.e. LeftButton, ShiftButton etc).\n" "@return True to terminate dispatcher\n" ) + - callback ("mouse_click_event", &gsi::PluginBase::mouse_click_event, &gsi::PluginBase::f_mouse_click_event, gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"), + callback ("mouse_click_event", &gsi::PluginBase::mouse_click_event_noref, &gsi::PluginBase::f_mouse_click_event, gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"), "@brief Handles the mouse button click event (after the button has been released)\n" "The behaviour of this callback is the same than for \\mouse_press_event, except that it is called when the mouse button has been released without moving it.\n" ) + - callback ("mouse_double_click_event", &gsi::PluginBase::mouse_double_click_event, &gsi::PluginBase::f_mouse_double_click_event, gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"), + callback ("mouse_double_click_event", &gsi::PluginBase::mouse_double_click_event_noref, &gsi::PluginBase::f_mouse_double_click_event, gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"), "@brief Handles the mouse button double-click event\n" "The behaviour of this callback is the same than for \\mouse_press_event, except that it is called when the mouse button has been double-clicked.\n" ) + @@ -733,15 +769,15 @@ Class decl_Plugin ("lay", "Plugin", "The behaviour of this callback is the same than for \\mouse_press_event, except that it is called when the mouse enters the canvas area.\n" "This method does not have a position nor button flags.\n" ) + - callback ("mouse_moved_event", &gsi::PluginBase::mouse_move_event, &gsi::PluginBase::f_mouse_move_event, gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"), + callback ("mouse_moved_event", &gsi::PluginBase::mouse_move_event_noref, &gsi::PluginBase::f_mouse_move_event, gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"), "@brief Handles the mouse move event\n" "The behaviour of this callback is the same than for \\mouse_press_event, except that it is called when the mouse is moved in the canvas area.\n" ) + - callback ("mouse_button_released_event", &gsi::PluginBase::mouse_release_event, &gsi::PluginBase::f_mouse_release_event, gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"), + callback ("mouse_button_released_event", &gsi::PluginBase::mouse_release_event_noref, &gsi::PluginBase::f_mouse_release_event, gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"), "@brief Handles the mouse button release event\n" "The behaviour of this callback is the same than for \\mouse_press_event, except that it is called when the mouse button is released.\n" ) + - callback ("wheel_event", &gsi::PluginBase::wheel_event, &gsi::PluginBase::f_wheel_event, gsi::arg ("delta"), gsi::arg ("horizontal"), gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"), + callback ("wheel_event", &gsi::PluginBase::wheel_event_noref, &gsi::PluginBase::f_wheel_event, gsi::arg ("delta"), gsi::arg ("horizontal"), gsi::arg ("p"), gsi::arg ("buttons"), gsi::arg ("prio"), "The behaviour of this callback is the same than for \\mouse_press_event, except that it is called when the mouse wheel is rotated.\n" "Additional parameters for this event are 'delta' (the rotation angle in units of 1/8th degree) and 'horizontal' which is true when the horizontal wheel was rotated and " "false if the vertical wheel was rotated.\n" diff --git a/src/laybasic/laybasic/layAbstractMenu.cc b/src/laybasic/laybasic/layAbstractMenu.cc index f921eb266..680b782b6 100644 --- a/src/laybasic/laybasic/layAbstractMenu.cc +++ b/src/laybasic/laybasic/layAbstractMenu.cc @@ -1677,4 +1677,26 @@ AbstractMenu::collect_configure_actions (std::vector &ca } } +void +AbstractMenu::get_shortcuts (const std::string &root, std::map &bindings, bool with_defaults) +{ + std::vector items = this->items (root); + for (std::vector::const_iterator i = items.begin (); i != items.end (); ++i) { + if (i->size () > 0) { + if (is_valid (*i) && action (*i)->is_visible ()) { + if (is_menu (*i)) { + // a menu must be listed (so it can be hidden), but does not have a shortcut + // but we don't include special menus + if (i->at (0) != '@') { + bindings.insert (std::make_pair (*i, std::string ())); + } + get_shortcuts (*i, bindings, with_defaults); + } else if (! is_separator (*i)) { + bindings.insert (std::make_pair (*i, with_defaults ? action (*i)->get_default_shortcut () : action (*i)->get_effective_shortcut ())); + } + } + } + } +} + } diff --git a/src/laybasic/laybasic/layAbstractMenu.h b/src/laybasic/laybasic/layAbstractMenu.h index 9ac8caeb7..96ee27294 100644 --- a/src/laybasic/laybasic/layAbstractMenu.h +++ b/src/laybasic/laybasic/layAbstractMenu.h @@ -773,6 +773,18 @@ public: */ QActionGroup *make_exclusive_group (const std::string &name); + /** + * @brief Gets the keyboard shortcuts + * @param with_defaults Returns the default shortcuts if true. Otherwise returns the effective shortcut. + * @return a hash with menu paths for keys and key binding for values + */ + std::map get_shortcuts (bool with_defaults) + { + std::map b; + get_shortcuts (std::string (), b, with_defaults); + return b; + } + /** * @brief Gets the root node of the menu */ @@ -798,6 +810,7 @@ private: void collect_group (std::vector &grp, const std::string &name, const AbstractMenuItem &item) const; void collect_configure_actions (std::vector &ca, AbstractMenuItem &item); void emit_changed (); + void get_shortcuts (const std::string &root, std::map &bindings, bool with_defaults); Dispatcher *mp_dispatcher; AbstractMenuItem m_root; diff --git a/src/laybasic/laybasic/layDispatcher.cc b/src/laybasic/laybasic/layDispatcher.cc index 14d3e224f..92642df97 100644 --- a/src/laybasic/laybasic/layDispatcher.cc +++ b/src/laybasic/laybasic/layDispatcher.cc @@ -36,13 +36,26 @@ static Dispatcher *ms_dispatcher_instance = 0; Dispatcher::Dispatcher (Plugin *parent, bool standalone) : Plugin (parent, standalone), - m_menu (this) + m_menu (this), + mp_menu_parent_widget (0), + mp_delegate (0) { if (! parent && ! ms_dispatcher_instance) { ms_dispatcher_instance = this; } } +Dispatcher::Dispatcher (DispatcherDelegate *delegate, Plugin *parent, bool standalone) + : Plugin (parent, standalone), + m_menu (this), + mp_menu_parent_widget (0), + mp_delegate (delegate) +{ + if (! ms_dispatcher_instance) { + ms_dispatcher_instance = this; + } +} + Dispatcher::~Dispatcher () { if (ms_dispatcher_instance == this) { @@ -57,9 +70,23 @@ Dispatcher::configure (const std::string &name, const std::string &value) for (std::vector::const_iterator a = ca.begin (); a != ca.end (); ++a) { (*a)->configure (value); } - return false; + + if (mp_delegate) { + return mp_delegate->configure (name, value); + } else { + return false; + } } +void +Dispatcher::config_finalize () +{ + if (mp_delegate) { + return mp_delegate->config_finalize (); + } +} + + // Writing and Reading of configuration struct ConfigGetAdaptor diff --git a/src/laybasic/laybasic/layDispatcher.h b/src/laybasic/laybasic/layDispatcher.h index a4b537b5b..e1bb70dd8 100644 --- a/src/laybasic/laybasic/layDispatcher.h +++ b/src/laybasic/laybasic/layDispatcher.h @@ -41,11 +41,69 @@ class AbstractMenu; class Action; class ConfigureAction; +/** + * @brief A delegate by which the dispatcher can submit notification events + */ +class LAYBASIC_PUBLIC DispatcherDelegate +{ +public: + /** + * @brief Notifies the plugin root that a new plugin class has been registered + * + * This method is called when a plugin is loaded dynamically during runtime. + */ + virtual void plugin_registered (lay::PluginDeclaration * /*cls*/) + { + // .. this implementation does nothing .. + } + + /** + * @brief Notifies the plugin root that a plugin class is about to be removed + */ + virtual void plugin_removed (lay::PluginDeclaration * /*cls*/) + { + // .. this implementation does nothing .. + } + + /** + * @brief Selects the given mode + * + * The implementation is supposed to select the given mode on all related plugins. + */ + virtual void select_mode (int /*mode*/) + { + // .. this implementation does nothing .. + } + + /** + * @brief Menu command handler + */ + virtual void menu_activated (const std::string & /*symbol*/) + { + // .. this implementation does nothing .. + } + + /** + * @brief Receives configuration events + */ + virtual bool configure (const std::string & /*name*/, const std::string & /*value*/) + { + return false; + } + + /** + * @brief Configuration finalization + */ + virtual void config_finalize () + { + // .. the default implementation does nothing .. + } +}; + /** * @brief The central menu event and configuration dispatcher class * * This class acts as the top level dispatcher for plugin events and the menu configuration. - * */ class LAYBASIC_PUBLIC Dispatcher : public Plugin @@ -57,7 +115,14 @@ public: * @param parent Usually 0, but a dispatcher may have parents. In this case, the dispatcher is not the actual dispatcher, but the real plugin chain's root is. * @param standalone The standalone flag passed to the plugin constructor. */ - Dispatcher (Plugin *parent, bool standalone = false); + Dispatcher (Plugin *parent = 0, bool standalone = false); + + /** + * @brief The root constructor + * + * @param delegate The notification receiver for dispatcher events + */ + Dispatcher (DispatcherDelegate *delegate, Plugin *parent = 0, bool standalone = false); /** * @brief Destructor @@ -95,29 +160,65 @@ public: * * This method is called when a plugin is loaded dynamically during runtime. */ - virtual void plugin_registered (lay::PluginDeclaration * /*cls*/) { } + virtual void plugin_registered (lay::PluginDeclaration *cls) + { + if (mp_delegate) { + mp_delegate->plugin_registered (cls); + } + } /** * @brief Notifies the plugin root that a plugin class is about to be removed */ - virtual void plugin_removed (lay::PluginDeclaration * /*cls*/) { } + virtual void plugin_removed (lay::PluginDeclaration *cls) + { + if (mp_delegate) { + mp_delegate->plugin_registered (cls); + } + } /** * @brief Selects the given mode * * The implementation is supposed to select the given mode on all related plugins. */ - virtual void select_mode (int /*mode*/) { } + virtual void select_mode (int mode) + { + if (mp_delegate) { + mp_delegate->select_mode (mode); + } + } + + /** + * @brief Called, when a menu item is selected + */ + virtual void menu_activated (const std::string &symbol) + { + if (mp_delegate) { + mp_delegate->menu_activated (symbol); + } + } /** * @brief Gets the parent widget */ - virtual QWidget *menu_parent_widget () { return 0; } + QWidget *menu_parent_widget () + { + return mp_menu_parent_widget; + } + + /** + * @brief Gets the parent widget + */ + void set_menu_parent_widget (QWidget *w) + { + mp_menu_parent_widget = w; + } /** * @brief Returns true, if the dispatcher supplies a user interface */ - virtual bool has_ui () { return menu_parent_widget () != 0; } + bool has_ui () { return menu_parent_widget () != 0; } /** * @brief Gets the AbstractMenu object @@ -132,12 +233,15 @@ public: protected: // capture the configuration events so we can change the value of the configuration actions virtual bool configure (const std::string &name, const std::string &value); + virtual void config_finalize (); private: Dispatcher (const Dispatcher &); Dispatcher &operator= (const Dispatcher &); lay::AbstractMenu m_menu; + QWidget *mp_menu_parent_widget; + DispatcherDelegate *mp_delegate; }; } diff --git a/src/laybasic/laybasic/layLayoutView.cc b/src/laybasic/laybasic/layLayoutView.cc index 5f3cbc636..e6e065511 100644 --- a/src/laybasic/laybasic/layLayoutView.cc +++ b/src/laybasic/laybasic/layLayoutView.cc @@ -261,6 +261,7 @@ LayoutView::LayoutView (db::Manager *manager, bool editable, lay::Plugin *plugin { // either it's us or the parent has a dispatcher tl_assert (dispatcher () != 0); + set_menu_parent_widget (this); // ensures the deferred method scheduler is present tl::DeferredMethodScheduler::instance (); @@ -712,11 +713,6 @@ LayoutView::~LayoutView () mp_bookmarks_view = 0; } -QWidget *LayoutView::menu_parent_widget () -{ - return this; -} - lay::EditorOptionsPages *LayoutView::editor_options_pages () { if (! mp_editor_options_frame) { diff --git a/src/laybasic/laybasic/layLayoutView.h b/src/laybasic/laybasic/layLayoutView.h index 7c14db0d5..b7e8b7838 100644 --- a/src/laybasic/laybasic/layLayoutView.h +++ b/src/laybasic/laybasic/layLayoutView.h @@ -2917,9 +2917,6 @@ private: std::list::iterator cellview_iter (int cv_index); std::list::const_iterator cellview_iter (int cv_index) const; - - // implementation of Dispatcher - virtual QWidget *menu_parent_widget (); }; } diff --git a/src/lvs/unit_tests/lvsSimpleTests.cc b/src/lvs/unit_tests/lvsSimpleTests.cc index 8aa2252fb..f9e07d3a2 100644 --- a/src/lvs/unit_tests/lvsSimpleTests.cc +++ b/src/lvs/unit_tests/lvsSimpleTests.cc @@ -193,3 +193,9 @@ TEST(22_split_gate) run_test (_this, "nand2_split_gate", "nand2_split_gate.oas"); } +// empty gds +TEST(23_issue709) +{ + run_test (_this, "empty_subcells", "empty_subcells.gds"); +} + diff --git a/testdata/gds/t166_au.gds.gz b/testdata/gds/t166_au.gds.gz index 7766ef28e..b250e67b3 100644 Binary files a/testdata/gds/t166_au.gds.gz and b/testdata/gds/t166_au.gds.gz differ diff --git a/testdata/lvs/empty_subcells.cir b/testdata/lvs/empty_subcells.cir new file mode 100644 index 000000000..8c16d33c2 --- /dev/null +++ b/testdata/lvs/empty_subcells.cir @@ -0,0 +1,25 @@ +* Extracted by KLayout + +* cell empty_subcells +.SUBCKT empty_subcells +* cell instance $1 r0 *1 0,0 +X$1 1 2 blockB +* cell instance $2 r0 *1 310,0 +X$2 1 2 blockA +.ENDS empty_subcells + +* cell blockB +* pin V +* pin W +.SUBCKT blockB 1 2 +* net 1 V +* net 2 W +.ENDS blockB + +* cell blockA +* pin A +* pin B +.SUBCKT blockA 1 2 +* net 1 A +* net 2 B +.ENDS blockA diff --git a/testdata/lvs/empty_subcells.gds b/testdata/lvs/empty_subcells.gds new file mode 100644 index 000000000..1db25bd36 Binary files /dev/null and b/testdata/lvs/empty_subcells.gds differ diff --git a/testdata/lvs/empty_subcells.lvs b/testdata/lvs/empty_subcells.lvs new file mode 100644 index 000000000..949f73f30 --- /dev/null +++ b/testdata/lvs/empty_subcells.lvs @@ -0,0 +1,21 @@ + +source($lvs_test_source) +report_lvs($lvs_test_target_lvsdb, true) +target_netlist($lvs_test_target_cir, write_spice, "Extracted by KLayout") + +schematic("empty_subcells_sch.cir") + +deep + +m1 = input(1, 0) +via = input(2, 0) +m2 = input(3, 0) +lab = labels(254,0) + +connect(m1, via) +connect(via, m2) +connect(m2, lab) + +netlist.make_top_level_pins +compare + diff --git a/testdata/lvs/empty_subcells.lvsdb b/testdata/lvs/empty_subcells.lvsdb new file mode 100644 index 000000000..81138e85d --- /dev/null +++ b/testdata/lvs/empty_subcells.lvsdb @@ -0,0 +1,167 @@ +#%lvsdb-klayout + +# Layout +layout( + top(empty_subcells) + unit(1) + + # Layer section + # This section lists the mask layers (drawing or derived) and their connections. + + # Mask layers + layer(l1 '1/0') + layer(l2 '2/0') + layer(l3 '3/0') + layer(l4 '254/0') + + # Mask layer connectivity + connect(l1 l1 l2) + connect(l2 l1 l2 l3) + connect(l3 l2 l3 l4) + connect(l4 l3) + + # Circuit section + # Circuits are the hierarchical building blocks of the netlist. + circuit(blockA + + # Circuit boundary + rect((100 10) (30 80)) + + # Nets with their geometries + net(1 name(A) + rect(l3 (100 60) (30 30)) + text(l4 A (-10 -10)) + ) + net(2 name(B) + rect(l3 (100 10) (30 30)) + text(l4 B (-10 -10)) + ) + + # Outgoing pins and their connections to nets + pin(1 name(A)) + pin(2 name(B)) + + ) + circuit(blockB + + # Circuit boundary + rect((100 10) (30 80)) + + # Nets with their geometries + net(1 name(V) + rect(l3 (100 60) (30 30)) + text(l4 V (-10 -10)) + ) + net(2 name(W) + rect(l3 (100 10) (30 30)) + text(l4 W (-10 -10)) + ) + + # Outgoing pins and their connections to nets + pin(1 name(V)) + pin(2 name(W)) + + ) + circuit(empty_subcells + + # Circuit boundary + rect((100 10) (340 80)) + + # Nets with their geometries + net(1 + rect(l1 (180 60) (90 30)) + rect(l2 (-160 -20) (90 10)) + rect(l2 (40 -10) (180 10)) + ) + net(2 + rect(l1 (180 10) (90 30)) + rect(l2 (-160 -20) (90 10)) + rect(l2 (40 -10) (180 10)) + ) + + # Subcircuits and their connections + circuit(1 blockB location(0 0) + pin(0 1) + pin(1 2) + ) + circuit(2 blockA location(310 0) + pin(0 1) + pin(1 2) + ) + + ) +) + +# Reference netlist +reference( + + # Circuit section + # Circuits are the hierarchical building blocks of the netlist. + circuit(BLOCKA + + # Nets + net(1 name(A)) + net(2 name(B)) + + # Outgoing pins and their connections to nets + pin(1 name(A)) + pin(2 name(B)) + + ) + circuit(BLOCKB + + # Nets + net(1 name(V)) + net(2 name(W)) + + # Outgoing pins and their connections to nets + pin(1 name(V)) + pin(2 name(W)) + + ) + circuit(EMPTY_SUBCELLS + + # Nets + net(1 name(E)) + net(2 name(F)) + + # Subcircuits and their connections + circuit(1 BLOCKA name('1') + pin(0 1) + pin(1 2) + ) + circuit(2 BLOCKB name('2') + pin(0 1) + pin(1 2) + ) + + ) +) + +# Cross reference +xref( + circuit(blockA BLOCKA match + xref( + net(1 1 match) + net(2 2 match) + pin(0 0 match) + pin(1 1 match) + ) + ) + circuit(blockB BLOCKB match + xref( + net(1 1 match) + net(2 2 match) + pin(0 0 match) + pin(1 1 match) + ) + ) + circuit(empty_subcells EMPTY_SUBCELLS match + xref( + net(1 1 warning) + net(2 2 warning) + circuit(2 1 match) + circuit(1 2 match) + ) + ) +) diff --git a/testdata/lvs/empty_subcells_sch.cir b/testdata/lvs/empty_subcells_sch.cir new file mode 100644 index 000000000..6e6cbceee --- /dev/null +++ b/testdata/lvs/empty_subcells_sch.cir @@ -0,0 +1,12 @@ + + +.subckt blockA a b +.ends + +.subckt blockB v w +.ends + +.subckt empty_subcells +X1 e f blockA +X2 e f blockB +.ends diff --git a/testdata/python/basic.py b/testdata/python/basic.py index f078d093f..97a2aed30 100644 --- a/testdata/python/basic.py +++ b/testdata/python/basic.py @@ -2841,21 +2841,32 @@ class BasicTest(unittest.TestCase): # binary strings - qba = pya.A.ia_cref_to_qba([ 17, 42, 0, 8 ]) - self.assertEqual(repr(qba), "b'\\x11*\\x00\\x08'") - self.assertEqual(pya.A.qba_to_ia(qba), [ 17, 42, 0, 8 ]) - self.assertEqual(pya.A.qba_cref_to_ia(qba), [ 17, 42, 0, 8 ]) - self.assertEqual(pya.A.qba_cptr_to_ia(qba), [ 17, 42, 0, 8 ]) - self.assertEqual(pya.A.qba_ref_to_ia(qba), [ 17, 42, 0, 8 ]) - self.assertEqual(pya.A.qba_ptr_to_ia(qba), [ 17, 42, 0, 8 ]) + if "ia_cref_to_qba" in pya.A.__dict__: - qba = pya.A.ia_cref_to_qba_cref([ 17, 42, 0, 8 ]) - self.assertEqual(repr(qba), "b'\\x11*\\x00\\x08'") + qba = pya.A.ia_cref_to_qba([ 17, 42, 0, 8 ]) + if sys.version_info < (3, 0): + self.assertEqual(repr(qba), "bytearray(b'\\x11*\\x00\\x08')") + else: + self.assertEqual(repr(qba), "b'\\x11*\\x00\\x08'") + self.assertEqual(pya.A.qba_to_ia(qba), [ 17, 42, 0, 8 ]) + self.assertEqual(pya.A.qba_cref_to_ia(qba), [ 17, 42, 0, 8 ]) + self.assertEqual(pya.A.qba_cptr_to_ia(qba), [ 17, 42, 0, 8 ]) + self.assertEqual(pya.A.qba_ref_to_ia(qba), [ 17, 42, 0, 8 ]) + self.assertEqual(pya.A.qba_ptr_to_ia(qba), [ 17, 42, 0, 8 ]) + + qba = pya.A.ia_cref_to_qba_cref([ 17, 42, 0, 8 ]) + if sys.version_info < (3, 0): + self.assertEqual(repr(qba), "bytearray(b'\\x11*\\x00\\x08')") + else: + self.assertEqual(repr(qba), "b'\\x11*\\x00\\x08'") - self.assertEqual(pya.A.qba_to_ia(b'\x00\x01\x02'), [ 0, 1, 2 ]) + self.assertEqual(pya.A.qba_to_ia(b'\x00\x01\x02'), [ 0, 1, 2 ]) ba = pya.A.ia_cref_to_ba([ 17, 42, 0, 8 ]) - self.assertEqual(repr(ba), "b'\\x11*\\x00\\x08'") + if sys.version_info < (3, 0): + self.assertEqual(repr(ba), "bytearray(b'\\x11*\\x00\\x08')") + else: + self.assertEqual(repr(ba), "b'\\x11*\\x00\\x08'") self.assertEqual(pya.A.ba_to_ia(ba), [ 17, 42, 0, 8 ]) self.assertEqual(pya.A.ba_cref_to_ia(ba), [ 17, 42, 0, 8 ]) self.assertEqual(pya.A.ba_cptr_to_ia(ba), [ 17, 42, 0, 8 ]) @@ -2863,7 +2874,10 @@ class BasicTest(unittest.TestCase): self.assertEqual(pya.A.ba_ptr_to_ia(ba), [ 17, 42, 0, 8 ]) ba = pya.A.ia_cref_to_ba_cref([ 17, 42, 0, 8 ]) - self.assertEqual(repr(ba), "b'\\x11*\\x00\\x08'") + if sys.version_info < (3, 0): + self.assertEqual(repr(ba), "bytearray(b'\\x11*\\x00\\x08')") + else: + self.assertEqual(repr(ba), "b'\\x11*\\x00\\x08'") self.assertEqual(pya.A.ba_to_ia(b'\x00\x01\x02'), [ 0, 1, 2 ]) diff --git a/testdata/python/qtbinding.py b/testdata/python/qtbinding.py index 0a7cfcfc5..5a2acd1a0 100644 --- a/testdata/python/qtbinding.py +++ b/testdata/python/qtbinding.py @@ -487,12 +487,18 @@ class QtBindingTest(unittest.TestCase): slm = MyStandardItemModel() rn = slm.roleNames() - self.assertEqual(map2str(rn), "{0: b'display', 1: b'decoration', 2: b'edit', 3: b'toolTip', 4: b'statusTip', 5: b'whatsThis'}") + if sys.version_info < (3, 0): + self.assertEqual(map2str(rn), "{0: display, 1: decoration, 2: edit, 3: toolTip, 4: statusTip, 5: whatsThis}") + else: + self.assertEqual(map2str(rn), "{0: b'display', 1: b'decoration', 2: b'edit', 3: b'toolTip', 4: b'statusTip', 5: b'whatsThis'}") rnNew = slm.roleNames() rnNew[7] = "blabla" slm.srn(rnNew) rn = slm.roleNames() - self.assertEqual(map2str(rn), "{0: b'display', 1: b'decoration', 2: b'edit', 3: b'toolTip', 4: b'statusTip', 5: b'whatsThis', 7: b'blabla'}") + if sys.version_info < (3, 0): + self.assertEqual(map2str(rn), "{0: display, 1: decoration, 2: edit, 3: toolTip, 4: statusTip, 5: whatsThis, 7: blabla}") + else: + self.assertEqual(map2str(rn), "{0: b'display', 1: b'decoration', 2: b'edit', 3: b'toolTip', 4: b'statusTip', 5: b'whatsThis', 7: b'blabla'}") def test_44(self): diff --git a/testdata/ruby/basic_testcore.rb b/testdata/ruby/basic_testcore.rb index f800503d2..81011ed18 100644 --- a/testdata/ruby/basic_testcore.rb +++ b/testdata/ruby/basic_testcore.rb @@ -770,7 +770,10 @@ class Basic_TestClass < TestBase b.each_b_copy { |bb| arr.push(bb.str) } assert_equal(arr, ["a", "y", "uu"]) # through enumerator - assert_equal(b.each_b_copy.collect(&:str), ["a", "y", "uu"]) + if RUBY_VERSION > "2.0.0" + # this creates GC leaks in 2.0.0 + assert_equal(b.each_b_copy.collect(&:str), ["a", "y", "uu"]) + end arr = [] b.each_b_copy { |bb| bb.set_str(bb.str + "x"); arr.push(bb.str) } @@ -784,7 +787,10 @@ class Basic_TestClass < TestBase b.each_b_cref { |bb| arr.push(bb.str) } assert_equal(arr, ["a", "y", "uu"]) # through enumerator - assert_equal(b.each_b_cref.collect(&:str), ["a", "y", "uu"]) + if RUBY_VERSION > "2.0.0" + # this creates GC leaks in 2.0.0 + assert_equal(b.each_b_cref.collect(&:str), ["a", "y", "uu"]) + end arr = [] # this works, since the "const B &" will be converted to a copy @@ -800,7 +806,10 @@ class Basic_TestClass < TestBase b.each_b_cptr { |bb| arr.push(bb.str) } assert_equal(arr, ["a", "y", "uu"]) # through enumerator - assert_equal(b.each_b_cptr.collect(&:str), ["a", "y", "uu"]) + if RUBY_VERSION > "2.0.0" + # this creates GC leaks in 2.0.0 + assert_equal(b.each_b_cptr.collect(&:str), ["a", "y", "uu"]) + end arr = [] # const references cannot be modified @@ -821,7 +830,10 @@ class Basic_TestClass < TestBase b.each_b_ref { |bb| arr.push(bb.str) } assert_equal(arr, ["a", "y", "uu"]) # through enumerator - assert_equal(b.each_b_ref.collect(&:str), ["a", "y", "uu"]) + if RUBY_VERSION > "2.0.0" + # this creates GC leaks in 2.0.0 + assert_equal(b.each_b_ref.collect(&:str), ["a", "y", "uu"]) + end arr = [] b.each_b_ref { |bb| bb.set_str(bb.str + "x"); arr.push(bb.str) } @@ -835,7 +847,10 @@ class Basic_TestClass < TestBase b.each_b_ptr { |bb| arr.push(bb.str) } assert_equal(arr, ["ax", "yx", "uux"]) # through enumerator - assert_equal(b.each_b_ptr.collect(&:str), ["ax", "yx", "uux"]) + if RUBY_VERSION > "2.0.0" + # this creates GC leaks in 2.0.0 + assert_equal(b.each_b_ptr.collect(&:str), ["ax", "yx", "uux"]) + end arr = [] b.each_b_ptr { |bb| bb.set_str(bb.str + "x"); arr.push(bb.str) } @@ -1422,6 +1437,7 @@ class Basic_TestClass < TestBase # makes sure the objects inside the block before are deleted GC.start + GC.start # 2.0.0 needs a second one assert_equal(RBA::A.instance_count, ic0) assert_equal(RBA::A.a20_get == nil, true) diff --git a/testdata/ruby/layMenuTest.rb b/testdata/ruby/layMenuTest.rb index 2ffb383e2..97199ea78 100644 --- a/testdata/ruby/layMenuTest.rb +++ b/testdata/ruby/layMenuTest.rb @@ -383,6 +383,42 @@ RESULT end + def test_6 + + app = RBA::Application.instance + mw = app.main_window + + assert_equal(mw.get_key_bindings["file_menu.exit"], "Ctrl+Q") + + # key bindings + + mw.set_key_bindings({"file_menu.exit" => "F2"}) + assert_equal(mw.get_key_bindings["file_menu.exit"], "F2") + + mw.set_key_bindings({"file_menu.exit" => ""}) + assert_equal(mw.get_key_bindings["file_menu.exit"], "") + + mw.set_key_bindings(mw.get_default_key_bindings) + assert_equal(mw.get_key_bindings["file_menu.exit"], "Ctrl+Q") + + mw.set_key_bindings({"file_menu.exit" => ""}) + assert_equal(mw.get_key_bindings["file_menu.exit"], "") + + # menu items hidden + + assert_equal(mw.get_menu_items_hidden["file_menu.exit"], false) + + mw.set_menu_items_hidden({"file_menu.exit" => true}) + assert_equal(mw.get_menu_items_hidden["file_menu.exit"], true) + + mw.set_menu_items_hidden(mw.get_default_menu_items_hidden) + assert_equal(mw.get_menu_items_hidden["file_menu.exit"], false) + + mw.set_menu_items_hidden({"file_menu.exit" => true}) + assert_equal(mw.get_menu_items_hidden["file_menu.exit"], true) + + end + end load("test_epilogue.rb")