mirror of https://github.com/KLayout/klayout.git
commit
be1be467d4
|
|
@ -252,8 +252,8 @@ static void transform_deep_layer (db::DeepLayer &deep_layer, const Trans &t)
|
|||
|
||||
// Plain move
|
||||
|
||||
// build cell variants for different orientations
|
||||
db::OrientationReducer same_orientation;
|
||||
// build cell variants for different orientations and magnifications
|
||||
db::MagnificationAndOrientationReducer same_orientation;
|
||||
|
||||
db::VariantsCollectorBase vars (&same_orientation);
|
||||
vars.collect (deep_layer.layout (), deep_layer.initial_cell ());
|
||||
|
|
|
|||
|
|
@ -464,7 +464,7 @@ struct edge_defs
|
|||
"@return The scaled edge\n"
|
||||
) +
|
||||
method ("contains?", &C::contains, gsi::arg ("p"),
|
||||
"@brief Test whether a point is on an edge.\n"
|
||||
"@brief Tests whether a point is on an edge.\n"
|
||||
"\n"
|
||||
"A point is on a edge if it is on (or at least closer \n"
|
||||
"than a grid point to) the edge.\n"
|
||||
|
|
@ -474,7 +474,7 @@ struct edge_defs
|
|||
"@return True if the point is on the edge.\n"
|
||||
) +
|
||||
method ("contains_excl?", &C::contains_excl, gsi::arg ("p"),
|
||||
"@brief Test whether a point is on an edge excluding the endpoints.\n"
|
||||
"@brief Tests whether a point is on an edge excluding the endpoints.\n"
|
||||
"\n"
|
||||
"A point is on a edge if it is on (or at least closer \n"
|
||||
"than a grid point to) the edge.\n"
|
||||
|
|
@ -494,15 +494,18 @@ struct edge_defs
|
|||
"\n"
|
||||
"@return True if the edges are coincident.\n"
|
||||
) +
|
||||
method ("intersect?", &C::intersect, gsi::arg ("e"),
|
||||
method ("intersects?|#intersect?", &C::intersect, gsi::arg ("e"),
|
||||
"@brief Intersection test. \n"
|
||||
"\n"
|
||||
"Returns true if the edges intersect. Two edges intersect if they share at least one point. \n"
|
||||
"If the edges coincide, they also intersect.\n"
|
||||
"For degenerated edges, the intersection is mapped to\n"
|
||||
"point containment tests.\n"
|
||||
"If one of the edges is degenerate (both points are identical), that point is "
|
||||
"required to sit exaclty on the other edge. If both edges are degenerate, their "
|
||||
"points are required to be identical.\n"
|
||||
"\n"
|
||||
"@param e The edge to test.\n"
|
||||
"\n"
|
||||
"The 'intersects' (with an 's') synonym has been introduced in version 0.28.12.\n"
|
||||
) +
|
||||
method_ext ("intersection_point", &intersect_point, gsi::arg ("e"),
|
||||
"@brief Returns the intersection point of two edges. \n"
|
||||
|
|
@ -600,19 +603,21 @@ struct edge_defs
|
|||
"This method has been introduced in version 0.23.\n"
|
||||
) +
|
||||
method ("crossed_by?", &C::crossed_by, gsi::arg ("e"),
|
||||
"@brief Check, if an edge is cut by a line (given by an edge)\n"
|
||||
"@brief Checks, if the line given by self is crossed by the edge e\n"
|
||||
"\n"
|
||||
"This method returns true if p1 is in one semispace \n"
|
||||
"while p2 is in the other or one of them is on the line\n"
|
||||
"through the edge \"e\"\n"
|
||||
"self if considered an infinite line. This predicate renders true "
|
||||
"if the edge e is cut by this line. In other words: "
|
||||
"this method returns true if e.p1 is in one semispace of self \n"
|
||||
"while e.p2 is in the other or one of them is exactly on self.\n"
|
||||
"\n"
|
||||
"@param e The edge representing the line that the edge must be crossing.\n"
|
||||
) +
|
||||
method_ext ("crossing_point", &crossing_point, gsi::arg ("e"),
|
||||
"@brief Returns the crossing point on two edges. \n"
|
||||
"\n"
|
||||
"This method delivers the point where the given edge (self) crosses the line given "
|
||||
"by the edge in argument \"e\". If self does not cross this line, the result is undefined. "
|
||||
"This method delivers the point where the given line (self) crosses the edge given "
|
||||
"by the argument \"e\". self is considered infinitely long and is required to cut "
|
||||
"through the edge \"e\". If self does not cut this line, the result is undefined. "
|
||||
"See \\crossed_by? for a description of the crossing predicate.\n"
|
||||
"\n"
|
||||
"@param e The edge representing the line that self must be crossing.\n"
|
||||
|
|
|
|||
|
|
@ -425,7 +425,7 @@ Class<db::EdgePairs> decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs",
|
|||
"\n"
|
||||
"This method has been added in version 0.26."
|
||||
) +
|
||||
method ("+", &db::EdgePairs::operator+, gsi::arg ("other"),
|
||||
method ("+|join", &db::EdgePairs::operator+, gsi::arg ("other"),
|
||||
"@brief Returns the combined edge pair collection of self and the other one\n"
|
||||
"\n"
|
||||
"@return The resulting edge pair collection\n"
|
||||
|
|
@ -433,8 +433,9 @@ Class<db::EdgePairs> decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs",
|
|||
"This operator adds the edge pairs of the other collection to self and returns a new combined set.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.24.\n"
|
||||
) +
|
||||
method ("+=", &db::EdgePairs::operator+=, gsi::arg ("other"),
|
||||
"The 'join' alias has been introduced in version 0.28.12."
|
||||
) +
|
||||
method ("+=|join_with", &db::EdgePairs::operator+=, gsi::arg ("other"),
|
||||
"@brief Adds the edge pairs of the other edge pair collection to self\n"
|
||||
"\n"
|
||||
"@return The edge pair collection after modification (self)\n"
|
||||
|
|
@ -442,7 +443,12 @@ Class<db::EdgePairs> decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs",
|
|||
"This operator adds the edge pairs of the other collection to self.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.24.\n"
|
||||
) +
|
||||
"\n"
|
||||
"Note that in Ruby, the '+=' operator actually does not exist, but is emulated by '+' followed by an assignment. "
|
||||
"This is less efficient than the in-place operation, so it is recommended to use 'join_with' instead.\n"
|
||||
"\n"
|
||||
"The 'join_with' alias has been introduced in version 0.28.12."
|
||||
) +
|
||||
method_ext ("move", &move_p, gsi::arg ("p"),
|
||||
"@brief Moves the edge pair collection\n"
|
||||
"\n"
|
||||
|
|
|
|||
|
|
@ -769,7 +769,7 @@ Class<db::Edges> decl_Edges (decl_dbShapeCollection, "db", "Edges",
|
|||
"Crossing edges are not merged.\n"
|
||||
"In contrast to \\merge, this method does not modify the edge collection but returns a merged copy.\n"
|
||||
) +
|
||||
method ("&", (db::Edges (db::Edges::*)(const db::Edges &) const) &db::Edges::operator&, gsi::arg ("other"),
|
||||
method ("&|and", (db::Edges (db::Edges::*)(const db::Edges &) const) &db::Edges::operator&, gsi::arg ("other"),
|
||||
"@brief Returns the boolean AND between self and the other edge collection\n"
|
||||
"\n"
|
||||
"@return The result of the boolean AND operation\n"
|
||||
|
|
@ -777,17 +777,24 @@ Class<db::Edges> decl_Edges (decl_dbShapeCollection, "db", "Edges",
|
|||
"The boolean AND operation will return all parts of the edges in this collection which "
|
||||
"are coincident with parts of the edges in the other collection."
|
||||
"The result will be a merged edge collection.\n"
|
||||
) +
|
||||
method ("&=", (db::Edges &(db::Edges::*)(const db::Edges &)) &db::Edges::operator&=, gsi::arg ("other"),
|
||||
"@brief Performs the boolean AND between self and the other edge collection\n"
|
||||
"\n"
|
||||
"The 'and' alias has been introduced in version 0.28.12."
|
||||
) +
|
||||
method ("&=|and_with", (db::Edges &(db::Edges::*)(const db::Edges &)) &db::Edges::operator&=, gsi::arg ("other"),
|
||||
"@brief Performs the boolean AND between self and the other edge collection in-place (modifying self)\n"
|
||||
"\n"
|
||||
"@return The edge collection after modification (self)\n"
|
||||
"\n"
|
||||
"The boolean AND operation will return all parts of the edges in this collection which "
|
||||
"are coincident with parts of the edges in the other collection."
|
||||
"The result will be a merged edge collection.\n"
|
||||
) +
|
||||
method ("&", (db::Edges (db::Edges::*)(const db::Region &) const) &db::Edges::operator&, gsi::arg ("other"),
|
||||
"\n"
|
||||
"Note that in Ruby, the '&=' operator actually does not exist, but is emulated by '&' followed by an assignment. "
|
||||
"This is less efficient than the in-place operation, so it is recommended to use 'and_with' instead.\n"
|
||||
"\n"
|
||||
"The 'and_with' alias has been introduced in version 0.28.12."
|
||||
) +
|
||||
method ("&|and", (db::Edges (db::Edges::*)(const db::Region &) const) &db::Edges::operator&, gsi::arg ("other"),
|
||||
"@brief Returns the parts of the edges inside the given region\n"
|
||||
"\n"
|
||||
"@return The edges inside the given region\n"
|
||||
|
|
@ -798,9 +805,10 @@ Class<db::Edges> decl_Edges (decl_dbShapeCollection, "db", "Edges",
|
|||
"edges intersect.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.24."
|
||||
) +
|
||||
method ("&=", (db::Edges &(db::Edges::*)(const db::Region &)) &db::Edges::operator&=, gsi::arg ("other"),
|
||||
"@brief Selects the parts of the edges inside the given region\n"
|
||||
"The 'and' alias has been introduced in version 0.28.12."
|
||||
) +
|
||||
method ("&=|and_with", (db::Edges &(db::Edges::*)(const db::Region &)) &db::Edges::operator&=, gsi::arg ("other"),
|
||||
"@brief Selects the parts of the edges inside the given region in-place (modifying self)\n"
|
||||
"\n"
|
||||
"@return The edge collection after modification (self)\n"
|
||||
"\n"
|
||||
|
|
@ -810,8 +818,13 @@ Class<db::Edges> decl_Edges (decl_dbShapeCollection, "db", "Edges",
|
|||
"edges intersect.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.24."
|
||||
) +
|
||||
method ("-", (db::Edges (db::Edges::*)(const db::Edges &) const) &db::Edges::operator-, gsi::arg ("other"),
|
||||
"\n"
|
||||
"Note that in Ruby, the '&=' operator actually does not exist, but is emulated by '&' followed by an assignment. "
|
||||
"This is less efficient than the in-place operation, so it is recommended to use 'and_with' instead.\n"
|
||||
"\n"
|
||||
"The 'and_with' alias has been introduced in version 0.28.12."
|
||||
) +
|
||||
method ("-|not", (db::Edges (db::Edges::*)(const db::Edges &) const) &db::Edges::operator-, gsi::arg ("other"),
|
||||
"@brief Returns the boolean NOT between self and the other edge collection\n"
|
||||
"\n"
|
||||
"@return The result of the boolean NOT operation\n"
|
||||
|
|
@ -819,17 +832,24 @@ Class<db::Edges> decl_Edges (decl_dbShapeCollection, "db", "Edges",
|
|||
"The boolean NOT operation will return all parts of the edges in this collection which "
|
||||
"are not coincident with parts of the edges in the other collection."
|
||||
"The result will be a merged edge collection.\n"
|
||||
) +
|
||||
method ("-=", (db::Edges &(db::Edges::*)(const db::Edges &)) &db::Edges::operator-=, gsi::arg ("other"),
|
||||
"@brief Performs the boolean NOT between self and the other edge collection\n"
|
||||
"\n"
|
||||
"The 'not' alias has been introduced in version 0.28.12."
|
||||
) +
|
||||
method ("-=|not_with", (db::Edges &(db::Edges::*)(const db::Edges &)) &db::Edges::operator-=, gsi::arg ("other"),
|
||||
"@brief Performs the boolean NOT between self and the other edge collection in-place (modifying self)\n"
|
||||
"\n"
|
||||
"@return The edge collection after modification (self)\n"
|
||||
"\n"
|
||||
"The boolean NOT operation will return all parts of the edges in this collection which "
|
||||
"are not coincident with parts of the edges in the other collection."
|
||||
"The result will be a merged edge collection.\n"
|
||||
) +
|
||||
method ("-", (db::Edges (db::Edges::*)(const db::Region &) const) &db::Edges::operator-, gsi::arg ("other"),
|
||||
"\n"
|
||||
"Note that in Ruby, the '-=' operator actually does not exist, but is emulated by '-' followed by an assignment. "
|
||||
"This is less efficient than the in-place operation, so it is recommended to use 'not_with' instead.\n"
|
||||
"\n"
|
||||
"The 'not_with' alias has been introduced in version 0.28.12."
|
||||
) +
|
||||
method ("-|not", (db::Edges (db::Edges::*)(const db::Region &) const) &db::Edges::operator-, gsi::arg ("other"),
|
||||
"@brief Returns the parts of the edges outside the given region\n"
|
||||
"\n"
|
||||
"@return The edges outside the given region\n"
|
||||
|
|
@ -840,9 +860,10 @@ Class<db::Edges> decl_Edges (decl_dbShapeCollection, "db", "Edges",
|
|||
"edges intersect.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.24."
|
||||
) +
|
||||
method ("-=", (db::Edges &(db::Edges::*)(const db::Region &)) &db::Edges::operator-=, gsi::arg ("other"),
|
||||
"@brief Selects the parts of the edges outside the given region\n"
|
||||
"The 'not' alias has been introduced in version 0.28.12."
|
||||
) +
|
||||
method ("-=|not_with", (db::Edges &(db::Edges::*)(const db::Region &)) &db::Edges::operator-=, gsi::arg ("other"),
|
||||
"@brief Selects the parts of the edges outside the given region in-place (modifying self)\n"
|
||||
"\n"
|
||||
"@return The edge collection after modification (self)\n"
|
||||
"\n"
|
||||
|
|
@ -851,8 +872,12 @@ Class<db::Edges> decl_Edges (decl_dbShapeCollection, "db", "Edges",
|
|||
"As a side effect, the edges are made non-intersecting by introducing cut points where\n"
|
||||
"edges intersect.\n"
|
||||
"\n"
|
||||
"Note that in Ruby, the '-=' operator actually does not exist, but is emulated by '-' followed by an assignment. "
|
||||
"This is less efficient than the in-place operation, so it is recommended to use 'not_with' instead.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.24."
|
||||
) +
|
||||
"The 'not_with' alias has been introduced in version 0.28.12."
|
||||
) +
|
||||
method_ext ("andnot", &andnot_with_edges, gsi::arg ("other"),
|
||||
"@brief Returns the boolean AND and NOT between self and the other edge set\n"
|
||||
"\n"
|
||||
|
|
@ -873,7 +898,7 @@ Class<db::Edges> decl_Edges (decl_dbShapeCollection, "db", "Edges",
|
|||
"\n"
|
||||
"This method has been added in version 0.28.\n"
|
||||
) +
|
||||
method ("^", &db::Edges::operator^, gsi::arg ("other"),
|
||||
method ("^|xor", &db::Edges::operator^, gsi::arg ("other"),
|
||||
"@brief Returns the boolean XOR between self and the other edge collection\n"
|
||||
"\n"
|
||||
"@return The result of the boolean XOR operation\n"
|
||||
|
|
@ -881,48 +906,69 @@ Class<db::Edges> decl_Edges (decl_dbShapeCollection, "db", "Edges",
|
|||
"The boolean XOR operation will return all parts of the edges in this and the other collection except "
|
||||
"the parts where both are coincident.\n"
|
||||
"The result will be a merged edge collection.\n"
|
||||
) +
|
||||
method ("^=", &db::Edges::operator^=, gsi::arg ("other"),
|
||||
"@brief Performs the boolean XOR between self and the other edge collection\n"
|
||||
"\n"
|
||||
"The 'xor' alias has been introduced in version 0.28.12."
|
||||
) +
|
||||
method ("^=|xor_with", &db::Edges::operator^=, gsi::arg ("other"),
|
||||
"@brief Performs the boolean XOR between self and the other edge collection in-place (modifying self)\n"
|
||||
"\n"
|
||||
"@return The edge collection after modification (self)\n"
|
||||
"\n"
|
||||
"The boolean XOR operation will return all parts of the edges in this and the other collection except "
|
||||
"the parts where both are coincident.\n"
|
||||
"The result will be a merged edge collection.\n"
|
||||
) +
|
||||
method ("\\|", &db::Edges::operator|, gsi::arg ("other"),
|
||||
"\n"
|
||||
"Note that in Ruby, the '^=' operator actually does not exist, but is emulated by '^' followed by an assignment. "
|
||||
"This is less efficient than the in-place operation, so it is recommended to use 'xor_with' instead.\n"
|
||||
"\n"
|
||||
"The 'xor_with' alias has been introduced in version 0.28.12."
|
||||
) +
|
||||
method ("\\||or", &db::Edges::operator|, gsi::arg ("other"),
|
||||
"@brief Returns the boolean OR between self and the other edge set\n"
|
||||
"\n"
|
||||
"@return The resulting edge collection\n"
|
||||
"\n"
|
||||
"The boolean OR is implemented by merging the edges of both edge sets. To simply join the edge collections "
|
||||
"without merging, the + operator is more efficient."
|
||||
) +
|
||||
method ("\\|=", &db::Edges::operator|=, gsi::arg ("other"),
|
||||
"@brief Performs the boolean OR between self and the other edge set\n"
|
||||
"\n"
|
||||
"The 'or' alias has been introduced in version 0.28.12."
|
||||
) +
|
||||
method ("\\|=|or_with", &db::Edges::operator|=, gsi::arg ("other"),
|
||||
"@brief Performs the boolean OR between self and the other edge set in-place (modifying self)\n"
|
||||
"\n"
|
||||
"@return The edge collection after modification (self)\n"
|
||||
"\n"
|
||||
"The boolean OR is implemented by merging the edges of both edge sets. To simply join the edge collections "
|
||||
"without merging, the + operator is more efficient."
|
||||
) +
|
||||
method ("+", &db::Edges::operator+, gsi::arg ("other"),
|
||||
"\n"
|
||||
"Note that in Ruby, the '|=' operator actually does not exist, but is emulated by '|' followed by an assignment. "
|
||||
"This is less efficient than the in-place operation, so it is recommended to use 'or_with' instead.\n"
|
||||
"\n"
|
||||
"The 'or_with' alias has been introduced in version 0.28.12."
|
||||
) +
|
||||
method ("+|join", &db::Edges::operator+, gsi::arg ("other"),
|
||||
"@brief Returns the combined edge set of self and the other one\n"
|
||||
"\n"
|
||||
"@return The resulting edge set\n"
|
||||
"\n"
|
||||
"This operator adds the edges of the other edge set to self and returns a new combined edge set. "
|
||||
"This usually creates unmerged edge sets and edges may overlap. Use \\merge if you want to ensure the result edge set is merged.\n"
|
||||
) +
|
||||
method ("+=", &db::Edges::operator+=, gsi::arg ("other"),
|
||||
"\n"
|
||||
"The 'join' alias has been introduced in version 0.28.12."
|
||||
) +
|
||||
method ("+=|join_with", &db::Edges::operator+=, gsi::arg ("other"),
|
||||
"@brief Adds the edges of the other edge collection to self\n"
|
||||
"\n"
|
||||
"@return The edge set after modification (self)\n"
|
||||
"\n"
|
||||
"This operator adds the edges of the other edge set to self. "
|
||||
"This usually creates unmerged edge sets and edges may overlap. Use \\merge if you want to ensure the result edge set is merged.\n"
|
||||
) +
|
||||
"\n"
|
||||
"Note that in Ruby, the '+=' operator actually does not exist, but is emulated by '+' followed by an assignment. "
|
||||
"This is less efficient than the in-place operation, so it is recommended to use 'join_with' instead.\n"
|
||||
"\n"
|
||||
"The 'join_with' alias has been introduced in version 0.28.12."
|
||||
) +
|
||||
method ("interacting", (db::Edges (db::Edges::*) (const db::Edges &) const) &db::Edges::selected_interacting, gsi::arg ("other"),
|
||||
"@brief Returns the edges of this edge collection which overlap or touch edges from the other edge collection\n"
|
||||
"\n"
|
||||
|
|
|
|||
|
|
@ -1037,6 +1037,22 @@ static void dtransform_cplx (db::Layout *layout, const db::DCplxTrans &trans)
|
|||
layout->transform (dbu_trans.inverted () * trans * dbu_trans);
|
||||
}
|
||||
|
||||
static db::LayerProperties get_properties (const db::Layout *layout, unsigned int index)
|
||||
{
|
||||
if (layout->is_valid_layer (index)) {
|
||||
return layout->get_properties (index);
|
||||
} else {
|
||||
return db::LayerProperties ();
|
||||
}
|
||||
}
|
||||
|
||||
static void set_properties (db::Layout *layout, unsigned int index, const db::LayerProperties &props)
|
||||
{
|
||||
if (layout->is_valid_layer (index)) {
|
||||
layout->set_properties (index, props);
|
||||
}
|
||||
}
|
||||
|
||||
Class<db::Layout> decl_Layout ("db", "Layout",
|
||||
gsi::constructor ("new", &layout_ctor_with_manager, gsi::arg ("manager"),
|
||||
"@brief Creates a layout object attached to a manager\n"
|
||||
|
|
@ -1667,11 +1683,12 @@ Class<db::Layout> decl_Layout ("db", "Layout",
|
|||
"\n"
|
||||
"See \\insert_special_layer for a description of special layers."
|
||||
) +
|
||||
gsi::method ("set_info", &db::Layout::set_properties, gsi::arg ("index"), gsi::arg ("props"),
|
||||
gsi::method_ext ("set_info", &set_properties, gsi::arg ("index"), gsi::arg ("props"),
|
||||
"@brief Sets the info structure for a specified layer\n"
|
||||
) +
|
||||
gsi::method ("get_info", &db::Layout::get_properties, gsi::arg ("index"),
|
||||
gsi::method_ext ("get_info", &get_properties, gsi::arg ("index"),
|
||||
"@brief Gets the info structure for a specified layer\n"
|
||||
"If the layer index is not a valid layer index, an empty LayerProperties object will be returned."
|
||||
) +
|
||||
gsi::method ("cells", &db::Layout::cells,
|
||||
"@brief Returns the number of cells\n"
|
||||
|
|
|
|||
|
|
@ -1647,15 +1647,18 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"This variant has been introduced in version 0.28.4."
|
||||
) +
|
||||
method ("&=", &db::Region::operator&=, gsi::arg ("other"),
|
||||
"@brief Performs the boolean AND between self and the other region\n"
|
||||
"@brief Performs the boolean AND between self and the other region in-place (modifying self)\n"
|
||||
"\n"
|
||||
"@return The region after modification (self)\n"
|
||||
"\n"
|
||||
"This method will compute the boolean AND (intersection) between two regions. "
|
||||
"The result is often but not necessarily always merged.\n"
|
||||
"\n"
|
||||
"Note that in Ruby, the '&=' operator actually does not exist, but is emulated by '&' followed by an assignment. "
|
||||
"This is less efficient than the in-place operation, so it is recommended to use 'and_with' instead."
|
||||
) +
|
||||
method ("and_with", &db::Region::bool_and_with, gsi::arg ("other"), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"),
|
||||
"@brief Performs the boolean AND between self and the other region\n"
|
||||
"@brief Performs the boolean AND between self and the other region in-place (modifying self)\n"
|
||||
"\n"
|
||||
"@return The region after modification (self)\n"
|
||||
"\n"
|
||||
|
|
@ -1687,15 +1690,18 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"This variant has been introduced in version 0.28.4."
|
||||
) +
|
||||
method ("-=", &db::Region::operator-=, gsi::arg ("other"),
|
||||
"@brief Performs the boolean NOT between self and the other region\n"
|
||||
"@brief Performs the boolean NOT between self and the other region in-place (modifying self)\n"
|
||||
"\n"
|
||||
"@return The region after modification (self)\n"
|
||||
"\n"
|
||||
"This method will compute the boolean NOT (intersection) between two regions. "
|
||||
"The result is often but not necessarily always merged.\n"
|
||||
"\n"
|
||||
"Note that in Ruby, the '-=' operator actually does not exist, but is emulated by '-' followed by an assignment. "
|
||||
"This is less efficient than the in-place operation, so it is recommended to use 'not_with' instead."
|
||||
) +
|
||||
method ("not_with", &db::Region::bool_not_with, gsi::arg ("other"), gsi::arg ("property_constraint", db::IgnoreProperties, "IgnoreProperties"),
|
||||
"@brief Performs the boolean NOT between self and the other region\n"
|
||||
"@brief Performs the boolean NOT between self and the other region in-place (modifying self)\n"
|
||||
"\n"
|
||||
"@return The region after modification (self)\n"
|
||||
"\n"
|
||||
|
|
@ -1706,53 +1712,74 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
|||
"\n"
|
||||
"This variant has been introduced in version 0.28.4."
|
||||
) +
|
||||
method ("^", &db::Region::operator^, gsi::arg ("other"),
|
||||
method ("^|xor", &db::Region::operator^, gsi::arg ("other"),
|
||||
"@brief Returns the boolean XOR between self and the other region\n"
|
||||
"\n"
|
||||
"@return The result of the boolean XOR operation\n"
|
||||
"\n"
|
||||
"This method will compute the boolean XOR (intersection) between two regions. "
|
||||
"The result is often but not necessarily always merged.\n"
|
||||
"\n"
|
||||
"The 'xor' alias has been introduced in version 0.28.12."
|
||||
) +
|
||||
method ("^=", &db::Region::operator^=, gsi::arg ("other"),
|
||||
"@brief Performs the boolean XOR between self and the other region\n"
|
||||
method ("^=|xor_with", &db::Region::operator^=, gsi::arg ("other"),
|
||||
"@brief Performs the boolean XOR between self and the other region in-place (modifying self)\n"
|
||||
"\n"
|
||||
"@return The region after modification (self)\n"
|
||||
"\n"
|
||||
"This method will compute the boolean XOR (intersection) between two regions. "
|
||||
"The result is often but not necessarily always merged.\n"
|
||||
"\n"
|
||||
"Note that in Ruby, the '^=' operator actually does not exist, but is emulated by '^' followed by an assignment. "
|
||||
"This is less efficient than the in-place operation, so it is recommended to use 'xor_with' instead.\n"
|
||||
"\n"
|
||||
"The 'xor_with' alias has been introduced in version 0.28.12."
|
||||
) +
|
||||
method ("\\|", &db::Region::operator|, gsi::arg ("other"),
|
||||
method ("\\||or", &db::Region::operator|, gsi::arg ("other"),
|
||||
"@brief Returns the boolean OR between self and the other region\n"
|
||||
"\n"
|
||||
"@return The resulting region\n"
|
||||
"\n"
|
||||
"The boolean OR is implemented by merging the polygons of both regions. To simply join the regions "
|
||||
"without merging, the + operator is more efficient."
|
||||
"\n"
|
||||
"The 'or' alias has been introduced in version 0.28.12."
|
||||
) +
|
||||
method ("\\|=", &db::Region::operator|=, gsi::arg ("other"),
|
||||
"@brief Performs the boolean OR between self and the other region\n"
|
||||
method ("\\|=|or_with", &db::Region::operator|=, gsi::arg ("other"),
|
||||
"@brief Performs the boolean OR between self and the other region in-place (modifying self)\n"
|
||||
"\n"
|
||||
"@return The region after modification (self)\n"
|
||||
"\n"
|
||||
"The boolean OR is implemented by merging the polygons of both regions. To simply join the regions "
|
||||
"without merging, the + operator is more efficient."
|
||||
"\n"
|
||||
"Note that in Ruby, the '|=' operator actually does not exist, but is emulated by '|' followed by an assignment. "
|
||||
"This is less efficient than the in-place operation, so it is recommended to use 'or_with' instead.\n"
|
||||
"\n"
|
||||
"The 'or_with' alias has been introduced in version 0.28.12."
|
||||
) +
|
||||
method ("+", &db::Region::operator+, gsi::arg ("other"),
|
||||
method ("+|join", &db::Region::operator+, gsi::arg ("other"),
|
||||
"@brief Returns the combined region of self and the other region\n"
|
||||
"\n"
|
||||
"@return The resulting region\n"
|
||||
"\n"
|
||||
"This operator adds the polygons of the other region to self and returns a new combined region. "
|
||||
"This usually creates unmerged regions and polygons may overlap. Use \\merge if you want to ensure the result region is merged.\n"
|
||||
"\n"
|
||||
"The 'join' alias has been introduced in version 0.28.12."
|
||||
) +
|
||||
method ("+=", &db::Region::operator+=, gsi::arg ("other"),
|
||||
method ("+=|join_with", &db::Region::operator+=, gsi::arg ("other"),
|
||||
"@brief Adds the polygons of the other region to self\n"
|
||||
"\n"
|
||||
"@return The region after modification (self)\n"
|
||||
"\n"
|
||||
"This operator adds the polygons of the other region to self. "
|
||||
"This usually creates unmerged regions and polygons may overlap. Use \\merge if you want to ensure the result region is merged.\n"
|
||||
"\n"
|
||||
"Note that in Ruby, the '+=' operator actually does not exist, but is emulated by '+' followed by an assignment. "
|
||||
"This is less efficient than the in-place operation, so it is recommended to use 'join_with' instead.\n"
|
||||
"\n"
|
||||
"The 'join_with' alias has been introduced in version 0.28.12."
|
||||
) +
|
||||
method ("covering", &db::Region::selected_enclosing, gsi::arg ("other"), gsi::arg ("min_count", size_t (1)), gsi::arg ("max_count", size_t (std::numeric_limits<size_t>::max ()), "unlimited"),
|
||||
"@brief Returns the polygons of this region which are completely covering polygons from the other region\n"
|
||||
|
|
|
|||
|
|
@ -276,20 +276,27 @@ Class<db::Texts> decl_Texts (decl_dbShapeCollection, "db", "Texts",
|
|||
method_ext ("data_id", &id,
|
||||
"@brief Returns the data ID (a unique identifier for the underlying data storage)\n"
|
||||
) +
|
||||
method ("+", &db::Texts::operator+, gsi::arg ("other"),
|
||||
method ("+|join", &db::Texts::operator+, gsi::arg ("other"),
|
||||
"@brief Returns the combined text collection of self and the other one\n"
|
||||
"\n"
|
||||
"@return The resulting text collection\n"
|
||||
"\n"
|
||||
"This operator adds the texts of the other collection to self and returns a new combined set.\n"
|
||||
) +
|
||||
method ("+=", &db::Texts::operator+=, gsi::arg ("other"),
|
||||
"\n"
|
||||
"The 'join' alias has been introduced in version 0.28.12."
|
||||
) +
|
||||
method ("+=|join_with", &db::Texts::operator+=, gsi::arg ("other"),
|
||||
"@brief Adds the texts of the other text collection to self\n"
|
||||
"\n"
|
||||
"@return The text collection after modification (self)\n"
|
||||
"\n"
|
||||
"This operator adds the texts of the other collection to self.\n"
|
||||
) +
|
||||
"\n"
|
||||
"Note that in Ruby, the '+=' operator actually does not exist, but is emulated by '+' followed by an assignment. "
|
||||
"This is less efficient than the in-place operation, so it is recommended to use 'join_with' instead.\n"
|
||||
"\n"
|
||||
"The 'join_with' alias has been introduced in version 0.28.12."
|
||||
) +
|
||||
method_ext ("move", &move_p, gsi::arg ("p"),
|
||||
"@brief Moves the text collection\n"
|
||||
"\n"
|
||||
|
|
|
|||
|
|
@ -2779,6 +2779,37 @@ TEST(issue_400_with_region)
|
|||
db::compare_layouts (_this, ly, tl::testdata () + "/algo/deep_region_au400c.gds");
|
||||
}
|
||||
|
||||
TEST(deep_region_transform_with_scaled)
|
||||
{
|
||||
db::Layout ly;
|
||||
{
|
||||
std::string fn (tl::testdata ());
|
||||
fn += "/algo/deep_region_transform_with_scaled.gds";
|
||||
tl::InputStream stream (fn);
|
||||
db::Reader reader (stream);
|
||||
reader.read (ly);
|
||||
}
|
||||
|
||||
db::cell_index_type top_cell_index = *ly.begin_top_down ();
|
||||
db::Cell &top_cell = ly.cell (top_cell_index);
|
||||
|
||||
db::DeepShapeStore dss;
|
||||
|
||||
for (db::Layout::layer_iterator l = ly.begin_layers (); l != ly.end_layers (); ++l) {
|
||||
|
||||
unsigned int li = (*l).first;
|
||||
db::Region r (db::RecursiveShapeIterator (ly, top_cell, li), dss);
|
||||
r.transform (db::Trans (db::Vector (10000, 0)));
|
||||
|
||||
ly.clear_layer (li);
|
||||
r.insert_into (&ly, top_cell_index, li);
|
||||
|
||||
}
|
||||
|
||||
CHECKPOINT();
|
||||
db::compare_layouts (_this, ly, tl::testdata () + "/algo/deep_region_transform_with_scaled_au.gds");
|
||||
}
|
||||
|
||||
TEST(issue_663_separation_from_inside)
|
||||
{
|
||||
db::Layout ly;
|
||||
|
|
|
|||
|
|
@ -91,9 +91,10 @@ module DRC
|
|||
end
|
||||
|
||||
def finish
|
||||
@tmp_layers.each do |li|
|
||||
@layout.delete_layer(li)
|
||||
@tmp_layers.each do |layout,li|
|
||||
layout.delete_layer(li)
|
||||
end
|
||||
@tmp_layers = []
|
||||
end
|
||||
|
||||
def set_box(method, *args)
|
||||
|
|
@ -347,7 +348,7 @@ CODE
|
|||
|
||||
if cell_filter
|
||||
tmp = @layout_var.insert_layer(RBA::LayerInfo::new)
|
||||
@tmp_layers << tmp
|
||||
@tmp_layers << [ @layout_var, tmp ]
|
||||
@layout_var.cells(cell_filter).each do |cell|
|
||||
cell.shapes(tmp).insert(cell.bbox)
|
||||
end
|
||||
|
|
@ -606,7 +607,7 @@ CODE
|
|||
|
||||
li = @layout.insert_layer(RBA::LayerInfo::new)
|
||||
li && layers.push(li)
|
||||
li && @tmp_layers.push(li)
|
||||
li && @tmp_layers.push([ @layout, li ])
|
||||
|
||||
elsif (args.size == 1 && args[0].is_a?(RBA::LayerInfo))
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ module DRC
|
|||
|
||||
class DRCExecutable < RBA::Executable
|
||||
|
||||
def initialize(macro, generator, rdb_index = nil)
|
||||
def initialize(macro, interpreter, generator, rdb_index = nil)
|
||||
|
||||
@drc = DRCEngine::new
|
||||
@drc._rdb_index = rdb_index
|
||||
|
|
@ -27,6 +27,8 @@ module DRC
|
|||
|
||||
@macro = macro
|
||||
|
||||
@interpreter = interpreter
|
||||
|
||||
end
|
||||
|
||||
def execute
|
||||
|
|
@ -38,9 +40,11 @@ module DRC
|
|||
|
||||
begin
|
||||
|
||||
encoded_file, expanded_text = @interpreter.include_expansion(@macro)
|
||||
|
||||
# No verbosity set in drc engine - we cannot use the engine's logger
|
||||
RBA::Logger::verbosity >= 10 && RBA::Logger::info("Running #{@macro.path}")
|
||||
@drc.instance_eval(@macro.text, @macro.path)
|
||||
@drc.instance_eval(expanded_text, encoded_file)
|
||||
|
||||
rescue => ex
|
||||
|
||||
|
|
@ -66,13 +70,40 @@ module DRC
|
|||
|
||||
end
|
||||
|
||||
# A recipe implementation allowing the DRC run to be redone
|
||||
class DRCRecipe < RBA::Recipe
|
||||
|
||||
def initialize(interpreter)
|
||||
|
||||
super("drc", "DRC recipe")
|
||||
|
||||
@interpreter = interpreter
|
||||
|
||||
end
|
||||
|
||||
def executable(params)
|
||||
|
||||
script = params["script"]
|
||||
if ! script
|
||||
return
|
||||
end
|
||||
|
||||
macro = RBA::Macro::macro_by_path(script)
|
||||
macro || raise("Can't find DRC script #{script} - unable to re-run")
|
||||
|
||||
DRCExecutable::new(macro, @interpreter, self.generator("script" => script), params["rdb_index"])
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# A DSL implementation for a DRC language (XML format)
|
||||
class DRCInterpreter < RBA::MacroInterpreter
|
||||
|
||||
# Constructor
|
||||
def initialize(recipe)
|
||||
def initialize
|
||||
|
||||
@recipe = recipe
|
||||
@recipe = DRCRecipe::new(self)
|
||||
|
||||
# Make the DSL use ruby syntax highlighting
|
||||
self.syntax_scheme = "ruby"
|
||||
|
|
@ -97,7 +128,7 @@ module DRC
|
|||
|
||||
# Implements the execute method
|
||||
def executable(macro)
|
||||
DRCExecutable::new(macro, @recipe.generator("script" => macro.path))
|
||||
DRCExecutable::new(macro, self, @recipe.generator("script" => macro.path))
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -106,9 +137,9 @@ module DRC
|
|||
class DRCPlainTextInterpreter < RBA::MacroInterpreter
|
||||
|
||||
# Constructor
|
||||
def initialize(recipe)
|
||||
def initialize
|
||||
|
||||
@recipe = recipe
|
||||
@recipe = DRCRecipe::new(self)
|
||||
|
||||
# Make the DSL use ruby syntax highlighting
|
||||
self.syntax_scheme = "ruby"
|
||||
|
|
@ -124,40 +155,14 @@ module DRC
|
|||
|
||||
# Implements the execute method
|
||||
def executable(macro)
|
||||
DRCExecutable::new(macro, @recipe.generator("script" => macro.path))
|
||||
DRCExecutable::new(macro, self, @recipe.generator("script" => macro.path))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# A recipe implementation allowing the DRC run to be redone
|
||||
class DRCRecipe < RBA::Recipe
|
||||
|
||||
def initialize
|
||||
super("drc", "DRC recipe")
|
||||
end
|
||||
|
||||
def executable(params)
|
||||
|
||||
script = params["script"]
|
||||
if ! script
|
||||
return
|
||||
end
|
||||
|
||||
macro = RBA::Macro::macro_by_path(script)
|
||||
macro || raise("Can't find DRC script #{script} - unable to re-run")
|
||||
|
||||
DRCExecutable::new(macro, self.generator("script" => script), params["rdb_index"])
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Register the recipe
|
||||
drc_recipe = DRCRecipe::new
|
||||
|
||||
# Register the new interpreters
|
||||
DRCInterpreter::new(drc_recipe)
|
||||
DRCPlainTextInterpreter::new(drc_recipe)
|
||||
DRCInterpreter::new
|
||||
DRCPlainTextInterpreter::new
|
||||
|
||||
# Creates a new macro category
|
||||
if RBA.constants.member?(:Application) && RBA::Application::instance
|
||||
|
|
|
|||
|
|
@ -1146,8 +1146,6 @@ PartialService::move_ac () const
|
|||
void
|
||||
PartialService::deactivated ()
|
||||
{
|
||||
// clear selection when this mode is left
|
||||
partial_select (db::DBox (), lay::Editable::Reset);
|
||||
clear_partial_transient_selection ();
|
||||
}
|
||||
|
||||
|
|
@ -2169,6 +2167,110 @@ PartialService::mouse_release_event (const db::DPoint &p, unsigned int buttons,
|
|||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
PartialService::begin_move (MoveMode mode, const db::DPoint &p, lay::angle_constraint_type ac)
|
||||
{
|
||||
if (has_selection () && mode == lay::Editable::Selected) {
|
||||
|
||||
m_alt_ac = ac;
|
||||
|
||||
m_dragging = true;
|
||||
m_keep_selection = true;
|
||||
|
||||
if (is_single_point_selection ()) {
|
||||
// for a single selected point we use the original point as the start location which
|
||||
// allows bringing it to grid
|
||||
m_current = m_start = single_selected_point ();
|
||||
} else if (is_single_edge_selection ()) {
|
||||
// for an edge selection use the point projected to edge as the start location which
|
||||
// allows bringing it to grid
|
||||
m_current = m_start = projected_to_edge (single_selected_edge (), p);
|
||||
} else {
|
||||
m_current = m_start = p;
|
||||
}
|
||||
|
||||
m_alt_ac = lay::AC_Global;
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PartialService::move (const db::DPoint &p, lay::angle_constraint_type ac)
|
||||
{
|
||||
m_alt_ac = ac;
|
||||
|
||||
set_cursor (lay::Cursor::size_all);
|
||||
|
||||
// drag the vertex or edge/segment
|
||||
if (is_single_point_selection () || is_single_edge_selection ()) {
|
||||
|
||||
lay::PointSnapToObjectResult snap_details;
|
||||
|
||||
// for a single selected point or edge, m_start is the original position and we snap the target -
|
||||
// thus, we can bring the point on grid or to an object's edge or vertex
|
||||
snap_details = snap2 (p);
|
||||
if (snap_details.object_snap == lay::PointSnapToObjectResult::NoObject) {
|
||||
m_current = m_start + snap (p - m_start);
|
||||
} else {
|
||||
m_current = snap_details.snapped_point;
|
||||
mouse_cursor_from_snap_details (snap_details);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// snap movement to angle and grid without object
|
||||
m_current = m_start + snap (p - m_start);
|
||||
clear_mouse_cursors ();
|
||||
|
||||
}
|
||||
|
||||
selection_to_view ();
|
||||
|
||||
m_alt_ac = lay::AC_Global;
|
||||
}
|
||||
|
||||
void
|
||||
PartialService::end_move (const db::DPoint & /*p*/, lay::angle_constraint_type ac)
|
||||
{
|
||||
m_alt_ac = ac;
|
||||
|
||||
if (m_current != m_start) {
|
||||
|
||||
// stop dragging
|
||||
ui ()->ungrab_mouse (this);
|
||||
|
||||
if (manager ()) {
|
||||
manager ()->transaction (tl::to_string (tr ("Partial move")));
|
||||
}
|
||||
|
||||
// heuristically, if there is just one edge selected: do not confine to the movement
|
||||
// angle constraint - the edge usually is confined enough
|
||||
db::DTrans move_trans = db::DTrans (m_current - m_start);
|
||||
|
||||
transform_selection (move_trans);
|
||||
|
||||
if (manager ()) {
|
||||
manager ()->commit ();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (! m_keep_selection) {
|
||||
m_selection.clear ();
|
||||
}
|
||||
|
||||
m_dragging = false;
|
||||
selection_to_view ();
|
||||
|
||||
clear_mouse_cursors ();
|
||||
|
||||
m_alt_ac = lay::AC_Global;
|
||||
}
|
||||
|
||||
bool
|
||||
PartialService::has_selection ()
|
||||
{
|
||||
|
|
@ -2181,6 +2283,58 @@ PartialService::selection_size ()
|
|||
return m_selection.size ();
|
||||
}
|
||||
|
||||
db::DBox
|
||||
PartialService::selection_bbox ()
|
||||
{
|
||||
// build the transformation variants cache
|
||||
// TODO: this is done multiple times - once for each service!
|
||||
TransformationVariants tv (view ());
|
||||
const db::DCplxTrans &vp = view ()->viewport ().trans ();
|
||||
|
||||
lay::TextInfo text_info (view ());
|
||||
|
||||
db::DBox box;
|
||||
for (partial_objects::const_iterator r = m_selection.begin (); r != m_selection.end (); ++r) {
|
||||
|
||||
const lay::CellView &cv = view ()->cellview (r->first.cv_index ());
|
||||
const db::Layout &layout = cv->layout ();
|
||||
|
||||
db::CplxTrans ctx_trans = db::CplxTrans (layout.dbu ()) * cv.context_trans () * r->first.trans ();
|
||||
|
||||
db::box_convert<db::CellInst> bc (layout);
|
||||
if (! r->first.is_cell_inst ()) {
|
||||
|
||||
const std::vector<db::DCplxTrans> *tv_list = tv.per_cv_and_layer (r->first.cv_index (), r->first.layer ());
|
||||
if (tv_list != 0) {
|
||||
for (std::vector<db::DCplxTrans>::const_iterator t = tv_list->begin (); t != tv_list->end (); ++t) {
|
||||
if (r->first.shape ().is_text ()) {
|
||||
db::Text text;
|
||||
r->first.shape ().text (text);
|
||||
box += *t * text_info.bbox (ctx_trans * text, vp * *t);
|
||||
} else {
|
||||
for (auto e = r->second.begin (); e != r->second.end (); ++e) {
|
||||
box += *t * (ctx_trans * e->bbox ());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
const std::vector<db::DCplxTrans> *tv_list = tv.per_cv (r->first.cv_index ());
|
||||
if (tv_list != 0) {
|
||||
for (std::vector<db::DCplxTrans>::const_iterator t = tv_list->begin (); t != tv_list->end (); ++t) {
|
||||
box += *t * (ctx_trans * r->first.back ().bbox (bc));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return box;
|
||||
}
|
||||
|
||||
bool
|
||||
PartialService::has_transient_selection ()
|
||||
{
|
||||
|
|
@ -2191,6 +2345,8 @@ PartialService::has_transient_selection ()
|
|||
void
|
||||
PartialService::del ()
|
||||
{
|
||||
std::set<db::Layout *> needs_cleanup;
|
||||
|
||||
// stop dragging
|
||||
ui ()->ungrab_mouse (this);
|
||||
|
||||
|
|
@ -2272,6 +2428,9 @@ PartialService::del ()
|
|||
if (r->first.is_cell_inst ()) {
|
||||
const lay::CellView &cv = view ()->cellview (r->first.cv_index ());
|
||||
if (cv.is_valid ()) {
|
||||
if (cv->layout ().cell (r->first.back ().inst_ptr.cell_index ()).is_proxy ()) {
|
||||
needs_cleanup.insert (& cv->layout ());
|
||||
}
|
||||
cv->layout ().cell (r->first.cell_index ()).erase (r->first.back ().inst_ptr);
|
||||
}
|
||||
}
|
||||
|
|
@ -2285,6 +2444,11 @@ PartialService::del ()
|
|||
m_selection.clear ();
|
||||
m_dragging = false;
|
||||
selection_to_view ();
|
||||
|
||||
// clean up the layouts that need to do so.
|
||||
for (std::set<db::Layout *>::const_iterator l = needs_cleanup.begin (); l != needs_cleanup.end (); ++l) {
|
||||
(*l)->cleanup ();
|
||||
}
|
||||
}
|
||||
|
||||
lay::InstanceMarker *
|
||||
|
|
|
|||
|
|
@ -266,6 +266,26 @@ public:
|
|||
*/
|
||||
virtual bool has_transient_selection ();
|
||||
|
||||
/**
|
||||
* @brief Gets the selection bounding box
|
||||
*/
|
||||
virtual db::DBox selection_bbox ();
|
||||
|
||||
/**
|
||||
* @brief Start a "move" operation
|
||||
*/
|
||||
virtual bool begin_move (MoveMode sel, const db::DPoint &p, lay::angle_constraint_type ac);
|
||||
|
||||
/**
|
||||
* @brief Continue a "move" operation
|
||||
*/
|
||||
virtual void move (const db::DPoint &p, lay::angle_constraint_type ac);
|
||||
|
||||
/**
|
||||
* @brief Terminate a "move" operation
|
||||
*/
|
||||
virtual void end_move (const db::DPoint &p, lay::angle_constraint_type ac);
|
||||
|
||||
/**
|
||||
* @brief Implement the "select" method at least to clear the selection
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -471,6 +471,7 @@ Service::selection_bbox ()
|
|||
// build the transformation variants cache
|
||||
// TODO: this is done multiple times - once for each service!
|
||||
TransformationVariants tv (view ());
|
||||
const db::DCplxTrans &vp = view ()->viewport ().trans ();
|
||||
|
||||
lay::TextInfo text_info (view ());
|
||||
|
||||
|
|
@ -491,7 +492,7 @@ Service::selection_bbox ()
|
|||
if (r->shape ().is_text ()) {
|
||||
db::Text text;
|
||||
r->shape ().text (text);
|
||||
box += *t * text_info.bbox (ctx_trans * text, *t);
|
||||
box += *t * text_info.bbox (ctx_trans * text, vp * *t);
|
||||
} else {
|
||||
box += *t * (ctx_trans * r->shape ().bbox ());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -463,6 +463,10 @@
|
|||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../icons/icons.qrc">
|
||||
<normaloff>:/breakpoint_16px.png</normaloff>:/breakpoint_16px.png</iconset>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>F9</string>
|
||||
</property>
|
||||
|
|
@ -614,13 +618,20 @@
|
|||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="lay::DecoratedLineEdit" name="searchEditBox"/>
|
||||
<widget class="lay::DecoratedLineEdit" name="searchEditBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Ignored">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="findNextButton">
|
||||
|
|
@ -721,7 +732,14 @@
|
|||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="lay::DecoratedLineEdit" name="replaceText"/>
|
||||
<widget class="lay::DecoratedLineEdit" name="replaceText">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Ignored">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="replaceNextButton">
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
<ui version="4.0" >
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TechMacrosPage</class>
|
||||
<widget class="QWidget" name="TechMacrosPage" >
|
||||
<property name="geometry" >
|
||||
<widget class="QWidget" name="TechMacrosPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
|
|
@ -9,94 +10,86 @@
|
|||
<height>492</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<layout class="QVBoxLayout">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="margin" stdset="0">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="title_label" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy>
|
||||
<hsizetype>5</hsizetype>
|
||||
<vsizetype>0</vsizetype>
|
||||
<widget class="QLabel" name="title_label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<property name="text">
|
||||
<string>%CAT_DESC%</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="macro_frame" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy>
|
||||
<hsizetype>5</hsizetype>
|
||||
<vsizetype>5</vsizetype>
|
||||
<widget class="QFrame" name="macro_frame">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>1</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frameShape" >
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow" >
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<layout class="QVBoxLayout">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="margin" stdset="0">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QSplitter" name="splitter" >
|
||||
<property name="orientation" >
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<widget class="QTreeView" name="folder_tree" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy>
|
||||
<hsizetype>5</hsizetype>
|
||||
<vsizetype>7</vsizetype>
|
||||
<widget class="QTreeView" name="folder_tree">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QFrame" name="frame" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy>
|
||||
<hsizetype>5</hsizetype>
|
||||
<vsizetype>5</vsizetype>
|
||||
<widget class="QFrame" name="frame">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>3</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frameShape" >
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow" >
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<layout class="QVBoxLayout">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="margin" stdset="0">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTextBrowser" name="macro_text" >
|
||||
<property name="lineWrapMode" >
|
||||
<widget class="QTextBrowser" name="macro_text">
|
||||
<property name="lineWrapMode">
|
||||
<enum>QTextEdit::NoWrap</enum>
|
||||
</property>
|
||||
</widget>
|
||||
|
|
@ -109,70 +102,68 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="note_label" >
|
||||
<property name="text" >
|
||||
<string><html><body><span style=" font-weight:600;">Note</span>: to edit, add or delete scripts, use the macro development facility in "Tools/Macro Development" or "Tools/Verification/DRC/Edit DRC Scripts". You will find a branch for this technology there.</p></body></html></string>
|
||||
<widget class="QLabel" name="note_label">
|
||||
<property name="text">
|
||||
<string><html><body><span style=" font-weight:600;">Note</span>: to edit, add or delete scripts, use the macro development facility in "Tools/Macro Development". You will find a branch for this technology there.</p></body></html></string>
|
||||
</property>
|
||||
<property name="wordWrap" >
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="empty_label1" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy>
|
||||
<hsizetype>5</hsizetype>
|
||||
<vsizetype>5</vsizetype>
|
||||
<widget class="QLabel" name="empty_label1">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string><html><body><span style=" font-weight:600;">Note</span>: this page is empty because no valid base path is set.<br/>Select a base path on the "General" page to establish one.</p></body></html></string>
|
||||
<property name="text">
|
||||
<string><html><body><span style=" font-weight:600;">Note</span>: this page is empty because no valid base path is set.<br/>Select a base path on the "General" page to establish one.</p></body></html></string>
|
||||
</property>
|
||||
<property name="alignment" >
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap" >
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="empty_label2_frame" >
|
||||
<property name="minimumSize" >
|
||||
<widget class="QFrame" name="empty_label2_frame">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
<height>200</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="frameShape" >
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow" >
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QGridLayout" >
|
||||
<property name="margin" >
|
||||
<layout class="QGridLayout">
|
||||
<property name="margin" stdset="0">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="2" column="1" >
|
||||
<widget class="QPushButton" name="create_folder_button" >
|
||||
<property name="text" >
|
||||
<item row="2" column="1">
|
||||
<widget class="QPushButton" name="create_folder_button">
|
||||
<property name="text">
|
||||
<string>Create Folder Now</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2" >
|
||||
<item row="2" column="2">
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>211</width>
|
||||
<height>20</height>
|
||||
|
|
@ -180,12 +171,12 @@
|
|||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="2" column="0" >
|
||||
<item row="2" column="0">
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>231</width>
|
||||
<height>20</height>
|
||||
|
|
@ -193,12 +184,12 @@
|
|||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="3" >
|
||||
<item row="3" column="0" colspan="3">
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>651</width>
|
||||
<height>40</height>
|
||||
|
|
@ -206,12 +197,12 @@
|
|||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="3" >
|
||||
<item row="0" column="0" colspan="3">
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>671</width>
|
||||
<height>40</height>
|
||||
|
|
@ -219,23 +210,21 @@
|
|||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="3" >
|
||||
<widget class="QLabel" name="empty_label2" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy>
|
||||
<hsizetype>5</hsizetype>
|
||||
<vsizetype>5</vsizetype>
|
||||
<item row="1" column="0" colspan="3">
|
||||
<widget class="QLabel" name="empty_label2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string><html><body><span style=" font-weight:600;">Note</span>: this page is empty, because the corresponding macro folder</p><p style=" margin-top:5px; margin-bottom:5px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><i>%BASE_PATH%/%CAT%</i></p><p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">cannot be found or is not accessible.</p></body></html></string>
|
||||
<property name="text">
|
||||
<string><html><body><span style=" font-weight:600;">Note</span>: this page is empty, because the corresponding macro folder</p><p style=" margin-top:5px; margin-bottom:5px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><i>%BASE_PATH%/%CAT%</i></p><p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">cannot be found or is not accessible.</p></body></html></string>
|
||||
</property>
|
||||
<property name="alignment" >
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap" >
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
|
|
@ -244,15 +233,15 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="empty_label3" >
|
||||
<property name="text" >
|
||||
<string><html><body>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Note</span>: this page is empty, because the macro location is shared by a different macro site (specifically "<i>%ALT_DESC%</i>"). Such a site cannot be made technology specific. Choose a base path that points to a different location.</p></body></html></string>
|
||||
<widget class="QLabel" name="empty_label3">
|
||||
<property name="text">
|
||||
<string><html><body>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Note</span>: this page is empty, because the macro location is shared by a different macro site (specifically "<i>%ALT_DESC%</i>"). Such a site cannot be made technology specific. Choose a base path that points to a different location.</p></body></html></string>
|
||||
</property>
|
||||
<property name="alignment" >
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap" >
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
|
|
|
|||
|
|
@ -385,9 +385,9 @@ MacroController::sync_implicit_macros (bool ask_before_autorun)
|
|||
} else {
|
||||
|
||||
// determine the paths currently in use
|
||||
std::map<std::string, const ExternalPathDescriptor *> prev_folders_by_path;
|
||||
std::map<std::string, ExternalPathDescriptor> prev_folders_by_path;
|
||||
for (std::vector<ExternalPathDescriptor>::const_iterator p = m_external_paths.begin (); p != m_external_paths.end (); ++p) {
|
||||
prev_folders_by_path.insert (std::make_pair (p->path, p.operator-> ()));
|
||||
prev_folders_by_path.insert (std::make_pair (p->path, *p));
|
||||
}
|
||||
|
||||
// gets the external paths (tech, packages) into m_external_paths
|
||||
|
|
@ -433,23 +433,39 @@ MacroController::sync_implicit_macros (bool ask_before_autorun)
|
|||
|
||||
for (std::vector<ExternalPathDescriptor>::const_iterator p = m_external_paths.begin (); p != m_external_paths.end (); ++p) {
|
||||
|
||||
if (prev_folders_by_path.find (p->path) != prev_folders_by_path.end ()) {
|
||||
continue;
|
||||
}
|
||||
auto pf = prev_folders_by_path.find (p->path);
|
||||
if (pf != prev_folders_by_path.end ()) {
|
||||
|
||||
if (tl::verbosity () >= 20) {
|
||||
tl::info << "Adding macro folder " << p->path << ", category '" << p->cat << "' for '" << p->description << "'";
|
||||
}
|
||||
if (pf->second.version != p->version) {
|
||||
|
||||
// Add the folder. Note: it may happen that a macro folder for the tech specific macros already exists in
|
||||
// a non-tech context.
|
||||
// In that case, the add_folder method will return 0.
|
||||
if (tl::verbosity () >= 20) {
|
||||
tl::info << "New version (" << p->version << " vs. " << pf->second.version << ") of macro folder " << p->path << ", category '" << p->cat << "' for '" << p->description << "'";
|
||||
}
|
||||
|
||||
lym::MacroCollection *mc = lym::MacroCollection::root ().folder_by_name (p->path);
|
||||
if (mc) {
|
||||
new_folders.push_back (mc);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (tl::verbosity () >= 20) {
|
||||
tl::info << "Adding macro folder " << p->path << ", category '" << p->cat << "' for '" << p->description << "'";
|
||||
}
|
||||
|
||||
// Add the folder. Note: it may happen that a macro folder for the tech specific macros already exists in
|
||||
// a non-tech context.
|
||||
// In that case, the add_folder method will return 0.
|
||||
|
||||
// TODO: is it wise to make this writeable?
|
||||
lym::MacroCollection *mc = lym::MacroCollection::root ().add_folder (p->description, p->path, p->cat, p->readonly);
|
||||
if (mc) {
|
||||
mc->set_virtual_mode (p->type);
|
||||
new_folders.push_back (mc);
|
||||
}
|
||||
|
||||
// TODO: is it wise to make this writeable?
|
||||
lym::MacroCollection *mc = lym::MacroCollection::root ().add_folder (p->description, p->path, p->cat, p->readonly);
|
||||
if (mc) {
|
||||
mc->set_virtual_mode (p->type);
|
||||
new_folders.push_back (mc);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -565,7 +581,7 @@ MacroController::sync_macro_sources ()
|
|||
if (*f != macro_categories () [c].name) {
|
||||
description += " - " + tl::to_string (tr ("%1 branch").arg (tl::to_qstring (*f)));
|
||||
}
|
||||
external_paths.push_back (ExternalPathDescriptor (tl::to_string (macro_dir.path ()), description, macro_categories () [c].name, lym::MacroCollection::SaltFolder, g->is_readonly ()));
|
||||
external_paths.push_back (ExternalPathDescriptor (tl::to_string (macro_dir.path ()), description, macro_categories () [c].name, lym::MacroCollection::SaltFolder, g->is_readonly (), g->version ()));
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -204,8 +204,8 @@ private:
|
|||
*/
|
||||
struct ExternalPathDescriptor
|
||||
{
|
||||
ExternalPathDescriptor (const std::string &_path, const std::string &_description, const std::string &_cat, lym::MacroCollection::FolderType _type, bool _readonly)
|
||||
: path (_path), description (_description), cat (_cat), type (_type), readonly (_readonly)
|
||||
ExternalPathDescriptor (const std::string &_path, const std::string &_description, const std::string &_cat, lym::MacroCollection::FolderType _type, bool _readonly, const std::string &_version = std::string ())
|
||||
: path (_path), description (_description), cat (_cat), type (_type), version (_version), readonly (_readonly)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
|
@ -214,6 +214,7 @@ private:
|
|||
std::string description;
|
||||
std::string cat;
|
||||
lym::MacroCollection::FolderType type;
|
||||
std::string version;
|
||||
bool readonly;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -254,7 +254,6 @@ MacroEditorDialog::MacroEditorDialog (lay::Dispatcher *pr, lym::MacroCollection
|
|||
m_first_show (true), m_debugging_on (true),
|
||||
mp_run_macro (0),
|
||||
md_update_console_text (this, &MacroEditorDialog::update_console_text),
|
||||
md_search_edited (this, &MacroEditorDialog::do_search_edited),
|
||||
m_in_event_handler (false),
|
||||
m_os (OS_none),
|
||||
m_new_line (true),
|
||||
|
|
@ -429,6 +428,17 @@ MacroEditorDialog::MacroEditorDialog (lay::Dispatcher *pr, lym::MacroCollection
|
|||
connect (action, SIGNAL (triggered ()), this, SLOT (close_all_right ()));
|
||||
tabWidget->addAction (action);
|
||||
|
||||
action = new QAction (this);
|
||||
action->setSeparator (true);
|
||||
tabWidget->addAction (action);
|
||||
|
||||
mp_tabs_menu = new QMenu ();
|
||||
|
||||
action = new QAction (tr ("Tabs"), this);
|
||||
action->setMenu (mp_tabs_menu);
|
||||
connect (mp_tabs_menu, SIGNAL (aboutToShow ()), this, SLOT (tabs_menu_about_to_show ()));
|
||||
tabWidget->addAction (action);
|
||||
|
||||
dbgOn->setEnabled (true);
|
||||
runButton->setEnabled (true);
|
||||
runThisButton->setEnabled (true);
|
||||
|
|
@ -672,6 +682,34 @@ MacroEditorDialog::instance ()
|
|||
return s_macro_editor_instance;
|
||||
}
|
||||
|
||||
void
|
||||
MacroEditorDialog::tab_menu_selected ()
|
||||
{
|
||||
QAction *action = dynamic_cast<QAction *> (sender ());
|
||||
if (action) {
|
||||
tabWidget->setCurrentIndex (action->data ().toInt ());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroEditorDialog::tabs_menu_about_to_show ()
|
||||
{
|
||||
mp_tabs_menu->clear ();
|
||||
|
||||
for (int i = 0; i < tabWidget->count (); ++i) {
|
||||
MacroEditorPage *page = dynamic_cast<MacroEditorPage *> (tabWidget->widget (i));
|
||||
if (page) {
|
||||
QAction *action = new QAction (tl::to_qstring (page->path ()), mp_tabs_menu);
|
||||
action->setData (i);
|
||||
connect (action, SIGNAL (triggered ()), this, SLOT (tab_menu_selected ()));
|
||||
if (page->macro () == mp_run_macro) {
|
||||
action->setIcon (QIcon (":/run_16px.png"));
|
||||
}
|
||||
mp_tabs_menu->addAction (action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroEditorDialog::select_category (const std::string &cat)
|
||||
{
|
||||
|
|
@ -1770,8 +1808,6 @@ MacroEditorDialog::current_tab_changed (int index)
|
|||
}
|
||||
}
|
||||
|
||||
// clear the search
|
||||
searchEditBox->clear ();
|
||||
replaceFrame->setEnabled (page && page->macro () && !page->macro ()->is_readonly ());
|
||||
|
||||
apply_search ();
|
||||
|
|
@ -2062,14 +2098,6 @@ MacroEditorDialog::search_editing ()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroEditorDialog::search_edited ()
|
||||
{
|
||||
// since we want to move the focus to the text field, we have to do this in the deferred method
|
||||
// (this method is called from an event handler and setFocus does not have an effect then)
|
||||
md_search_edited ();
|
||||
}
|
||||
|
||||
void
|
||||
MacroEditorDialog::search_finished ()
|
||||
{
|
||||
|
|
@ -2083,7 +2111,7 @@ MacroEditorDialog::search_finished ()
|
|||
}
|
||||
|
||||
void
|
||||
MacroEditorDialog::do_search_edited ()
|
||||
MacroEditorDialog::search_edited ()
|
||||
{
|
||||
MacroEditorPage *page = dynamic_cast<MacroEditorPage *> (tabWidget->currentWidget ());
|
||||
if (! page) {
|
||||
|
|
@ -2095,7 +2123,6 @@ MacroEditorDialog::do_search_edited ()
|
|||
if (! page->has_multi_block_selection ()) {
|
||||
page->find_next ();
|
||||
}
|
||||
set_editor_focus ();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -2375,6 +2402,15 @@ BEGIN_PROTECTED
|
|||
END_PROTECTED
|
||||
}
|
||||
|
||||
void
|
||||
MacroEditorDialog::close_requested ()
|
||||
{
|
||||
MacroEditorPage *page = dynamic_cast<MacroEditorPage *> (sender ());
|
||||
if (! m_in_exec && page) {
|
||||
tab_close_requested (tabWidget->indexOf (page));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroEditorDialog::tab_close_requested (int index)
|
||||
{
|
||||
|
|
@ -2607,66 +2643,65 @@ BEGIN_PROTECTED
|
|||
return;
|
||||
}
|
||||
|
||||
std::set<std::string> modified_files;
|
||||
for (std::map <lym::Macro *, MacroEditorPage *>::const_iterator m = m_tab_widgets.begin (); m != m_tab_widgets.end (); ++m) {
|
||||
if (m->first->is_modified ()) {
|
||||
modified_files.insert (m->first->path ());
|
||||
std::map<std::string, MacroEditorPage *> path_to_page;
|
||||
for (int i = 0; i < tabWidget->count (); ++i) {
|
||||
MacroEditorPage *page = dynamic_cast<MacroEditorPage *> (tabWidget->widget (i));
|
||||
if (page) {
|
||||
path_to_page.insert (std::make_pair (page->path (), page));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<QString> conflicts;
|
||||
for (std::vector<QString>::const_iterator f = m_changed_files.begin (); f != m_changed_files.end (); ++f) {
|
||||
if (modified_files.find (tl::to_string (*f)) != modified_files.end ()) {
|
||||
conflicts.push_back (*f);
|
||||
|
||||
std::string fn = tl::to_string (*f);
|
||||
auto w = path_to_page.find (fn);
|
||||
if (w == path_to_page.end ()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (w->second->macro () && w->second->macro ()->is_modified ()) {
|
||||
|
||||
lay::MacroEditorNotification n ("reload", tl::to_string (tr ("Macro has changed on disk, but was modified")), tl::Variant (fn));
|
||||
n.add_action ("reload", tl::to_string (tr ("Reload and discard changes")));
|
||||
w->second->add_notification (n);
|
||||
|
||||
} else {
|
||||
|
||||
lay::MacroEditorNotification n ("reload", tl::to_string (tr ("Macro has changed on disk")), tl::Variant (fn));
|
||||
n.add_action ("reload", tl::to_string (tr ("Reload")));
|
||||
w->second->add_notification (n);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for (std::vector<QString>::const_iterator f = m_removed_files.begin (); f != m_removed_files.end (); ++f) {
|
||||
if (modified_files.find (tl::to_string (*f)) != modified_files.end ()) {
|
||||
conflicts.push_back (*f);
|
||||
|
||||
std::string fn = tl::to_string (*f);
|
||||
auto w = path_to_page.find (fn);
|
||||
if (w == path_to_page.end ()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (w->second->macro () && w->second->macro ()->is_modified ()) {
|
||||
|
||||
lay::MacroEditorNotification n ("close", tl::to_string (tr ("Macro has been removed on disk, but was modified")), tl::Variant (fn));
|
||||
n.add_action ("close", tl::to_string (tr ("Close tab and discard changes")));
|
||||
w->second->add_notification (n);
|
||||
|
||||
} else {
|
||||
|
||||
lay::MacroEditorNotification n ("close", tl::to_string (tr ("Macro has been removed on disk")), tl::Variant (fn));
|
||||
n.add_action ("close", tl::to_string (tr ("Close tab")));
|
||||
w->second->add_notification (n);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
QString msg;
|
||||
|
||||
if (m_changed_files.size () + m_removed_files.size () == 1) {
|
||||
msg = QObject::tr ("The following file has been changed on disk:\n\n");
|
||||
for (std::vector<QString>::const_iterator f = m_changed_files.begin (); f != m_changed_files.end (); ++f) {
|
||||
msg += QString::fromUtf8 (" %1 (modified)\n").arg (*f);
|
||||
}
|
||||
for (std::vector<QString>::const_iterator f = m_removed_files.begin (); f != m_removed_files.end (); ++f) {
|
||||
msg += QString::fromUtf8 (" %1 (removed)\n").arg (*f);
|
||||
}
|
||||
if (!conflicts.empty ()) {
|
||||
msg += tr ("\nThis file has been modified in the editor as well.\nRefresh this file and discard changes?");
|
||||
} else {
|
||||
msg += tr ("\nRefresh this file?");
|
||||
}
|
||||
} else {
|
||||
msg = QObject::tr ("The following files have been changed on disk:\n\n");
|
||||
for (std::vector<QString>::const_iterator f = m_changed_files.begin (); f != m_changed_files.end (); ++f) {
|
||||
msg += QString::fromUtf8 (" %1 (modified)\n").arg (*f);
|
||||
}
|
||||
for (std::vector<QString>::const_iterator f = m_removed_files.begin (); f != m_removed_files.end (); ++f) {
|
||||
msg += QString::fromUtf8 (" %1 (removed)\n").arg (*f);
|
||||
}
|
||||
if (!conflicts.empty ()) {
|
||||
msg += tr("\nSome of these files are modified on disk and in the editor:\n\n");
|
||||
for (std::vector<QString>::const_iterator f = conflicts.begin (); f != conflicts.end (); ++f) {
|
||||
msg += QString::fromUtf8 (" %1 (conflict)\n").arg (*f);
|
||||
}
|
||||
msg += tr ("\nRefresh these and the other files and discard all changes?");
|
||||
} else {
|
||||
msg += tr ("\nRefresh those files?");
|
||||
}
|
||||
}
|
||||
refresh_file_watcher ();
|
||||
|
||||
m_changed_files.clear ();
|
||||
m_removed_files.clear ();
|
||||
|
||||
if (QMessageBox::question (this, tr ("Refresh Files"), msg, QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
|
||||
reload_macros ();
|
||||
}
|
||||
|
||||
END_PROTECTED
|
||||
}
|
||||
|
||||
|
|
@ -2790,7 +2825,7 @@ BEGIN_PROTECTED
|
|||
std::string new_path = tl::to_string (QFileInfo (new_dir).absoluteFilePath ());
|
||||
paths.push_back (std::make_pair (new_path, cat));
|
||||
|
||||
lym::MacroCollection *c = mp_root->add_folder (tl::to_string (QObject::tr ("Project")) + " - " + new_path, new_path, cat, false);
|
||||
lym::MacroCollection *c = mp_root->add_folder (tl::to_string (QObject::tr ("Project")) + " - " + new_path, new_path, cat, false /* writeable */, false /* do not auto-create folders */);
|
||||
if (!c) {
|
||||
throw tl::Exception (tl::to_string (QObject::tr ("The selected directory is already installed as custom location")));
|
||||
}
|
||||
|
|
@ -3525,6 +3560,7 @@ MacroEditorDialog::create_page (lym::Macro *macro)
|
|||
editor->set_font (m_font_family, m_font_size);
|
||||
editor->exec_model ()->set_run_mode (m_in_exec);
|
||||
editor->connect_macro (macro);
|
||||
connect (editor.get (), SIGNAL (close_requested ()), this, SLOT (close_requested ()));
|
||||
connect (editor.get (), SIGNAL (help_requested (const QString &)), this, SLOT (help_requested (const QString &)));
|
||||
connect (editor.get (), SIGNAL (search_requested (const QString &, bool)), this, SLOT (search_requested (const QString &, bool)));
|
||||
connect (editor.get (), SIGNAL (edit_trace (bool)), this, SLOT (add_edit_trace (bool)));
|
||||
|
|
|
|||
|
|
@ -212,6 +212,7 @@ private slots:
|
|||
void search_editing ();
|
||||
void search_finished ();
|
||||
void tab_close_requested (int);
|
||||
void close_requested ();
|
||||
void close_all ();
|
||||
void close_all_but_this ();
|
||||
void close_all_left ();
|
||||
|
|
@ -232,6 +233,8 @@ private slots:
|
|||
void del_watches ();
|
||||
void clear_watches ();
|
||||
void set_debugging_on (bool on);
|
||||
void tabs_menu_about_to_show ();
|
||||
void tab_menu_selected ();
|
||||
|
||||
// edit trace navigation
|
||||
void forward ();
|
||||
|
|
@ -311,7 +314,6 @@ private:
|
|||
lym::Macro *mp_run_macro;
|
||||
std::vector<lym::Macro *> m_macro_templates;
|
||||
tl::DeferredMethod<MacroEditorDialog> md_update_console_text;
|
||||
tl::DeferredMethod<MacroEditorDialog> md_search_edited;
|
||||
TextEditWidget *mp_console_text;
|
||||
std::map <lym::Macro *, MacroEditorPage *> m_tab_widgets;
|
||||
int m_history_index;
|
||||
|
|
@ -358,6 +360,7 @@ private:
|
|||
std::vector<QString> m_changed_files, m_removed_files;
|
||||
tl::DeferredMethod<MacroEditorDialog> dm_refresh_file_watcher;
|
||||
tl::DeferredMethod<MacroEditorDialog> dm_update_ui_to_run_mode;
|
||||
QMenu *mp_tabs_menu;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@
|
|||
#include <QTimer>
|
||||
#include <QListWidget>
|
||||
#include <QApplication>
|
||||
#include <QToolButton>
|
||||
#include <QPushButton>
|
||||
|
||||
namespace lay
|
||||
{
|
||||
|
|
@ -81,6 +83,59 @@ void MacroEditorTextWidget::paintEvent (QPaintEvent *event)
|
|||
return TextEditWidget::paintEvent (event);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
// MacroEditorNotificationWidget implementation
|
||||
|
||||
MacroEditorNotificationWidget::MacroEditorNotificationWidget (MacroEditorPage *parent, const MacroEditorNotification *notification)
|
||||
: QFrame (parent), mp_parent (parent), mp_notification (notification)
|
||||
{
|
||||
setBackgroundRole (QPalette::ToolTipBase);
|
||||
setAutoFillBackground (true);
|
||||
|
||||
QHBoxLayout *layout = new QHBoxLayout (this);
|
||||
layout->setContentsMargins (4, 4, 4, 4);
|
||||
|
||||
QLabel *title_label = new QLabel (this);
|
||||
layout->addWidget (title_label, 1);
|
||||
title_label->setText (tl::to_qstring (notification->title ()));
|
||||
title_label->setForegroundRole (QPalette::ToolTipText);
|
||||
title_label->setWordWrap (true);
|
||||
activate_help_links (title_label);
|
||||
|
||||
for (auto a = notification->actions ().begin (); a != notification->actions ().end (); ++a) {
|
||||
|
||||
QPushButton *pb = new QPushButton (this);
|
||||
layout->addWidget (pb);
|
||||
|
||||
pb->setText (tl::to_qstring (a->second));
|
||||
m_action_buttons.insert (std::make_pair (pb, a->first));
|
||||
connect (pb, SIGNAL (clicked ()), this, SLOT (action_triggered ()));
|
||||
|
||||
}
|
||||
|
||||
QToolButton *close_button = new QToolButton ();
|
||||
close_button->setIcon (QIcon (":clear_edit_16px.png"));
|
||||
close_button->setAutoRaise (true);
|
||||
layout->addWidget (close_button);
|
||||
|
||||
connect (close_button, SIGNAL (clicked ()), this, SLOT (close_triggered ()));
|
||||
}
|
||||
|
||||
void
|
||||
MacroEditorNotificationWidget::action_triggered ()
|
||||
{
|
||||
auto a = m_action_buttons.find (sender ());
|
||||
if (a != m_action_buttons.end ()) {
|
||||
mp_parent->notification_action (*mp_notification, a->second);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroEditorNotificationWidget::close_triggered ()
|
||||
{
|
||||
mp_parent->remove_notification (*mp_notification);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
// MacroEditorHighlighters implementation
|
||||
|
||||
|
|
@ -491,15 +546,20 @@ void MacroEditorSidePanel::paintEvent (QPaintEvent *)
|
|||
MacroEditorPage::MacroEditorPage (QWidget * /*parent*/, MacroEditorHighlighters *highlighters)
|
||||
: mp_macro (0), mp_highlighters (highlighters), mp_highlighter (0), m_error_line (-1), m_ntab (8), m_nindent (2), m_ignore_cursor_changed_event (false)
|
||||
{
|
||||
QVBoxLayout *layout = new QVBoxLayout (this);
|
||||
|
||||
mp_layout = new QVBoxLayout (this);
|
||||
mp_layout->setContentsMargins (0, 0, 0, 0);
|
||||
|
||||
QVBoxLayout *vlayout = new QVBoxLayout (this);
|
||||
vlayout->setContentsMargins (4, 4, 4, 4);
|
||||
mp_layout->addLayout (vlayout);
|
||||
|
||||
mp_readonly_label = new QLabel (this);
|
||||
mp_readonly_label->setText (QObject::tr ("Macro is read-only and cannot be edited"));
|
||||
mp_readonly_label->hide ();
|
||||
layout->addWidget (mp_readonly_label);
|
||||
vlayout->addWidget (mp_readonly_label);
|
||||
|
||||
QHBoxLayout *hlayout = new QHBoxLayout ();
|
||||
layout->addLayout (hlayout);
|
||||
mp_layout->addLayout (hlayout);
|
||||
|
||||
mp_exec_model = new MacroEditorExecutionModel (this);
|
||||
mp_text = new MacroEditorTextWidget (this);
|
||||
|
|
@ -1021,6 +1081,8 @@ void MacroEditorPage::connect_macro (lym::Macro *macro)
|
|||
|
||||
if (mp_macro) {
|
||||
|
||||
m_path = mp_macro->path ();
|
||||
|
||||
connect (mp_macro, SIGNAL (changed ()), this, SLOT (update ()));
|
||||
|
||||
lym::Macro::Interpreter lang = macro->interpreter ();
|
||||
|
|
@ -1924,5 +1986,54 @@ MacroEditorPage::eventFilter (QObject *watched, QEvent *event)
|
|||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
MacroEditorPage::add_notification (const MacroEditorNotification ¬ification)
|
||||
{
|
||||
if (m_notification_widgets.find (¬ification) == m_notification_widgets.end ()) {
|
||||
m_notifications.push_back (notification);
|
||||
QWidget *w = new MacroEditorNotificationWidget (this, &m_notifications.back ());
|
||||
m_notification_widgets.insert (std::make_pair (&m_notifications.back (), w));
|
||||
mp_layout->insertWidget (0, w);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroEditorPage::remove_notification (const MacroEditorNotification ¬ification)
|
||||
{
|
||||
auto nw = m_notification_widgets.find (¬ification);
|
||||
if (nw != m_notification_widgets.end ()) {
|
||||
|
||||
nw->second->deleteLater ();
|
||||
m_notification_widgets.erase (nw);
|
||||
|
||||
for (auto n = m_notifications.begin (); n != m_notifications.end (); ++n) {
|
||||
if (*n == notification) {
|
||||
m_notifications.erase (n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroEditorPage::notification_action (const MacroEditorNotification ¬ification, const std::string &action)
|
||||
{
|
||||
if (action == "close") {
|
||||
|
||||
remove_notification (notification);
|
||||
|
||||
emit close_requested ();
|
||||
|
||||
} else if (action == "reload") {
|
||||
|
||||
remove_notification (notification);
|
||||
|
||||
mp_macro->load ();
|
||||
mp_macro->reset_modified ();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
#include "lymMacro.h"
|
||||
#include "layGenericSyntaxHighlighter.h"
|
||||
#include "tlVariant.h"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QPixmap>
|
||||
|
|
@ -47,10 +48,13 @@ class QSyntaxHighlighter;
|
|||
class QTimer;
|
||||
class QWindow;
|
||||
class QListWidget;
|
||||
class QVBoxLayout;
|
||||
|
||||
namespace lay
|
||||
{
|
||||
|
||||
class MacroEditorPage;
|
||||
|
||||
/**
|
||||
* @brief A collection of highlighters
|
||||
*/
|
||||
|
|
@ -209,7 +213,92 @@ private:
|
|||
bool m_debugging_on;
|
||||
};
|
||||
|
||||
class MacroEditorPage
|
||||
/**
|
||||
* @brief Descriptor for a notification inside the macro editor
|
||||
*
|
||||
* Notifications are popups added at the top of the view to indicate need for reloading for example.
|
||||
* Notifications have a name, a title, optional actions (id, title) and a parameter (e.g. file path to reload).
|
||||
* Actions are mapped to QPushButtons.
|
||||
*/
|
||||
class MacroEditorNotification
|
||||
{
|
||||
public:
|
||||
MacroEditorNotification (const std::string &name, const std::string &title, const tl::Variant ¶meter = tl::Variant ())
|
||||
: m_name (name), m_title (title), m_parameter (parameter)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void add_action (const std::string &name, const std::string &title)
|
||||
{
|
||||
m_actions.push_back (std::make_pair (name, title));
|
||||
}
|
||||
|
||||
const std::vector<std::pair<std::string, std::string> > &actions () const
|
||||
{
|
||||
return m_actions;
|
||||
}
|
||||
|
||||
const std::string &name () const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
const std::string &title () const
|
||||
{
|
||||
return m_title;
|
||||
}
|
||||
|
||||
const tl::Variant ¶meter () const
|
||||
{
|
||||
return m_parameter;
|
||||
}
|
||||
|
||||
bool operator<(const MacroEditorNotification &other) const
|
||||
{
|
||||
if (m_name != other.name ()) {
|
||||
return m_name < other.name ();
|
||||
}
|
||||
return m_parameter < other.parameter ();
|
||||
}
|
||||
|
||||
bool operator==(const MacroEditorNotification &other) const
|
||||
{
|
||||
if (m_name != other.name ()) {
|
||||
return false;
|
||||
}
|
||||
return m_parameter == other.parameter ();
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
std::string m_title;
|
||||
tl::Variant m_parameter;
|
||||
std::vector<std::pair<std::string, std::string> > m_actions;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A widget representing a notification
|
||||
*/
|
||||
class MacroEditorNotificationWidget
|
||||
: public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroEditorNotificationWidget (MacroEditorPage *parent, const MacroEditorNotification *notification);
|
||||
|
||||
private slots:
|
||||
void action_triggered ();
|
||||
void close_triggered ();
|
||||
|
||||
private:
|
||||
MacroEditorPage *mp_parent;
|
||||
const MacroEditorNotification *mp_notification;
|
||||
std::map<QObject *, std::string> m_action_buttons;
|
||||
};
|
||||
|
||||
class MacroEditorPage
|
||||
: public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
|
@ -224,6 +313,11 @@ public:
|
|||
return mp_macro;
|
||||
}
|
||||
|
||||
const std::string path () const
|
||||
{
|
||||
return m_path;
|
||||
}
|
||||
|
||||
bool is_modified () const
|
||||
{
|
||||
return m_is_modified;
|
||||
|
|
@ -269,10 +363,14 @@ public:
|
|||
|
||||
void set_editor_focus ();
|
||||
|
||||
void add_notification (const MacroEditorNotification ¬ificaton);
|
||||
void remove_notification (const MacroEditorNotification ¬ificaton);
|
||||
|
||||
signals:
|
||||
void help_requested (const QString &s);
|
||||
void search_requested (const QString &s, bool backward);
|
||||
void edit_trace (bool);
|
||||
void close_requested ();
|
||||
|
||||
public slots:
|
||||
void commit ();
|
||||
|
|
@ -288,10 +386,22 @@ protected slots:
|
|||
void hide_completer ();
|
||||
|
||||
private:
|
||||
friend class MacroEditorNotificationWidget;
|
||||
|
||||
struct CompareNotificationPointers
|
||||
{
|
||||
bool operator() (const MacroEditorNotification *a, const MacroEditorNotification *b) const
|
||||
{
|
||||
return *a < *b;
|
||||
}
|
||||
};
|
||||
|
||||
lym::Macro *mp_macro;
|
||||
std::string m_path;
|
||||
MacroEditorExecutionModel *mp_exec_model;
|
||||
MacroEditorTextWidget *mp_text;
|
||||
MacroEditorSidePanel *mp_side_panel;
|
||||
QVBoxLayout *mp_layout;
|
||||
QLabel *mp_readonly_label;
|
||||
bool m_is_modified;
|
||||
MacroEditorHighlighters *mp_highlighters;
|
||||
|
|
@ -305,6 +415,8 @@ private:
|
|||
QTimer *mp_completer_timer;
|
||||
QWidget *mp_completer_popup;
|
||||
QListWidget *mp_completer_list;
|
||||
std::list<MacroEditorNotification> m_notifications;
|
||||
std::map<const MacroEditorNotification *, QWidget *, CompareNotificationPointers> m_notification_widgets;
|
||||
|
||||
void update_extra_selections ();
|
||||
bool return_pressed ();
|
||||
|
|
@ -316,6 +428,7 @@ private:
|
|||
QTextCursor get_completer_cursor (int &pos0, int &pos);
|
||||
bool select_match_here ();
|
||||
void replace_in_selection (const QString &replace, bool first);
|
||||
void notification_action (const MacroEditorNotification ¬ification, const std::string &action);
|
||||
|
||||
bool eventFilter (QObject *watched, QEvent *event);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#include "layConfig.h"
|
||||
#include "layMainWindow.h"
|
||||
#include "layQtTools.h"
|
||||
#include "layBusy.h"
|
||||
#include "tlLog.h"
|
||||
|
||||
#include <QDir>
|
||||
|
|
@ -138,11 +139,12 @@ SaltController::show_editor ()
|
|||
lay::restore_dialog_state (mp_salt_dialog, s);
|
||||
}
|
||||
|
||||
// while running the dialog, don't watch file events - that would interfere with
|
||||
// the changes applied by the dialog itself.
|
||||
m_file_watcher->enable (false);
|
||||
mp_salt_dialog->exec ();
|
||||
m_file_watcher->enable (true);
|
||||
{
|
||||
// while running the dialog, don't watch file events - that would interfere with
|
||||
// the changes applied by the dialog itself.
|
||||
lay::BusySection busy_section; // disable file watcher
|
||||
mp_salt_dialog->exec ();
|
||||
}
|
||||
|
||||
mp_plugin_root->config_set (cfg_salt_manager_window_state, lay::save_dialog_state (mp_salt_dialog));
|
||||
|
||||
|
|
@ -154,13 +156,13 @@ SaltController::show_editor ()
|
|||
void
|
||||
SaltController::sync_file_watcher ()
|
||||
{
|
||||
lay::BusySection busy_section; // disable file watcher
|
||||
|
||||
if (m_file_watcher) {
|
||||
m_file_watcher->clear ();
|
||||
m_file_watcher->enable (false);
|
||||
for (lay::Salt::flat_iterator g = m_salt.begin_flat (); g != m_salt.end_flat (); ++g) {
|
||||
m_file_watcher->add_file ((*g)->path ());
|
||||
}
|
||||
m_file_watcher->enable (true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -216,7 +218,18 @@ SaltController::install_packages (const std::vector<std::string> &packages, bool
|
|||
manager.compute_packages (m_salt, salt_mine);
|
||||
}
|
||||
|
||||
return manager.execute (0, m_salt);
|
||||
bool result = false;
|
||||
|
||||
{
|
||||
// while running the dialog, don't watch file events - that would interfere with
|
||||
// the changes applied by the dialog itself.
|
||||
lay::BusySection busy_section; // disable file watcher
|
||||
result = manager.execute (0, m_salt);
|
||||
}
|
||||
|
||||
sync_file_watcher ();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@
|
|||
#include "tlException.h"
|
||||
#include "tlString.h"
|
||||
#include "tlLog.h"
|
||||
#include "tlFileUtils.h"
|
||||
#include "tlStream.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
|
|
@ -297,16 +299,6 @@ void signal_handler (int signo, siginfo_t *si, void *)
|
|||
lay::Version::name () + " " +
|
||||
lay::Version::version () + " (" + lay::Version::subversion () + ")\n";
|
||||
|
||||
std::unique_ptr<CrashMessage> msg;
|
||||
|
||||
bool has_gui = s_sh_has_gui && lay::ApplicationBase::instance () && lay::ApplicationBase::instance ()->has_gui ();
|
||||
|
||||
if (has_gui) {
|
||||
msg.reset (new CrashMessage (0, false, tl::to_qstring (text) + QObject::tr ("\nCollecting backtrace ..")));
|
||||
msg->show ();
|
||||
lay::ApplicationBase::instance ()->qapp_gui ()->setOverrideCursor (Qt::WaitCursor);
|
||||
}
|
||||
|
||||
text += std::string ("\nBacktrace:\n");
|
||||
|
||||
#if 0
|
||||
|
|
@ -332,14 +324,6 @@ void signal_handler (int signo, siginfo_t *si, void *)
|
|||
bool has_addr2line = true;
|
||||
for (size_t i = 0; i < nptrs; ++i) {
|
||||
|
||||
if (has_gui) {
|
||||
lay::ApplicationBase::instance ()->qapp_gui ()->processEvents ();
|
||||
if (msg->is_cancel_pressed ()) {
|
||||
text += "...\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Dl_info info;
|
||||
dladdr (array [i], &info);
|
||||
|
||||
|
|
@ -407,9 +391,25 @@ void signal_handler (int signo, siginfo_t *si, void *)
|
|||
|
||||
#endif
|
||||
|
||||
if (has_gui) {
|
||||
try {
|
||||
|
||||
lay::ApplicationBase::instance ()->qapp_gui ()->setOverrideCursor (QCursor ());
|
||||
// write crash log
|
||||
|
||||
std::string crash_log = tl::combine_path (lay::ApplicationBase::instance () ? lay::ApplicationBase::instance ()->appdata_path () : ".", "klayout_crash.log");
|
||||
|
||||
tl::OutputStream os (crash_log, tl::OutputStream::OM_Plain, true);
|
||||
os << text;
|
||||
|
||||
text += "\nCrash log written to " + crash_log;
|
||||
|
||||
} catch (...) {
|
||||
// .. ignore errors
|
||||
}
|
||||
|
||||
tl::error << text << tl::noendl;
|
||||
|
||||
bool has_gui = s_sh_has_gui && lay::ApplicationBase::instance () && lay::ApplicationBase::instance ()->has_gui ();
|
||||
if (has_gui) {
|
||||
|
||||
// YES! I! KNOW!
|
||||
// In a signal handler you shall not do fancy stuff (in particular not
|
||||
|
|
@ -418,8 +418,10 @@ void signal_handler (int signo, siginfo_t *si, void *)
|
|||
// from our stack frames) and everything is better than just core dumping.
|
||||
// Isn't it?
|
||||
|
||||
msg->set_text (tl::to_qstring (text));
|
||||
msg->set_can_resume (can_resume);
|
||||
lay::ApplicationBase::instance ()->qapp_gui ()->setOverrideCursor (QCursor ());
|
||||
|
||||
std::unique_ptr<CrashMessage> msg;
|
||||
msg.reset (new CrashMessage (0, can_resume, tl::to_qstring (text)));
|
||||
|
||||
if (! msg->exec ()) {
|
||||
|
||||
|
|
@ -438,7 +440,6 @@ void signal_handler (int signo, siginfo_t *si, void *)
|
|||
|
||||
} else {
|
||||
|
||||
tl::error << text << tl::noendl;
|
||||
_exit (signo);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -748,10 +748,11 @@ InstFinder::find_internal (LayoutViewBase *view, unsigned int cv_index, const db
|
|||
m_visible_layer_indexes.push_back (l->layer_index ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_visible_layers || view->guiding_shapes_visible ()) {
|
||||
m_visible_layer_indexes.push_back (cv->layout ().guiding_shape_layer ());
|
||||
// add guiding shape and error layers so we can select cells by error markers or guiding shapes
|
||||
if (view->guiding_shapes_visible ()) {
|
||||
m_visible_layer_indexes.push_back (cv->layout ().guiding_shape_layer ());
|
||||
}
|
||||
m_visible_layer_indexes.push_back (cv->layout ().error_layer ());
|
||||
}
|
||||
|
||||
m_cv_index = cv_index;
|
||||
|
|
|
|||
|
|
@ -5308,7 +5308,7 @@ LayoutViewBase::paste ()
|
|||
}
|
||||
|
||||
void
|
||||
LayoutViewBase::paste_interactive ()
|
||||
LayoutViewBase::paste_interactive (bool transient_mode)
|
||||
{
|
||||
clear_selection ();
|
||||
|
||||
|
|
@ -5320,7 +5320,7 @@ LayoutViewBase::paste_interactive ()
|
|||
// operations.
|
||||
trans->close ();
|
||||
|
||||
if (mp_move_service->begin_move (trans.release (), false)) {
|
||||
if (mp_move_service->begin_move (trans.release (), transient_mode)) {
|
||||
switch_mode (-1); // move mode
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -254,7 +254,7 @@ public:
|
|||
/**
|
||||
* @brief Pastes from clipboard and initiates a move
|
||||
*/
|
||||
void paste_interactive ();
|
||||
void paste_interactive (bool transient_mode = false);
|
||||
|
||||
/**
|
||||
* @brief Copies to clipboard
|
||||
|
|
|
|||
|
|
@ -240,7 +240,7 @@ MoveService::mouse_press_event (const db::DPoint &p, unsigned int buttons, bool
|
|||
}
|
||||
|
||||
bool
|
||||
MoveService::begin_move (db::Transaction *transaction, bool selected_after_move)
|
||||
MoveService::begin_move (db::Transaction *transaction, bool transient_selection)
|
||||
{
|
||||
if (m_dragging) {
|
||||
return false;
|
||||
|
|
@ -248,16 +248,28 @@ MoveService::begin_move (db::Transaction *transaction, bool selected_after_move)
|
|||
|
||||
std::unique_ptr<db::Transaction> trans_holder (transaction);
|
||||
|
||||
bool drag_transient = ! selected_after_move;
|
||||
if (! mp_editables->has_selection ()) {
|
||||
// try to use the transient selection for the real one
|
||||
mp_editables->transient_to_selection ();
|
||||
drag_transient = true;
|
||||
}
|
||||
bool drag_transient = false;
|
||||
|
||||
if (! transaction) {
|
||||
|
||||
// unless in "continue with move" use case try to establish a selection
|
||||
|
||||
if (! mp_editables->has_selection ()) {
|
||||
// try to use the transient selection for the real one
|
||||
mp_editables->transient_to_selection ();
|
||||
drag_transient = true;
|
||||
}
|
||||
|
||||
if (! mp_editables->has_selection ()) {
|
||||
// still nothing selected
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// inherit transient selection mode from previous operation
|
||||
drag_transient = transient_selection;
|
||||
|
||||
if (! mp_editables->has_selection ()) {
|
||||
// still nothing selected
|
||||
return false;
|
||||
}
|
||||
|
||||
db::DBox bbox = mp_editables->selection_bbox ();
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ public:
|
|||
~MoveService ();
|
||||
|
||||
virtual bool configure (const std::string &name, const std::string &value);
|
||||
bool begin_move (db::Transaction *transaction = 0, bool selected_after_move = true);
|
||||
bool begin_move (db::Transaction *transaction = 0, bool transient_selection = false);
|
||||
|
||||
private:
|
||||
virtual bool mouse_press_event (const db::DPoint &p, unsigned int buttons, bool prio);
|
||||
|
|
|
|||
|
|
@ -1,37 +1,38 @@
|
|||
<ui version="4.0" >
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>RenameCellDialog</class>
|
||||
<widget class="QDialog" name="RenameCellDialog" >
|
||||
<property name="geometry" >
|
||||
<widget class="QDialog" name="RenameCellDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>286</width>
|
||||
<height>124</height>
|
||||
<width>540</width>
|
||||
<height>133</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
<property name="windowTitle">
|
||||
<string>Rename Cell</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" >
|
||||
<property name="margin" >
|
||||
<layout class="QGridLayout">
|
||||
<property name="margin" stdset="0">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="1" column="0" >
|
||||
<widget class="QLabel" name="label" >
|
||||
<property name="text" >
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>New cell name </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2" >
|
||||
<item row="0" column="0" colspan="2">
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>268</width>
|
||||
<height>16</height>
|
||||
|
|
@ -39,15 +40,15 @@
|
|||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="1" >
|
||||
<widget class="QLineEdit" name="name_le" />
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="name_le"/>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2" >
|
||||
<item row="2" column="0" colspan="2">
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>268</width>
|
||||
<height>31</height>
|
||||
|
|
@ -55,13 +56,13 @@
|
|||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2" >
|
||||
<widget class="QDialogButtonBox" name="buttonBox" >
|
||||
<property name="orientation" >
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons" >
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
@ -79,11 +80,11 @@
|
|||
<receiver>RenameCellDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
|
|
@ -95,11 +96,11 @@
|
|||
<receiver>RenameCellDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include "layBusy.h"
|
||||
#include "tlThreads.h"
|
||||
#include "tlFileSystemWatcher.h"
|
||||
|
||||
namespace lay
|
||||
{
|
||||
|
|
@ -61,6 +62,9 @@ BusySection::BusySection ()
|
|||
m_previous_mode = mp_busy_mode->is_busy ();
|
||||
mp_busy_mode->enter_busy_mode (true);
|
||||
}
|
||||
|
||||
// disable file system watchers during busy periods
|
||||
tl::FileSystemWatcher::global_enable (false);
|
||||
}
|
||||
|
||||
BusySection::~BusySection ()
|
||||
|
|
@ -70,6 +74,8 @@ BusySection::~BusySection ()
|
|||
mp_busy_mode->enter_busy_mode (m_previous_mode);
|
||||
}
|
||||
mp_busy_mode = 0;
|
||||
|
||||
tl::FileSystemWatcher::global_enable (true);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
|||
|
|
@ -1177,11 +1177,12 @@ LayoutViewFunctions::do_cm_duplicate (bool interactive)
|
|||
db::Clipboard::instance ().swap (saved_clipboard);
|
||||
|
||||
try {
|
||||
bool transient_mode = ! view ()->has_selection ();
|
||||
view ()->copy_view_objects ();
|
||||
view ()->clear_selection ();
|
||||
view ()->cancel ();
|
||||
if (interactive) {
|
||||
view ()->paste_interactive ();
|
||||
view ()->paste_interactive (transient_mode);
|
||||
} else {
|
||||
view ()->paste ();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -272,6 +272,7 @@ PropertiesDialog::disconnect ()
|
|||
delete *p;
|
||||
}
|
||||
mp_properties_pages.clear ();
|
||||
m_index = -1;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -604,7 +605,7 @@ PropertiesDialog::ok_pressed ()
|
|||
{
|
||||
BEGIN_PROTECTED
|
||||
|
||||
if (! mp_properties_pages [m_index]->readonly ()) {
|
||||
if (m_index >= 0 && m_index < int (mp_properties_pages.size ()) && ! mp_properties_pages [m_index]->readonly ()) {
|
||||
|
||||
db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes")), m_transaction_id);
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ module LVS
|
|||
|
||||
class LVSExecutable < RBA::Executable
|
||||
|
||||
def initialize(macro, generator, l2ndb_index = nil)
|
||||
def initialize(macro, interpreter, generator, l2ndb_index = nil)
|
||||
|
||||
@lvs = LVSEngine::new
|
||||
@lvs._l2ndb_index = l2ndb_index
|
||||
|
|
@ -27,6 +27,8 @@ module LVS
|
|||
|
||||
@macro = macro
|
||||
|
||||
@interpreter = interpreter
|
||||
|
||||
end
|
||||
|
||||
def execute
|
||||
|
|
@ -38,9 +40,11 @@ module LVS
|
|||
|
||||
begin
|
||||
|
||||
encoded_file, expanded_text = @interpreter.include_expansion(@macro)
|
||||
|
||||
# No verbosity set in lvs engine - we cannot use the engine's logger
|
||||
RBA::Logger::verbosity >= 10 && RBA::Logger::info("Running #{@macro.path}")
|
||||
@lvs.instance_eval(@macro.text, @macro.path)
|
||||
@lvs.instance_eval(expanded_text, encoded_file)
|
||||
|
||||
rescue => ex
|
||||
|
||||
|
|
@ -66,13 +70,40 @@ module LVS
|
|||
|
||||
end
|
||||
|
||||
# A recipe implementation allowing the LVS run to be redone
|
||||
class LVSRecipe < RBA::Recipe
|
||||
|
||||
def initialize(interpreter)
|
||||
|
||||
super("lvs", "LVS recipe")
|
||||
|
||||
@interpreter = interpreter
|
||||
|
||||
end
|
||||
|
||||
def executable(params)
|
||||
|
||||
script = params["script"]
|
||||
if ! script
|
||||
return
|
||||
end
|
||||
|
||||
macro = RBA::Macro::macro_by_path(script)
|
||||
macro || raise("Can't find LVS script #{script} - unable to re-run")
|
||||
|
||||
LVSExecutable::new(macro, @interpreter, self.generator("script" => script), params["l2ndb_index"])
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# A DSL implementation for a LVS language (XML format)
|
||||
class LVSInterpreter < RBA::MacroInterpreter
|
||||
|
||||
# Constructor
|
||||
def initialize(recipe)
|
||||
def initialize
|
||||
|
||||
@recipe = recipe
|
||||
@recipe = LVSRecipe::new(self)
|
||||
|
||||
# Make the DSL use ruby syntax highlighting
|
||||
self.syntax_scheme = "ruby"
|
||||
|
|
@ -97,7 +128,7 @@ module LVS
|
|||
|
||||
# Implements the execute method
|
||||
def executable(macro)
|
||||
LVSExecutable::new(macro, @recipe.generator("script" => macro.path))
|
||||
LVSExecutable::new(macro, self, @recipe.generator("script" => macro.path))
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -106,9 +137,9 @@ module LVS
|
|||
class LVSPlainTextInterpreter < RBA::MacroInterpreter
|
||||
|
||||
# Constructor
|
||||
def initialize(recipe)
|
||||
def initialize
|
||||
|
||||
@recipe = recipe
|
||||
@recipe = LVSRecipe::new(self)
|
||||
|
||||
# Make the DSL use ruby syntax highlighting
|
||||
self.syntax_scheme = "ruby"
|
||||
|
|
@ -124,40 +155,14 @@ module LVS
|
|||
|
||||
# Implements the execute method
|
||||
def executable(macro)
|
||||
LVSExecutable::new(macro, @recipe.generator("script" => macro.path))
|
||||
LVSExecutable::new(macro, self, @recipe.generator("script" => macro.path))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# A recipe implementation allowing the LVS run to be redone
|
||||
class LVSRecipe < RBA::Recipe
|
||||
|
||||
def initialize
|
||||
super("lvs", "LVS recipe")
|
||||
end
|
||||
|
||||
def executable(params)
|
||||
|
||||
script = params["script"]
|
||||
if ! script
|
||||
return
|
||||
end
|
||||
|
||||
macro = RBA::Macro::macro_by_path(script)
|
||||
macro || raise("Can't find LVS script #{script} - unable to re-run")
|
||||
|
||||
LVSExecutable::new(macro, self.generator("script" => script), params["l2ndb_index"])
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Register the recipe
|
||||
lvs_recipe = LVSRecipe::new
|
||||
|
||||
# Register the new interpreters
|
||||
LVSInterpreter::new(lvs_recipe)
|
||||
LVSPlainTextInterpreter::new(lvs_recipe)
|
||||
LVSInterpreter::new
|
||||
LVSPlainTextInterpreter::new
|
||||
|
||||
# Creates a new macro category
|
||||
if RBA.constants.member?(:Application) && RBA::Application::instance
|
||||
|
|
|
|||
|
|
@ -247,6 +247,18 @@ private:
|
|||
bool m_supports_include_expansion;
|
||||
};
|
||||
|
||||
static std::vector<std::string>
|
||||
include_expansion (MacroInterpreterImpl *interp, lym::Macro *macro)
|
||||
{
|
||||
std::vector<std::string> res;
|
||||
|
||||
auto sp = interp->include_expansion (macro);
|
||||
res.push_back (sp.first);
|
||||
res.push_back (sp.second);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
gsi::EnumIn<lym::Macro, lym::Macro::Format> decl_FormatEnum ("lay", "Format",
|
||||
gsi::enum_const ("PlainTextFormat", lym::Macro::PlainTextFormat,
|
||||
"@brief The macro has plain text format"
|
||||
|
|
@ -298,6 +310,13 @@ Class<MacroInterpreterImpl> decl_MacroInterpreter ("lay", "MacroInterpreter",
|
|||
gsi::method ("NoDebugger", &const_NoDebugger,
|
||||
"@brief Indicates no debugging for \\debugger_scheme\n"
|
||||
) +
|
||||
gsi::method_ext ("include_expansion", &include_expansion, gsi::arg ("macro"),
|
||||
"@brief Provides include expansion as defined by the interpreter\n"
|
||||
"The return value will be a two-element array with the encoded file path "
|
||||
"and the include-expanded text.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.28.12."
|
||||
) +
|
||||
gsi::method ("register", &MacroInterpreterImpl::register_gsi, gsi::arg ("name"),
|
||||
"@brief Registers the macro interpreter\n"
|
||||
"@param name The interpreter name. This is an arbitrary string which should be unique.\n"
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
#include "tlProgress.h"
|
||||
#include "tlFileUtils.h"
|
||||
#include "tlResources.h"
|
||||
#include "tlEnv.h"
|
||||
|
||||
#include "rba.h"
|
||||
#include "pya.h"
|
||||
|
|
@ -262,7 +263,7 @@ MacroCollection::make_readonly (bool f)
|
|||
}
|
||||
|
||||
MacroCollection *
|
||||
MacroCollection::add_folder (const std::string &description, const std::string &p, const std::string &cat, bool readonly, bool force_create)
|
||||
MacroCollection::add_folder (const std::string &description, const std::string &p, const std::string &cat, bool readonly, bool auto_create)
|
||||
{
|
||||
if (! p.empty () && p[0] == ':') {
|
||||
|
||||
|
|
@ -278,7 +279,7 @@ MacroCollection::add_folder (const std::string &description, const std::string &
|
|||
if (! tl::file_exists (fp)) {
|
||||
|
||||
// Try to create the folder since it does not exist yet or skip that one
|
||||
if (! force_create) {
|
||||
if (readonly || ! auto_create) {
|
||||
|
||||
if (tl::verbosity () >= 20) {
|
||||
tl::log << tl::to_string (tr ("Folder does not exist - skipping: ")) << fp;
|
||||
|
|
@ -363,6 +364,22 @@ namespace {
|
|||
}
|
||||
#endif
|
||||
|
||||
// Some directories are ignored in addition to dotfiles
|
||||
static bool dir_is_ignored (const std::string &dn)
|
||||
{
|
||||
static std::set <std::string> ignored;
|
||||
static bool initialized = false;
|
||||
|
||||
if (! initialized) {
|
||||
// a colon-separated list of directory names
|
||||
std::string ign = tl::get_env ("KLAYOUT_IGNORE_MACRO_DIRS", "__pycache__");
|
||||
auto ignv = tl::split (ign, ":");
|
||||
ignored.insert (ignv.begin (), ignv.end ());
|
||||
}
|
||||
|
||||
return ignored.find (dn) != ignored.end ();
|
||||
}
|
||||
|
||||
void MacroCollection::scan ()
|
||||
{
|
||||
std::string p = path ();
|
||||
|
|
@ -431,6 +448,10 @@ void MacroCollection::scan ()
|
|||
continue;
|
||||
}
|
||||
|
||||
if (dir_is_ignored (*f)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
MacroCollection *&mc = m_folders.insert (std::make_pair (*f, (MacroCollection *) 0)).first->second;
|
||||
|
|
@ -831,7 +852,7 @@ void MacroCollection::reload (bool safe)
|
|||
|
||||
lym::MacroCollection new_collection;
|
||||
for (lym::MacroCollection::child_iterator c = begin_children (); c != end_children (); ++c) {
|
||||
new_collection.add_folder (c->second->description (), c->second->path (), c->second->category (), c->second->is_readonly (), false /* don't force to create */);
|
||||
new_collection.add_folder (c->second->description (), c->second->path (), c->second->category (), c->second->is_readonly (), false /* don't auto-create folder */);
|
||||
}
|
||||
|
||||
// and synchronize current with the actual one
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ public:
|
|||
* If force_create is true (the default), the folder will be created if it does not
|
||||
* exist yet. On error, 0 is returned.
|
||||
*/
|
||||
MacroCollection *add_folder (const std::string &description, const std::string &path, const std::string &category, bool readonly, bool force_create = true);
|
||||
MacroCollection *add_folder (const std::string &description, const std::string &path, const std::string &category, bool readonly, bool auto_create = true);
|
||||
|
||||
/**
|
||||
* @brief Gets the category tag of the collection
|
||||
|
|
|
|||
|
|
@ -130,31 +130,15 @@ MacroInterpreter::execute_macro (const lym::Macro *macro)
|
|||
|
||||
if (cls.current_name () == macro->dsl_interpreter ()) {
|
||||
|
||||
std::pair<std::string, std::string> et = cls->include_expansion (macro);
|
||||
if (et.first.empty () || et.first == macro->path ()) {
|
||||
|
||||
std::unique_ptr<tl::Executable> eo (cls->executable (macro));
|
||||
if (eo.get ()) {
|
||||
eo->do_execute ();
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// provide a copy which takes the include-expanded version
|
||||
lym::Macro tmp_macro;
|
||||
tmp_macro.assign (*macro);
|
||||
tmp_macro.set_text (et.second);
|
||||
tmp_macro.set_file_path (et.first);
|
||||
std::unique_ptr<tl::Executable> eo (cls->executable (&tmp_macro));
|
||||
if (eo.get ()) {
|
||||
eo->do_execute ();
|
||||
}
|
||||
|
||||
std::unique_ptr<tl::Executable> eo (cls->executable (macro));
|
||||
if (eo.get ()) {
|
||||
eo->do_execute ();
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
throw tl::Exception (tl::to_string (tr ("No interpreter registered for DSL type '")) + macro->dsl_interpreter () + "'");
|
||||
|
|
|
|||
|
|
@ -19,13 +19,15 @@ module D25
|
|||
|
||||
class D25Executable < RBA::Executable
|
||||
|
||||
def initialize(macro, generator)
|
||||
def initialize(macro, interpreter, generator)
|
||||
|
||||
@d25 = D25Engine::new
|
||||
@d25._generator = generator
|
||||
|
||||
@macro = macro
|
||||
|
||||
@interpreter = interpreter
|
||||
|
||||
end
|
||||
|
||||
def execute
|
||||
|
|
@ -37,9 +39,11 @@ module D25
|
|||
|
||||
begin
|
||||
|
||||
encoded_file, expanded_text = @interpreter.include_expansion(@macro)
|
||||
|
||||
# No verbosity set in d25 engine - we cannot use the engine's logger
|
||||
RBA::Logger::verbosity >= 10 && RBA::Logger::info("Running #{@macro.path}")
|
||||
@d25.instance_eval(@macro.text, @macro.path)
|
||||
@d25.instance_eval(expanded_text, encoded_file)
|
||||
|
||||
rescue => ex
|
||||
|
||||
|
|
@ -67,13 +71,40 @@ module D25
|
|||
|
||||
end
|
||||
|
||||
# A recipe implementation allowing the D25 run to be redone
|
||||
class D25Recipe < RBA::Recipe
|
||||
|
||||
def initialize(interpreter)
|
||||
|
||||
super("d25", "D25 recipe")
|
||||
|
||||
@interpreter = interpreter
|
||||
|
||||
end
|
||||
|
||||
def executable(params)
|
||||
|
||||
script = params["script"]
|
||||
if ! script
|
||||
return
|
||||
end
|
||||
|
||||
macro = RBA::Macro::macro_by_path(script)
|
||||
macro || raise("Can't find D25 script #{script} - unable to re-run")
|
||||
|
||||
D25Executable::new(macro, @interpreter, self.generator("script" => script))
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# A DSL implementation for a D25 language (XML format)
|
||||
class D25Interpreter < RBA::MacroInterpreter
|
||||
|
||||
# Constructor
|
||||
def initialize(recipe)
|
||||
def initialize
|
||||
|
||||
@recipe = recipe
|
||||
@recipe = D25Recipe::new(self)
|
||||
|
||||
# Make the DSL use ruby syntax highlighting
|
||||
self.syntax_scheme = "ruby"
|
||||
|
|
@ -98,7 +129,7 @@ module D25
|
|||
|
||||
# Implements the execute method
|
||||
def executable(macro)
|
||||
D25Executable::new(macro, @recipe.generator("script" => macro.path))
|
||||
D25Executable::new(macro, self, @recipe.generator("script" => macro.path))
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -107,9 +138,9 @@ module D25
|
|||
class D25PlainTextInterpreter < RBA::MacroInterpreter
|
||||
|
||||
# Constructor
|
||||
def initialize(recipe)
|
||||
def initialize
|
||||
|
||||
@recipe = recipe
|
||||
@recipe = D25Recipe::new(self)
|
||||
|
||||
# Make the DSL use ruby syntax highlighting
|
||||
self.syntax_scheme = "ruby"
|
||||
|
|
@ -125,40 +156,14 @@ module D25
|
|||
|
||||
# Implements the execute method
|
||||
def executable(macro)
|
||||
D25Executable::new(macro, @recipe.generator("script" => macro.path))
|
||||
D25Executable::new(macro, self, @recipe.generator("script" => macro.path))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# A recipe implementation allowing the D25 run to be redone
|
||||
class D25Recipe < RBA::Recipe
|
||||
|
||||
def initialize
|
||||
super("d25", "D25 recipe")
|
||||
end
|
||||
|
||||
def executable(params)
|
||||
|
||||
script = params["script"]
|
||||
if ! script
|
||||
return
|
||||
end
|
||||
|
||||
macro = RBA::Macro::macro_by_path(script)
|
||||
macro || raise("Can't find D25 script #{script} - unable to re-run")
|
||||
|
||||
D25Executable::new(macro, self.generator("script" => script))
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Register the recipe
|
||||
d25_recipe = D25Recipe::new
|
||||
|
||||
# Register the new interpreters
|
||||
D25Interpreter::new(d25_recipe)
|
||||
D25PlainTextInterpreter::new(d25_recipe)
|
||||
D25Interpreter::new
|
||||
D25PlainTextInterpreter::new
|
||||
|
||||
# Creates a new macro category
|
||||
if RBA::Application::instance
|
||||
|
|
|
|||
|
|
@ -109,13 +109,13 @@ public:
|
|||
{
|
||||
while (frame != NULL) {
|
||||
|
||||
#if PY_VERSION_HEX >= 0x030B0000
|
||||
#if PY_VERSION_HEX >= 0x030A0000
|
||||
int line = PyFrame_GetLineNumber(frame);
|
||||
#else
|
||||
int line = frame->f_lineno;
|
||||
#endif
|
||||
std::string fn;
|
||||
#if PY_VERSION_HEX >= 0x030B0000
|
||||
#if PY_VERSION_HEX >= 0x030A0000
|
||||
if (test_type<std::string> (PyFrame_GetCode(frame)->co_filename, true)) {
|
||||
fn = normalize_path (python2c<std::string> (PyFrame_GetCode(frame)->co_filename));
|
||||
#else
|
||||
|
|
@ -125,7 +125,7 @@ public:
|
|||
}
|
||||
m_stack_trace.push_back (tl::BacktraceElement (fn, line));
|
||||
|
||||
#if PY_VERSION_HEX >= 0x030B0000
|
||||
#if PY_VERSION_HEX >= 0x030A0000
|
||||
frame = PyFrame_GetBack(frame);
|
||||
#else
|
||||
frame = frame->f_back;
|
||||
|
|
|
|||
|
|
@ -115,13 +115,15 @@ rba_get_backtrace_from_array (VALUE backtrace, std::vector<tl::BacktraceElement>
|
|||
void
|
||||
block_exceptions (bool f)
|
||||
{
|
||||
RubyInterpreter::instance ()->block_exceptions (f);
|
||||
if (RubyInterpreter::instance ()) {
|
||||
RubyInterpreter::instance ()->block_exceptions (f);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
exceptions_blocked ()
|
||||
{
|
||||
return RubyInterpreter::instance ()->exceptions_blocked ();
|
||||
return RubyInterpreter::instance () ? RubyInterpreter::instance ()->exceptions_blocked () : false;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -161,8 +163,10 @@ rba_check_error ()
|
|||
std::vector <tl::BacktraceElement> bt;
|
||||
rba_get_backtrace_from_array (rb_funcall (lasterr, rb_intern ("backtrace"), 0), bt, 0);
|
||||
|
||||
const std::string &ds = RubyInterpreter::instance ()->debugger_scope ();
|
||||
bt.erase (bt.begin (), bt.begin () + RubyStackTraceProvider::scope_index (bt, ds));
|
||||
if (RubyInterpreter::instance ()) {
|
||||
const std::string &ds = RubyInterpreter::instance ()->debugger_scope ();
|
||||
bt.erase (bt.begin (), bt.begin () + RubyStackTraceProvider::scope_index (bt, ds));
|
||||
}
|
||||
|
||||
// parse the backtrace to get the line number
|
||||
tl::BacktraceElement info;
|
||||
|
|
|
|||
|
|
@ -31,6 +31,9 @@
|
|||
namespace tl
|
||||
{
|
||||
|
||||
// The global enable counter (<0: disable)
|
||||
static int s_global_enable = 0;
|
||||
|
||||
// The maximum allowed processing time in seconds
|
||||
const double processing_time = 0.02;
|
||||
|
||||
|
|
@ -47,6 +50,16 @@ FileSystemWatcher::FileSystemWatcher (QObject *parent)
|
|||
m_batch_size = 1000;
|
||||
}
|
||||
|
||||
void
|
||||
FileSystemWatcher::global_enable (bool en)
|
||||
{
|
||||
if (en) {
|
||||
++s_global_enable;
|
||||
} else {
|
||||
--s_global_enable;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FileSystemWatcher::enable (bool en)
|
||||
{
|
||||
|
|
@ -120,6 +133,10 @@ FileSystemWatcher::remove_file (const std::string &path)
|
|||
void
|
||||
FileSystemWatcher::timeout ()
|
||||
{
|
||||
if (s_global_enable < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
tl::Clock start = tl::Clock::current ();
|
||||
|
||||
if (m_iter == m_files.end ()) {
|
||||
|
|
|
|||
|
|
@ -57,6 +57,11 @@ public:
|
|||
*/
|
||||
FileSystemWatcher (QObject *parent = 0);
|
||||
|
||||
/**
|
||||
* @brief Global enable/disable
|
||||
*/
|
||||
static void global_enable (bool en);
|
||||
|
||||
/**
|
||||
* @brief Enables or disables the file watcher
|
||||
*/
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -113,8 +113,13 @@ class DBEdgePairs_TestClass < TestBase
|
|||
r2.insert(RBA::Edge::new(1, 1, 1, 101), RBA::Edge::new(-11, 1, -21, 51))
|
||||
|
||||
assert_equal(csort((r1 + r2).to_s), csort("(0,0;0,100)/(-10,0;-20,50);(0,1;0,101)/(-10,1;-20,51);(1,0;1,100)/(-11,0;-21,50);(1,1;1,101)/(-11,1;-21,51)"))
|
||||
r1 += r2
|
||||
assert_equal(csort(r1.to_s), csort("(0,0;0,100)/(-10,0;-20,50);(0,1;0,101)/(-10,1;-20,51);(1,0;1,100)/(-11,0;-21,50);(1,1;1,101)/(-11,1;-21,51)"))
|
||||
assert_equal(csort((r1.join(r2)).to_s), csort("(0,0;0,100)/(-10,0;-20,50);(0,1;0,101)/(-10,1;-20,51);(1,0;1,100)/(-11,0;-21,50);(1,1;1,101)/(-11,1;-21,51)"))
|
||||
rr1 = r1.dup
|
||||
rr1 += r2
|
||||
assert_equal(csort(rr1.to_s), csort("(0,0;0,100)/(-10,0;-20,50);(0,1;0,101)/(-10,1;-20,51);(1,0;1,100)/(-11,0;-21,50);(1,1;1,101)/(-11,1;-21,51)"))
|
||||
rr1 = r1.dup
|
||||
rr1.join_with(r2)
|
||||
assert_equal(csort(rr1.to_s), csort("(0,0;0,100)/(-10,0;-20,50);(0,1;0,101)/(-10,1;-20,51);(1,0;1,100)/(-11,0;-21,50);(1,1;1,101)/(-11,1;-21,51)"))
|
||||
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -315,6 +315,8 @@ class DBEdges_TestClass < TestBase
|
|||
|
||||
r = r1 + r2
|
||||
assert_equal(csort(r.to_s), csort("(0,0;100,0);(50,0;200,0)"))
|
||||
r = r1.join(r2)
|
||||
assert_equal(csort(r.to_s), csort("(0,0;100,0);(50,0;200,0)"))
|
||||
assert_equal(r.merged.to_s, "(0,0;200,0)")
|
||||
r.merge
|
||||
assert_equal(r.is_merged?, true)
|
||||
|
|
@ -322,9 +324,14 @@ class DBEdges_TestClass < TestBase
|
|||
r = r1.dup
|
||||
r += r2
|
||||
assert_equal(csort(r.to_s), csort("(0,0;100,0);(50,0;200,0)"))
|
||||
r = r1.dup
|
||||
r.join_with(r2)
|
||||
assert_equal(csort(r.to_s), csort("(0,0;100,0);(50,0;200,0)"))
|
||||
|
||||
r = r1 | r2
|
||||
assert_equal(r.to_s, "(0,0;200,0)")
|
||||
r = r1.or(r2)
|
||||
assert_equal(r.to_s, "(0,0;200,0)")
|
||||
assert_equal(r.merged.to_s, "(0,0;200,0)")
|
||||
r.merge
|
||||
assert_equal(r.is_merged?, true)
|
||||
|
|
@ -332,9 +339,14 @@ class DBEdges_TestClass < TestBase
|
|||
r = r1.dup
|
||||
r |= r2
|
||||
assert_equal(r.to_s, "(0,0;200,0)")
|
||||
r = r1.dup
|
||||
r.or_with(r2)
|
||||
assert_equal(r.to_s, "(0,0;200,0)")
|
||||
|
||||
r = r1 & r2
|
||||
assert_equal(r.to_s, "(50,0;100,0)")
|
||||
r = r1.and(r2)
|
||||
assert_equal(r.to_s, "(50,0;100,0)")
|
||||
assert_equal(r.is_merged?, true)
|
||||
r = r1.andnot(r2)[0]
|
||||
assert_equal(r.to_s, "(50,0;100,0)")
|
||||
|
|
@ -342,9 +354,14 @@ class DBEdges_TestClass < TestBase
|
|||
r = r1.dup
|
||||
r &= r2
|
||||
assert_equal(r.to_s, "(50,0;100,0)")
|
||||
r = r1.dup
|
||||
r.and_with(r2)
|
||||
assert_equal(r.to_s, "(50,0;100,0)")
|
||||
|
||||
r = r1 - r2
|
||||
assert_equal(r.to_s, "(0,0;50,0)")
|
||||
r = r1.not(r2)
|
||||
assert_equal(r.to_s, "(0,0;50,0)")
|
||||
assert_equal(r.is_merged?, true)
|
||||
r = r1.andnot(r2)[1]
|
||||
assert_equal(r.to_s, "(0,0;50,0)")
|
||||
|
|
@ -352,13 +369,21 @@ class DBEdges_TestClass < TestBase
|
|||
r = r1.dup
|
||||
r -= r2
|
||||
assert_equal(r.to_s, "(0,0;50,0)")
|
||||
r = r1.dup
|
||||
r.not_with(r2)
|
||||
assert_equal(r.to_s, "(0,0;50,0)")
|
||||
|
||||
r = r1 ^ r2
|
||||
assert_equal(csort(r.to_s), csort("(0,0;50,0);(100,0;200,0)"))
|
||||
r = r1.xor(r2)
|
||||
assert_equal(csort(r.to_s), csort("(0,0;50,0);(100,0;200,0)"))
|
||||
assert_equal(r.is_merged?, true)
|
||||
r = r1.dup
|
||||
r ^= r2
|
||||
assert_equal(csort(r.to_s), csort("(0,0;50,0);(100,0;200,0)"))
|
||||
r = r1.dup
|
||||
r.xor_with(r2)
|
||||
assert_equal(csort(r.to_s), csort("(0,0;50,0);(100,0;200,0)"))
|
||||
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -274,34 +274,54 @@ class DBRegion_TestClass < TestBase
|
|||
r2 = RBA::Region::new(RBA::Box::new(-10, -20, 80, 160))
|
||||
|
||||
assert_equal((r1 & r2).to_s, "(10,20;10,160;80,160;80,20)")
|
||||
assert_equal((r1.and(r2)).to_s, "(10,20;10,160;80,160;80,20)")
|
||||
assert_equal(r1.andnot(r2).count, 2)
|
||||
assert_equal(r1.andnot(r2)[0].to_s, "(10,20;10,160;80,160;80,20)")
|
||||
rr = r1.dup
|
||||
rr &= r2
|
||||
assert_equal(rr.to_s, "(10,20;10,160;80,160;80,20)")
|
||||
rr = r1.dup
|
||||
rr.and_with(r2)
|
||||
assert_equal(rr.to_s, "(10,20;10,160;80,160;80,20)")
|
||||
|
||||
assert_equal((r1 - r2).to_s, "(80,20;80,160;10,160;10,200;100,200;100,20)")
|
||||
assert_equal((r1.not(r2)).to_s, "(80,20;80,160;10,160;10,200;100,200;100,20)")
|
||||
assert_equal(r1.andnot(r2)[1].to_s, "(80,20;80,160;10,160;10,200;100,200;100,20)")
|
||||
rr = r1.dup
|
||||
rr -= r2
|
||||
assert_equal(rr.to_s, "(80,20;80,160;10,160;10,200;100,200;100,20)")
|
||||
rr = r1.dup
|
||||
rr.not_with(r2)
|
||||
assert_equal(rr.to_s, "(80,20;80,160;10,160;10,200;100,200;100,20)")
|
||||
|
||||
assert_equal((r1 ^ r2).to_s, "(-10,-20;-10,160;10,160;10,200;100,200;100,20;80,20;80,-20/10,20;80,20;80,160;10,160)")
|
||||
assert_equal((r1.xor(r2)).to_s, "(-10,-20;-10,160;10,160;10,200;100,200;100,20;80,20;80,-20/10,20;80,20;80,160;10,160)")
|
||||
r1.min_coherence = true
|
||||
assert_equal(csort((r1 ^ r2).to_s), csort("(-10,-20;-10,160;10,160;10,20;80,20;80,-20);(80,20;80,160;10,160;10,200;100,200;100,20)"))
|
||||
rr = r1.dup
|
||||
rr ^= r2
|
||||
assert_equal(csort(rr.to_s), csort("(-10,-20;-10,160;10,160;10,20;80,20;80,-20);(80,20;80,160;10,160;10,200;100,200;100,20)"))
|
||||
rr = r1.dup
|
||||
rr.xor_with(r2)
|
||||
assert_equal(csort(rr.to_s), csort("(-10,-20;-10,160;10,160;10,20;80,20;80,-20);(80,20;80,160;10,160;10,200;100,200;100,20)"))
|
||||
|
||||
assert_equal(csort((r1 + r2).to_s), csort("(10,20;10,200;100,200;100,20);(-10,-20;-10,160;80,160;80,-20)"))
|
||||
assert_equal(csort((r1.join(r2)).to_s), csort("(10,20;10,200;100,200;100,20);(-10,-20;-10,160;80,160;80,-20)"))
|
||||
rr = r1.dup
|
||||
rr += r2
|
||||
assert_equal(csort(rr.to_s), csort("(10,20;10,200;100,200;100,20);(-10,-20;-10,160;80,160;80,-20)"))
|
||||
rr = r1.dup
|
||||
rr.join_with(r2)
|
||||
assert_equal(csort(rr.to_s), csort("(10,20;10,200;100,200;100,20);(-10,-20;-10,160;80,160;80,-20)"))
|
||||
|
||||
assert_equal((r1 | r2).to_s, "(-10,-20;-10,160;10,160;10,200;100,200;100,20;80,20;80,-20)")
|
||||
assert_equal((r1.or(r2)).to_s, "(-10,-20;-10,160;10,160;10,200;100,200;100,20;80,20;80,-20)")
|
||||
rr = r1.dup
|
||||
rr |= r2
|
||||
assert_equal(rr.to_s, "(-10,-20;-10,160;10,160;10,200;100,200;100,20;80,20;80,-20)")
|
||||
rr = r1.dup
|
||||
rr.or_with(r2)
|
||||
assert_equal(rr.to_s, "(-10,-20;-10,160;10,160;10,200;100,200;100,20;80,20;80,-20)")
|
||||
|
||||
assert_equal((r1 + r2).sized(10).to_s, "(-20,-30;-20,170;0,170;0,210;110,210;110,10;90,10;90,-30)")
|
||||
rr = (r1 | r2).dup
|
||||
|
|
|
|||
|
|
@ -114,7 +114,12 @@ class DBTexts_TestClass < TestBase
|
|||
r1.insert(RBA::Text::new("uvm", RBA::Trans::new(RBA::Vector::new(111, 211))))
|
||||
|
||||
assert_equal(csort((r1 + r2).to_s), csort("('abc',r0 100,-200);('uvm',r0 110,210);('abc',r0 101,-201);('uvm',r0 111,211)"))
|
||||
r1 += r2
|
||||
assert_equal(csort((r1.join(r2)).to_s), csort("('abc',r0 100,-200);('uvm',r0 110,210);('abc',r0 101,-201);('uvm',r0 111,211)"))
|
||||
rr1 = r1.dup
|
||||
rr1 += r2
|
||||
assert_equal(csort(r1.to_s), csort("('abc',r0 100,-200);('uvm',r0 110,210);('abc',r0 101,-201);('uvm',r0 111,211)"))
|
||||
rr1 = r1.dup
|
||||
rr1.join_with(r2)
|
||||
assert_equal(csort(r1.to_s), csort("('abc',r0 100,-200);('uvm',r0 110,210);('abc',r0 101,-201);('uvm',r0 111,211)"))
|
||||
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue