Macro file: flatten.lym
This application provides two new toolbar entries bound to keys F7 and F8. The first function brings up all selected shapes and instances to the current cell level and removes them from their original cell. This makes sense only if the selection contains objects from subcells (hence not in "top level only" selection mode). The second function brings up such objects one level in hierarchy. Both functions just bring up objects along the selection path, not into all instances of the selected cell. They are very similar to the "Move up in hierarchy" function in the "Edit/Selection" menu.
The new functions can only be used in "Edit" mode and require version 0.16 or later.
This code demonstrates in particular: <ul> <li>How to use the selection set of objects</li> <li>How to modify geometrical objects (transform, erase, copy)</li> <li>How to implement undo/redo support, which is pretty simple using the LayoutView's "transaction" and "commit" methods</li> </ul>
module Examples # HINT: this simple implementation does not account # for variant building - the shapes and instances are simply taken # out from the original shapes and therefore might disappear somewhere else. @f7_handler = RBA::Action.new @f7_handler.title = "Flatten" @f7_handler.shortcut = "F7" @f7_handler.on_triggered do app = RBA::Application.instance mw = app.main_window lv = mw.current_view if lv == nil raise "Flatten: No view selected" end # start transaction for "undo" lv.transaction("Flatten") begin # because objects might be referenced multiple times (thru different hierarchy paths) # we must first copy and then delete them # copy & transform them lv.each_object_selected do |sel| cv = lv.cellview(sel.cv_index) target = cv.cell # only if not flat already .. if target.cell_index != sel.cell_index source = cv.layout.cell(sel.cell_index) if sel.is_cell_inst? # copy and transform new_inst = target.insert(sel.inst) target.transform(new_inst, sel.source_trans) else # copy and transform target_shapes = target.shapes(sel.layer) new_shape = target_shapes.insert(sel.shape) target_shapes.transform(new_shape, sel.source_trans) end end end # delete the objects # HINT: since it is possible that a certain object is used multiple times, we need to test # each reference, if it is still valid (i.e. the object has not been deleted yet). lv.each_object_selected do |sel| cv = lv.cellview(sel.cv_index) target = cv.cell # only if not flat already .. if target.cell_index != sel.cell_index source = cv.layout.cell(sel.cell_index) if sel.is_cell_inst? if source.is_valid?(sel.inst) source.erase(sel.inst) end else if source.shapes(sel.layer).is_valid?(sel.shape) source.shapes(sel.layer).erase(sel.shape) end end end end ensure # always execute that code: # commit transaction lv.commit # clear selection and cancel all other edit operations, so # nothing refers to shapes that might have been deleted. lv.cancel end end # HINT: this simple implementation does not account # for variant building - the shapes and instances are simply taken # out from the original shapes and therefore might disappear somewhere else. @f8_handler = RBA::Action.new @f8_handler.title = "Propagate" @f8_handler.shortcut = "F8" @f8_handler.on_triggered do app = RBA::Application.instance mw = app.main_window lv = mw.current_view if lv == nil raise "Propagate: No view selected" end # start transaction for "undo" lv.transaction("Propagate") begin # because objects might be referenced multiple times (thru different hierarchy paths) # we must first copy and then delete them # copy & transform them lv.each_object_selected do |sel| cv = lv.cellview(sel.cv_index) # only if not flat already .. if cv.cell_index != sel.cell_index source = cv.layout.cell(sel.cell_index) if sel.is_cell_inst? # copy and transform if sel.path_length <= 2 target = cv.cell else target = cv.layout.cell(sel.path_nth(sel.path_length - 3).cell_inst.cell_index) end new_inst = target.insert(sel.inst) target.transform(new_inst, sel.path_nth(sel.path_length - 2).specific_cplx_trans) else # copy and transform if sel.path_length <= 1 target = cv.cell else target = cv.layout.cell(sel.path_nth(sel.path_length - 2).cell_inst.cell_index) end target_shapes = target.shapes(sel.layer) new_shape = target_shapes.insert(sel.shape) target_shapes.transform(new_shape, sel.path_nth(sel.path_length - 1).specific_cplx_trans) end end end # delete the objects # HINT: since it is possible that a certain object is used multiple times, we need to test # each reference, if it is still valid (i.e. the object has not been deleted yet). lv.each_object_selected do |sel| cv = lv.cellview(sel.cv_index) target = cv.cell # only if not flat already .. if target.cell_index != sel.cell_index source = cv.layout.cell(sel.cell_index) if sel.is_cell_inst? if source.is_valid?(sel.inst) source.erase(sel.inst) end else if source.shapes(sel.layer).is_valid?(sel.shape) source.shapes(sel.layer).erase(sel.shape) end end end end ensure # always execute that code: # commit transaction lv.commit # clear selection and cancel all other edit operations, so # nothing refers to shapes that might have been deleted. lv.cancel end end app = RBA::Application.instance mw = app.main_window menu = mw.menu menu.insert_separator("@toolbar.end", "name") menu.insert_item("@toolbar.end", "rba_flatten", @f7_handler) menu.insert_item("@toolbar.end", "rba_propagate", @f8_handler) end