diff --git a/src/laybasic/laybasic/gsiDeclLayCellView.cc b/src/laybasic/laybasic/gsiDeclLayCellView.cc new file mode 100644 index 000000000..95672c7bd --- /dev/null +++ b/src/laybasic/laybasic/gsiDeclLayCellView.cc @@ -0,0 +1,413 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2026 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "gsiDecl.h" +#include "gsiSignals.h" + +#include "layCellView.h" +#include "layLayoutViewBase.h" +#include "layLayoutHandle.h" + +namespace gsi +{ + +static db::Layout *get_layout (const lay::CellViewRef *cv) +{ + if (cv->handle ()) { + return &cv->handle ()->layout (); + } else { + return 0; + } +} + +static lay::LayoutHandleRef *get_layout_handle (const lay::CellViewRef *cv) +{ + if (cv->handle ()) { + return new lay::LayoutHandleRef (cv->handle ()); + } else { + return 0; + } +} + +static std::string name (const lay::CellViewRef *cv) +{ + if (cv->handle ()) { + return cv->handle ()->name (); + } else { + return std::string (); + } +} + +static void set_name (lay::CellViewRef *cv, const std::string &name) +{ + cv->set_name (name); +} + +static std::string filename (const lay::CellViewRef *cv) +{ + if (cv->handle ()) { + return cv->handle ()->filename (); + } else { + return std::string (); + } +} + +static bool is_dirty (const lay::CellViewRef *cv) +{ + if (cv->handle ()) { + return cv->handle ()->is_dirty (); + } else { + return false; + } +} + +static void apply_technology (const lay::CellViewRef *cv, const std::string &tech) +{ + if (cv->handle ()) { + cv->handle ()->apply_technology (tech); + } +} + +static std::string get_technology (const lay::CellViewRef *cv) +{ + if (cv->handle ()) { + return cv->handle ()->tech_name (); + } else { + return std::string (); + } +} + +static tl::Event &get_technology_changed_event (lay::CellViewRef *cv) +{ + if (! cv->is_valid ()) { + throw tl::Exception (tl::to_string (tr ("Not a valid cellview"))); + } + return (*cv)->technology_changed_event; +} + +static void set_cell (lay::CellViewRef *cv, db::Cell *cell) +{ + if (! cell) { + cv->reset_cell (); + } else { + cv->set_cell (cell->cell_index ()); + } +} + +static void close_cellview (lay::CellViewRef *cv) +{ + if (cv->is_valid ()) { + cv->view ()->erase_cellview (cv->index ()); + } +} + +static std::string get_cell_name (const lay::CellViewRef *cv) +{ + if (cv->cell () == 0) { + return std::string (); + } else { + return (*cv)->layout ().cell_name (cv->cell_index ()); + } +} + +static void cv_descend (lay::CellViewRef *cv, const std::vector &path) +{ + if (cv->is_valid ()) { + cv->view ()->descend (path, cv->index ()); + } +} + +static void cv_ascend (lay::CellViewRef *cv) +{ + if (cv->is_valid ()) { + cv->view ()->ascend (cv->index ()); + } +} + +static bool cv_is_cell_hidden (lay::CellViewRef *cv, const db::Cell *cell) +{ + if (cv->is_valid () && cell) { + if (cell->layout () != &(*cv)->layout ()) { + throw tl::Exception (tl::to_string (tr ("The cell is not a cell of the view's layout"))); + } + return cv->view ()->is_cell_hidden (cell->cell_index (), cv->index ()); + } else { + return false; + } +} + +static void cv_hide_cell (lay::CellViewRef *cv, const db::Cell *cell) +{ + if (cv->is_valid () && cell) { + if (cell->layout () != &(*cv)->layout ()) { + throw tl::Exception (tl::to_string (tr ("The cell is not a cell of the view's layout"))); + } + cv->view ()->hide_cell (cell->cell_index (), cv->index ()); + } +} + +static void cv_show_cell (lay::CellViewRef *cv, const db::Cell *cell) +{ + if (cv->is_valid () && cell) { + if (cell->layout () != &(*cv)->layout ()) { + throw tl::Exception (tl::to_string (tr ("The cell is not a cell of the view's layout"))); + } + cv->view ()->show_cell (cell->cell_index (), cv->index ()); + } +} + +static void cv_show_all_cells (lay::CellViewRef *cv) +{ + if (cv->is_valid ()) { + cv->view ()->show_all_cells (cv->index ()); + } +} + +Class decl_CellView ("lay", "CellView", + method ("==", static_cast (&lay::CellViewRef::operator==), gsi::arg ("other"), + "@brief Equality: indicates whether the cellviews refer to the same one\n" + "In version 0.25, the definition of the equality operator has been changed to reflect identity of the " + "cellview. Before that version, identity of the cell shown was implied." + ) + + method ("index", &lay::CellViewRef::index, + "@brief Gets the index of this cellview in the layout view\n" + "The index will be negative if the cellview is not a valid one.\n" + "This method has been added in version 0.25.\n" + ) + + method ("is_valid?", &lay::CellViewRef::is_valid, + "@brief Returns true, if the cellview is valid\n" + "A cellview may become invalid if the corresponding tab is closed for example." + ) + + method ("path=|set_path", &lay::CellViewRef::set_unspecific_path, gsi::arg ("path"), + "@brief Sets the unspecific part of the path explicitly\n" + "\n" + "Setting the unspecific part of the path will clear the context path component and\n" + "update the context and target cell.\n" + ) + + method ("context_path=|set_context_path", &lay::CellViewRef::set_specific_path, gsi::arg ("path"), + "@brief Sets the context path explicitly\n" + "\n" + "This method assumes that the unspecific part of the path \n" + "is established already and that the context path starts\n" + "from the context cell.\n" + ) + + method ("cell_index=|set_cell", (void (lay::CellViewRef::*) (lay::CellViewRef::cell_index_type)) &lay::CellViewRef::set_cell, gsi::arg ("cell_index"), + "@brief Sets the path to the given cell\n" + "\n" + "This method will construct any path to this cell, not a \n" + "particular one. It will clear the context path\n" + "and update the context and target cell. Note that the cell is specified by its index.\n" + ) + + method ("cell_name=|set_cell_name", (void (lay::CellViewRef::*) (const std::string &)) &lay::CellViewRef::set_cell, gsi::arg ("cell_name"), + "@brief Sets the cell by name\n" + "\n" + "If the name is not a valid one, the cellview will become\n" + "invalid.\n" + "This method will construct any path to this cell, not a \n" + "particular one. It will clear the context path\n" + "and update the context and target cell.\n" + ) + + method_ext ("cell=", set_cell, gsi::arg ("cell"), + "@brief Sets the cell by reference to a Cell object\n" + "Setting the cell reference to nil invalidates the cellview. " + "This method will construct any path to this cell, not a \n" + "particular one. It will clear the context path\n" + "and update the context and target cell.\n" + ) + + method ("reset_cell", &lay::CellViewRef::reset_cell, + "@brief Resets the cell \n" + "\n" + "The cellview will become invalid. The layout object will\n" + "still be attached to the cellview, but no cell will be selected.\n" + ) + + method ("ctx_cell_index", &lay::CellViewRef::ctx_cell_index, + "@brief Gets the context cell's index\n" + ) + + method ("ctx_cell", &lay::CellViewRef::ctx_cell, + "@brief Gets the reference to the context cell currently addressed\n" + ) + + method ("cell_index", &lay::CellViewRef::cell_index, + "@brief Gets the target cell's index\n" + ) + + method ("cell", &lay::CellViewRef::cell, + "@brief Gets the reference to the target cell currently addressed\n" + ) + + method_ext ("cell_name", &get_cell_name, + "@brief Gets the name of the target cell currently addressed\n" + ) + + method_ext ("filename", &gsi::filename, + "@brief Gets filename associated with the layout behind the cellview\n" + ) + + method_ext ("is_dirty?", &gsi::is_dirty, + "@brief Gets a flag indicating whether the layout needs saving\n" + "A layout is 'dirty' if it is modified and needs saving. This method returns " + "true in this case.\n" + "\n" + "This method has been introduced in version 0.24.10.\n" + ) + + method_ext ("name", &gsi::name, + "@brief Gets the unique name associated with the layout behind the cellview\n" + ) + + method_ext ("name=", &gsi::set_name, gsi::arg("name"), + "@brief sets the unique name associated with the layout behind the cellview\n" + "\n" + "this method has been introduced in version 0.25." + ) + + method ("path", &lay::CellViewRef::unspecific_path, + "@brief Gets the cell's unspecific part of the path leading to the context cell\n" + ) + + method ("context_path", &lay::CellViewRef::specific_path, + "@brief Gets the cell's context path\n" + "The context path leads from the context cell to the target cell in a specific " + "fashion, i.e. describing each instance in detail, not just by cell indexes. If " + "the context and target cell are identical, the context path is empty." + ) + + method ("context_trans", &lay::CellViewRef::context_trans, + "@brief Gets the accumulated transformation of the context path\n" + "This is the transformation applied to the target cell before it is shown in the context cell\n" + "Technically this is the product of all transformations over the context path.\n" + "See \\context_dtrans for a version delivering a micron-unit space transformation.\n" + "\n" + "This method has been introduced in version 0.27.3.\n" + ) + + method ("context_dtrans", &lay::CellViewRef::context_dtrans, + "@brief Gets the accumulated transformation of the context path in micron unit space\n" + "This is the transformation applied to the target cell before it is shown in the context cell\n" + "Technically this is the product of all transformations over the context path.\n" + "See \\context_trans for a version delivering an integer-unit space transformation.\n" + "\n" + "This method has been introduced in version 0.27.3.\n" + ) + + event_ext ("on_technology_changed", &get_technology_changed_event, + "@brief An event indicating that the technology has changed\n" + "This event is triggered when the CellView is attached to a different technology.\n" + "\n" + "This event has been introduced in version 0.27.\n" + ) + + method_ext ("technology", &get_technology, + "@brief Returns the technology name for the layout behind the given cell view\n" + "This method has been added in version 0.23.\n" + ) + + method_ext ("technology=", &apply_technology, gsi::arg ("tech_name"), + "@brief Sets the technology for the layout behind the given cell view\n" + "According to the specification of the technology, new layer properties may be loaded " + "or the net tracer may be reconfigured. If the layout is shown in multiple views, the " + "technology is updated for all views.\n" + "This method has been added in version 0.22.\n" + ) + + method_ext ("layout", &get_layout, + "@brief Gets the reference to the layout object addressed by this view\n" + ) + + factory_ext ("layout_handle", &get_layout_handle, + "@brief Gets the handle to the layout object addressed by this cell view\n" + "A layout handle is a reference-counted pointer to a layout object and allows sharing this " + "resource among different views or objects. By holding a handle, the layout object is not " + "destroyed when the view closes for example.\n" + "\n" + "This method has been added in version 0.30.7.\n" + ) + + method_ext ("descend", &cv_descend, gsi::arg ("path"), + "@brief Descends further into the hierarchy.\n" + "Adds the given path (given as an array of InstElement objects) to the specific path of the " + "cellview with the given index. In effect, the cell addressed by the terminal of the new path " + "components can be shown in the context of the upper cells, if the minimum hierarchy level is " + "set to a negative value.\n" + "The path is assumed to originate from the current cell and contain specific instances sorted from " + "top to bottom." + "\n" + "This method has been added in version 0.25." + ) + + method_ext ("ascend", &cv_ascend, + "@brief Ascends upwards in the hierarchy.\n" + "Removes one element from the specific path of the cellview with the given index. Returns the element " + "removed." + "\n" + "This method has been added in version 0.25." + ) + + method_ext ("is_cell_hidden?", &cv_is_cell_hidden, gsi::arg ("cell"), + "@brief Returns true, if the given cell is hidden\n" + "\n" + "This method has been added in version 0.25." + ) + + method_ext ("hide_cell", &cv_hide_cell, gsi::arg ("cell"), + "@brief Hides the given cell\n" + "\n" + "This method has been added in version 0.25." + ) + + method_ext ("show_cell", &cv_show_cell, gsi::arg ("cell"), + "@brief Shows the given cell (cancels the effect of \\hide_cell)\n" + "\n" + "This method has been added in version 0.25." + ) + + method_ext ("show_all_cells", &cv_show_all_cells, + "@brief Makes all cells shown (cancel effects of \\hide_cell) for the specified cell view\n" + "\n" + "This method has been added in version 0.25." + ) + + method_ext ("close", &close_cellview, + "@brief Closes this cell view\n" + "\n" + "This method will close the cellview - remove it from the layout view. After this method was called, the " + "cellview will become invalid (see \\is_valid?).\n" + "\n" + "This method was introduced in version 0.25." + ), + "@brief A class describing what is shown inside a layout view\n" + "\n" + "The cell view points to a specific cell within a certain layout and a hierarchical context.\n" + "For that, first of all a layout pointer is provided. The cell itself\n" + "is addressed by an cell_index or a cell object reference.\n" + "The layout pointer can be nil, indicating that the cell view is invalid.\n" + "\n" + "The cell is not only identified by its index or object but also \n" + "by the path leading to that cell. This path indicates how to find the\n" + "cell in the hierarchical context of its parent cells. \n" + "\n" + "The path is in fact composed of two parts: first in an unspecific fashion,\n" + "just describing which parent cells are used. The target of this path\n" + "is called the \"context cell\". It is accessible by the \\ctx_cell_index\n" + "or \\ctx_cell methods. In the viewer, the unspecific part of the path is\n" + "the location of the cell in the cell tree.\n" + "\n" + "Additionally the path's second part may further identify a specific instance of a certain\n" + "subcell in the context cell. This is done through a set of \\InstElement\n" + "objects. The target of this specific path is the actual cell addressed by the\n" + "cellview. This target cell is accessible by the \\cell_index or \\cell methods.\n" + "In the viewer, the target cell is shown in the context of the context cell.\n" + "The hierarchy levels are counted from the context cell, which is on level 0.\n" + "If the context path is empty, the context cell is identical with the target cell.\n" + "\n" + "Starting with version 0.25, the cellview can be modified directly. This will have an immediate " + "effect on the display. For example, the following code will select a different cell:\n" + "\n" + "@code\n" + "cv = RBA::CellView::active\n" + "cv.cell_name = \"TOP2\"\n" + "@/code\n" + "\n" + "See @The Application API@ for more details about the " + "cellview objects." +); + +} + diff --git a/src/laybasic/laybasic/gsiDeclLayLayoutHandle.cc b/src/laybasic/laybasic/gsiDeclLayLayoutHandle.cc new file mode 100644 index 000000000..a9cfb1120 --- /dev/null +++ b/src/laybasic/laybasic/gsiDeclLayLayoutHandle.cc @@ -0,0 +1,215 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2026 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "gsiDecl.h" +#include "layLayoutHandle.h" + +namespace gsi +{ + +static std::vector get_names () +{ + std::vector names; + lay::LayoutHandle::get_names (names); + return names; +} + +static lay::LayoutHandleRef *find (const std::string &name) +{ + auto h = lay::LayoutHandle::find (name); + return h ? new lay::LayoutHandleRef (h) : 0; +} + +static lay::LayoutHandleRef *find_layout (const db::Layout *layout) +{ + auto h = lay::LayoutHandle::find_layout (layout); + return h ? new lay::LayoutHandleRef (h) : 0; +} + +static lay::LayoutHandleRef *new_handle1 (const std::string &filename, const db::LoadLayoutOptions &options, const std::string &technology) +{ + std::unique_ptr handle (new lay::LayoutHandle (new db::Layout (), filename)); + handle->load (options, technology); + return new lay::LayoutHandleRef (handle.release ()); +} + +static lay::LayoutHandleRef *new_handle2 (db::Layout *layout) +{ + return new lay::LayoutHandleRef (new lay::LayoutHandle (layout, std::string ())); +} + +static bool is_valid (const lay::LayoutHandleRef *ref) +{ + return ref->get () != 0; +} + +static const std::string &name (const lay::LayoutHandleRef *ref) +{ + tl_assert (ref->get () != 0); + return ref->get ()->name (); +} + +static void set_name (lay::LayoutHandleRef *ref, const std::string &name) +{ + tl_assert (ref->get () != 0); + ref->get ()->rename (name, true); +} + +static db::Layout *layout (const lay::LayoutHandleRef *ref) +{ + tl_assert (ref->get () != 0); + return &ref->get ()->layout (); +} + +static const std::string &filename (const lay::LayoutHandleRef *ref) +{ + tl_assert (ref->get () != 0); + return ref->get ()->filename (); +} + +static int get_ref_count (const lay::LayoutHandleRef *ref) +{ + tl_assert (ref->get () != 0); + return ref->get ()->get_ref_count (); +} + +static bool is_dirty (const lay::LayoutHandleRef *ref) +{ + tl_assert (ref->get () != 0); + return ref->get ()->is_dirty (); +} + +static void save_as (lay::LayoutHandleRef *ref, const std::string &filename, const db::SaveLayoutOptions &options, int keep_backups) +{ + tl_assert (ref->get () != 0); + ref->get ()->save_as (filename, tl::OutputStream::OM_Auto, options, true, keep_backups); + ref->get ()->set_save_options (options, true); +} + +static tl::Variant save_options (const lay::LayoutHandleRef *ref) +{ + tl_assert (ref->get () != 0); + return ref->get ()->save_options_valid () ? tl::Variant (ref->get ()->save_options ()) : tl::Variant (); +} + +static const db::LoadLayoutOptions &load_options (const lay::LayoutHandleRef *ref) +{ + tl_assert (ref->get () != 0); + return ref->get ()->load_options (); +} + + +Class decl_LayoutHandle ("lay", "LayoutHandle", + gsi::method ("names", &get_names, + "@brief Gets the names of all layout handles registered currently.\n" + ) + + gsi::constructor ("find", &find, gsi::arg ("name"), + "@brief Finds a layout handle by name.\n" + "If no handle with that name exists, nil is returned." + ) + + gsi::constructor ("find", &find_layout, gsi::arg ("layout"), + "@brief Finds a layout handle for the given layout object.\n" + "If no handle for that layout exists, nil is returned." + ) + + gsi::constructor ("new", &new_handle1, gsi::arg ("filename"), gsi::arg ("options"), gsi::arg ("technology", std::string ()), + "@brief Creates a handle from a file.\n" + "Loads the layout and creates a handle from that layout. The name is derived from the file name initially. " + "You can pass load options and assign a technology using 'options' and 'technology' respectively." + ) + + gsi::constructor ("new", &new_handle2, gsi::arg ("layout"), + "@brief Creates a handle from an existing layout object.\n" + "Creates a layout handle for an existing layout object. The ownership over the layout object is transferred to the handle. " + ) + + gsi::method_ext ("is_valid?", &is_valid, + "@brief Gets a value indicating whether the layout handle is valid.\n" + "Invalid layout handles cannot be used. Default-created layout handles are invalid for example." + ) + + gsi::method_ext ("name", &name, + "@brief Gets the name of the layout handle.\n" + "The name identifies a layout handle in the global context. Names should be unique. " + "Handles can be retrieved by name using \\find." + ) + + gsi::method_ext ("name=", &set_name, + "@brief Sets the name of the layout handle.\n" + "The caller is responsible for selecting a unique name for the handle. Only uniquely named handles " + "can be retrieved by \\find." + ) + + gsi::method_ext ("layout", &layout, + "@brief Gets the layout object kept by the handle.\n" + ) + + gsi::method_ext ("filename", &filename, + "@brief Gets the file name the layout was loaded from.\n" + "If the handle was not constructed from a file, this attribute is an empty string." + ) + + gsi::method_ext ("ref_count", &get_ref_count, + "@brief Gets the reference count.\n" + "The reference count indicates how many references are present for the handle. If the " + "reference count reaches zero, the layout object is destroyed. References are kept by " + "layout views showing the layout, but can also be kept by separate handles." + ) + + gsi::method_ext ("is_dirty?", &is_dirty, + "@brief Gets a value indicating whether the layout needs saving.\n" + ) + + gsi::method_ext ("save_as?", &save_as, gsi::arg ("filename"), gsi::arg ("options"), gsi::arg ("keep_backups", 0), + "@brief Saves the layout to a file.\n" + "This method will save the layout kept by the handle to the given file. Calling this method will change " + "the file name and reset the \\is_dirty? flag and set \\save_options to the options passed.\n" + "\n" + "@param filename The path where to save the layout to\n" + "@param options The options used for saving the file\n" + "@param keep_backups The number of backup files to keep (0 for 'no backups')\n" + ) + + gsi::method_ext ("save_options", &save_options, + "@brief Gets the save options used most recently when saving the file or 'nil' if no save options are available." + ) + + gsi::method_ext ("load_options", &load_options, + "@brief Gets the load options used when the layout was read.\n" + "Default options will be returned when the layout was not created from a file." + ), + "@brief A handle to a global layout object\n" + "Layout objects shown in layout views are stored in a global repository. The layout handle is a pointer " + "into that repository. Handles are reference counted - when no handle points to a layout, the layout is discarded.\n" + "A handle stores some more information about the layout, i.e. whether it is 'dirty' (needs saving) and the " + "options used for loading or saving the layout.\n" + "\n" + "The layout handle object is useful to hold a separate reference to a layout. This way it is possible " + "to close a layout view, but still have a reference to the layout:\n" + "\n" + "You can use layout handles to move a layout to a different view for example:\n" + "\n" + "@code\n" + "cellview = ... # the source cellview\n" + "new_view = ... # the new target view\n" + "\n" + "h = cellview.handle\n" + "cellview.close\n" + "new_view.show_layout(h, true)\n" + "@/code\n" + "\n" + "Handles are named. You can use \\LayoutHandle#find the find a handle by name or to obtain the handle for " + "a given layout object. You can use \\LayoutHandle#names to get the names of all handles registered in the system.\n" + "\n" + "This class has been introduced in version 0.30.7." +); + +} diff --git a/src/laybasic/laybasic/gsiDeclLayLayoutViewBase.cc b/src/laybasic/laybasic/gsiDeclLayLayoutViewBase.cc index 8c4307134..78ee00486 100644 --- a/src/laybasic/laybasic/gsiDeclLayLayoutViewBase.cc +++ b/src/laybasic/laybasic/gsiDeclLayLayoutViewBase.cc @@ -281,8 +281,6 @@ static void delete_layers1 (lay::LayoutViewBase *view, const std::vectorkeep (); lay::LayoutHandle *handle = lay::LayoutHandle::find_layout (layout); if (! handle) { handle = new lay::LayoutHandle (layout, std::string ()); @@ -290,22 +288,8 @@ static unsigned int show_layout1 (lay::LayoutViewBase *view, db::Layout *layout, return view->add_layout (handle, add_cellview); } -static unsigned int show_layout2 (lay::LayoutViewBase *view, db::Layout *layout, std::string &tech, bool add_cellview) +static unsigned int show_layout2 (lay::LayoutViewBase *view, db::Layout *layout, std::string &tech, bool add_cellview, bool initialize_layers) { - // the layout gets held by the LayoutHandle object - layout->keep (); - lay::LayoutHandle *handle = lay::LayoutHandle::find_layout (layout); - if (! handle) { - handle = new lay::LayoutHandle (layout, std::string ()); - } - handle->set_tech_name (tech); - return view->add_layout (handle, add_cellview); -} - -static unsigned int show_layout3 (lay::LayoutViewBase *view, db::Layout *layout, std::string &tech, bool add_cellview, bool initialize_layers) -{ - // the layout gets held by the LayoutHandle object - layout->keep (); lay::LayoutHandle *handle = lay::LayoutHandle::find_layout (layout); if (! handle) { handle = new lay::LayoutHandle (layout, std::string ()); @@ -314,6 +298,11 @@ static unsigned int show_layout3 (lay::LayoutViewBase *view, db::Layout *layout, return view->add_layout (handle, add_cellview, initialize_layers); } +static unsigned int show_layout_handle (lay::LayoutViewBase *view, lay::LayoutHandleRef &handle, bool add_cellview, bool initialize_layers) +{ + return view->add_layout (handle.operator-> (), add_cellview, initialize_layers); +} + static void delete_layers2 (lay::LayoutViewBase *view, unsigned int index, const std::vector &iters) { std::vector sorted (iters); @@ -773,23 +762,7 @@ LAYBASIC_PUBLIC Class decl_LayoutViewBase (decl_Dispatcher, "\n" "This method has been introduced in version 0.22.\n" ) + - gsi::method_ext ("show_layout", &show_layout2, gsi::arg ("layout"), gsi::arg ("tech"), gsi::arg ("add_cellview"), - "@brief Shows an existing layout in the view\n" - "\n" - "Shows the given layout in the view. If add_cellview is true, the new layout is added to the list of " - "cellviews in the view.\n" - "The technology to use for that layout can be specified as well with the 'tech' parameter. Depending " - "on the definition of the technology, layer properties may be loaded for example.\n" - "The technology string can be empty for the default technology.\n" - "\n" - "Note: once a layout is passed to the view with show_layout, it is owned by the view and must not be " - "destroyed with the 'destroy' method.\n" - "\n" - "@return The index of the cellview created.\n" - "\n" - "This method has been introduced in version 0.22.\n" - ) + - gsi::method_ext ("show_layout", &show_layout3, gsi::arg ("layout"), gsi::arg ("tech"), gsi::arg ("add_cellview"), gsi::arg ("init_layers"), + gsi::method_ext ("show_layout", &show_layout2, gsi::arg ("layout"), gsi::arg ("tech"), gsi::arg ("add_cellview"), gsi::arg ("init_layers", true), "@brief Shows an existing layout in the view\n" "\n" "Shows the given layout in the view. If add_cellview is true, the new layout is added to the list of " @@ -807,6 +780,17 @@ LAYBASIC_PUBLIC Class decl_LayoutViewBase (decl_Dispatcher, "\n" "This method has been introduced in version 0.22.\n" ) + + gsi::method_ext ("show_layout", &show_layout_handle, gsi::arg ("layout_handle"), gsi::arg ("add_cellview"), gsi::arg ("init_layers", true), + "@brief Shows an existing layout (from a handle) in the view\n" + "\n" + "This variant of \\show_layout takes a layout handle instead of the layout. Layout handles are reference-counted pointers\n" + "and can be used to hold a reference to a layout object. This way, ownership over the layout can be shared between different\n" + "objects.\n" + "\n" + "@return The index of the cellview created.\n" + "\n" + "This method has been introduced in version 0.30.7.\n" + ) + gsi::method ("erase_cellview", static_cast (&lay::LayoutViewBase::erase_cellview), gsi::arg ("index"), "@brief Erases the cellview with the given index\n" "\n" @@ -2083,366 +2067,5 @@ gsi::EnumIn decl_layLayoutVie // Inject the NetlistCrossReference::Status declarations into NetlistCrossReference: gsi::ClassExt inject_SelectionMode_in_parent (decl_layLayoutView_SelectionMode.defs ()); -static db::Layout *get_layout (const lay::CellViewRef *cv) -{ - if ((*cv).operator-> ()) { - return &(*cv)->layout (); - } else { - return 0; - } -} - -static std::string name (const lay::CellViewRef *cv) -{ - if ((*cv).operator-> ()) { - return (*cv)->name (); - } else { - return std::string (); - } -} - -static void set_name (lay::CellViewRef *cv, const std::string &name) -{ - cv->set_name (name); -} - -static std::string filename (const lay::CellViewRef *cv) -{ - if ((*cv).operator-> ()) { - return (*cv)->filename (); - } else { - return std::string (); - } -} - -static bool is_dirty (const lay::CellViewRef *cv) -{ - if ((*cv).operator-> ()) { - return (*cv)->is_dirty (); - } else { - return false; - } -} - -static void apply_technology (const lay::CellViewRef *cv, const std::string &tech) -{ - if ((*cv).operator-> ()) { - (*cv)->apply_technology (tech); - } -} - -static std::string get_technology (const lay::CellViewRef *cv) -{ - if ((*cv).operator-> ()) { - return (*cv)->tech_name (); - } else { - return std::string (); - } -} - -static tl::Event &get_technology_changed_event (lay::CellViewRef *cv) -{ - if (! cv->is_valid ()) { - throw tl::Exception (tl::to_string (tr ("Not a valid cellview"))); - } - return (*cv)->technology_changed_event; -} - -static void set_cell (lay::CellViewRef *cv, db::Cell *cell) -{ - if (! cell) { - cv->reset_cell (); - } else { - cv->set_cell (cell->cell_index ()); - } -} - -static void close_cellview (lay::CellViewRef *cv) -{ - if (cv->is_valid ()) { - cv->view ()->erase_cellview (cv->index ()); - } -} - -static std::string get_cell_name (const lay::CellViewRef *cv) -{ - if (cv->cell () == 0) { - return std::string (); - } else { - return (*cv)->layout ().cell_name (cv->cell_index ()); - } -} - -static void cv_descend (lay::CellViewRef *cv, const std::vector &path) -{ - if (cv->is_valid ()) { - cv->view ()->descend (path, cv->index ()); - } -} - -static void cv_ascend (lay::CellViewRef *cv) -{ - if (cv->is_valid ()) { - cv->view ()->ascend (cv->index ()); - } -} - -static bool cv_is_cell_hidden (lay::CellViewRef *cv, const db::Cell *cell) -{ - if (cv->is_valid () && cell) { - if (cell->layout () != &(*cv)->layout ()) { - throw tl::Exception (tl::to_string (tr ("The cell is not a cell of the view's layout"))); - } - return cv->view ()->is_cell_hidden (cell->cell_index (), cv->index ()); - } else { - return false; - } -} - -static void cv_hide_cell (lay::CellViewRef *cv, const db::Cell *cell) -{ - if (cv->is_valid () && cell) { - if (cell->layout () != &(*cv)->layout ()) { - throw tl::Exception (tl::to_string (tr ("The cell is not a cell of the view's layout"))); - } - cv->view ()->hide_cell (cell->cell_index (), cv->index ()); - } -} - -static void cv_show_cell (lay::CellViewRef *cv, const db::Cell *cell) -{ - if (cv->is_valid () && cell) { - if (cell->layout () != &(*cv)->layout ()) { - throw tl::Exception (tl::to_string (tr ("The cell is not a cell of the view's layout"))); - } - cv->view ()->show_cell (cell->cell_index (), cv->index ()); - } -} - -static void cv_show_all_cells (lay::CellViewRef *cv) -{ - if (cv->is_valid ()) { - cv->view ()->show_all_cells (cv->index ()); - } -} - -Class decl_CellView ("lay", "CellView", - method ("==", static_cast (&lay::CellViewRef::operator==), gsi::arg ("other"), - "@brief Equality: indicates whether the cellviews refer to the same one\n" - "In version 0.25, the definition of the equality operator has been changed to reflect identity of the " - "cellview. Before that version, identity of the cell shown was implied." - ) + - method ("index", &lay::CellViewRef::index, - "@brief Gets the index of this cellview in the layout view\n" - "The index will be negative if the cellview is not a valid one.\n" - "This method has been added in version 0.25.\n" - ) + - method ("is_valid?", &lay::CellViewRef::is_valid, - "@brief Returns true, if the cellview is valid\n" - "A cellview may become invalid if the corresponding tab is closed for example." - ) + - method ("path=|set_path", &lay::CellViewRef::set_unspecific_path, gsi::arg ("path"), - "@brief Sets the unspecific part of the path explicitly\n" - "\n" - "Setting the unspecific part of the path will clear the context path component and\n" - "update the context and target cell.\n" - ) + - method ("context_path=|set_context_path", &lay::CellViewRef::set_specific_path, gsi::arg ("path"), - "@brief Sets the context path explicitly\n" - "\n" - "This method assumes that the unspecific part of the path \n" - "is established already and that the context path starts\n" - "from the context cell.\n" - ) + - method ("cell_index=|set_cell", (void (lay::CellViewRef::*) (lay::CellViewRef::cell_index_type)) &lay::CellViewRef::set_cell, gsi::arg ("cell_index"), - "@brief Sets the path to the given cell\n" - "\n" - "This method will construct any path to this cell, not a \n" - "particular one. It will clear the context path\n" - "and update the context and target cell. Note that the cell is specified by its index.\n" - ) + - method ("cell_name=|set_cell_name", (void (lay::CellViewRef::*) (const std::string &)) &lay::CellViewRef::set_cell, gsi::arg ("cell_name"), - "@brief Sets the cell by name\n" - "\n" - "If the name is not a valid one, the cellview will become\n" - "invalid.\n" - "This method will construct any path to this cell, not a \n" - "particular one. It will clear the context path\n" - "and update the context and target cell.\n" - ) + - method_ext ("cell=", set_cell, gsi::arg ("cell"), - "@brief Sets the cell by reference to a Cell object\n" - "Setting the cell reference to nil invalidates the cellview. " - "This method will construct any path to this cell, not a \n" - "particular one. It will clear the context path\n" - "and update the context and target cell.\n" - ) + - method ("reset_cell", &lay::CellViewRef::reset_cell, - "@brief Resets the cell \n" - "\n" - "The cellview will become invalid. The layout object will\n" - "still be attached to the cellview, but no cell will be selected.\n" - ) + - method ("ctx_cell_index", &lay::CellViewRef::ctx_cell_index, - "@brief Gets the context cell's index\n" - ) + - method ("ctx_cell", &lay::CellViewRef::ctx_cell, - "@brief Gets the reference to the context cell currently addressed\n" - ) + - method ("cell_index", &lay::CellViewRef::cell_index, - "@brief Gets the target cell's index\n" - ) + - method ("cell", &lay::CellViewRef::cell, - "@brief Gets the reference to the target cell currently addressed\n" - ) + - method_ext ("cell_name", &get_cell_name, - "@brief Gets the name of the target cell currently addressed\n" - ) + - method_ext ("filename", &gsi::filename, - "@brief Gets filename associated with the layout behind the cellview\n" - ) + - method_ext ("is_dirty?", &gsi::is_dirty, - "@brief Gets a flag indicating whether the layout needs saving\n" - "A layout is 'dirty' if it is modified and needs saving. This method returns " - "true in this case.\n" - "\n" - "This method has been introduced in version 0.24.10.\n" - ) + - method_ext ("name", &gsi::name, - "@brief Gets the unique name associated with the layout behind the cellview\n" - ) + - method_ext ("name=", &gsi::set_name, gsi::arg("name"), - "@brief sets the unique name associated with the layout behind the cellview\n" - "\n" - "this method has been introduced in version 0.25." - ) + - method ("path", &lay::CellViewRef::unspecific_path, - "@brief Gets the cell's unspecific part of the path leading to the context cell\n" - ) + - method ("context_path", &lay::CellViewRef::specific_path, - "@brief Gets the cell's context path\n" - "The context path leads from the context cell to the target cell in a specific " - "fashion, i.e. describing each instance in detail, not just by cell indexes. If " - "the context and target cell are identical, the context path is empty." - ) + - method ("context_trans", &lay::CellViewRef::context_trans, - "@brief Gets the accumulated transformation of the context path\n" - "This is the transformation applied to the target cell before it is shown in the context cell\n" - "Technically this is the product of all transformations over the context path.\n" - "See \\context_dtrans for a version delivering a micron-unit space transformation.\n" - "\n" - "This method has been introduced in version 0.27.3.\n" - ) + - method ("context_dtrans", &lay::CellViewRef::context_dtrans, - "@brief Gets the accumulated transformation of the context path in micron unit space\n" - "This is the transformation applied to the target cell before it is shown in the context cell\n" - "Technically this is the product of all transformations over the context path.\n" - "See \\context_trans for a version delivering an integer-unit space transformation.\n" - "\n" - "This method has been introduced in version 0.27.3.\n" - ) + - event_ext ("on_technology_changed", &get_technology_changed_event, - "@brief An event indicating that the technology has changed\n" - "This event is triggered when the CellView is attached to a different technology.\n" - "\n" - "This event has been introduced in version 0.27.\n" - ) + - method_ext ("technology", &get_technology, - "@brief Returns the technology name for the layout behind the given cell view\n" - "This method has been added in version 0.23.\n" - ) + - method_ext ("technology=", &apply_technology, gsi::arg ("tech_name"), - "@brief Sets the technology for the layout behind the given cell view\n" - "According to the specification of the technology, new layer properties may be loaded " - "or the net tracer may be reconfigured. If the layout is shown in multiple views, the " - "technology is updated for all views.\n" - "This method has been added in version 0.22.\n" - ) + - method_ext ("layout", &get_layout, - "@brief Gets the reference to the layout object addressed by this view\n" - ) + - method_ext ("descend", &cv_descend, gsi::arg ("path"), - "@brief Descends further into the hierarchy.\n" - "Adds the given path (given as an array of InstElement objects) to the specific path of the " - "cellview with the given index. In effect, the cell addressed by the terminal of the new path " - "components can be shown in the context of the upper cells, if the minimum hierarchy level is " - "set to a negative value.\n" - "The path is assumed to originate from the current cell and contain specific instances sorted from " - "top to bottom." - "\n" - "This method has been added in version 0.25." - ) + - method_ext ("ascend", &cv_ascend, - "@brief Ascends upwards in the hierarchy.\n" - "Removes one element from the specific path of the cellview with the given index. Returns the element " - "removed." - "\n" - "This method has been added in version 0.25." - ) + - method_ext ("is_cell_hidden?", &cv_is_cell_hidden, gsi::arg ("cell"), - "@brief Returns true, if the given cell is hidden\n" - "\n" - "This method has been added in version 0.25." - ) + - method_ext ("hide_cell", &cv_hide_cell, gsi::arg ("cell"), - "@brief Hides the given cell\n" - "\n" - "This method has been added in version 0.25." - ) + - method_ext ("show_cell", &cv_show_cell, gsi::arg ("cell"), - "@brief Shows the given cell (cancels the effect of \\hide_cell)\n" - "\n" - "This method has been added in version 0.25." - ) + - method_ext ("show_all_cells", &cv_show_all_cells, - "@brief Makes all cells shown (cancel effects of \\hide_cell) for the specified cell view\n" - "\n" - "This method has been added in version 0.25." - ) + - method_ext ("close", &close_cellview, - "@brief Closes this cell view\n" - "\n" - "This method will close the cellview - remove it from the layout view. After this method was called, the " - "cellview will become invalid (see \\is_valid?).\n" - "\n" - "This method was introduced in version 0.25." - ), - "@brief A class describing what is shown inside a layout view\n" - "\n" - "The cell view points to a specific cell within a certain layout and a hierarchical context.\n" - "For that, first of all a layout pointer is provided. The cell itself\n" - "is addressed by an cell_index or a cell object reference.\n" - "The layout pointer can be nil, indicating that the cell view is invalid.\n" - "\n" - "The cell is not only identified by its index or object but also \n" - "by the path leading to that cell. This path indicates how to find the\n" - "cell in the hierarchical context of its parent cells. \n" - "\n" - "The path is in fact composed of two parts: first in an unspecific fashion,\n" - "just describing which parent cells are used. The target of this path\n" - "is called the \"context cell\". It is accessible by the \\ctx_cell_index\n" - "or \\ctx_cell methods. In the viewer, the unspecific part of the path is\n" - "the location of the cell in the cell tree.\n" - "\n" - "Additionally the path's second part may further identify a specific instance of a certain\n" - "subcell in the context cell. This is done through a set of \\InstElement\n" - "objects. The target of this specific path is the actual cell addressed by the\n" - "cellview. This target cell is accessible by the \\cell_index or \\cell methods.\n" - "In the viewer, the target cell is shown in the context of the context cell.\n" - "The hierarchy levels are counted from the context cell, which is on level 0.\n" - "If the context path is empty, the context cell is identical with the target cell.\n" - "\n" - "Starting with version 0.25, the cellview can be modified directly. This will have an immediate " - "effect on the display. For example, the following code will select a different cell:\n" - "\n" - "@code\n" - "cv = RBA::CellView::active\n" - "cv.cell_name = \"TOP2\"\n" - "@/code\n" - "\n" - "See @The Application API@ for more details about the " - "cellview objects." -); - } diff --git a/src/laybasic/laybasic/layCellView.cc b/src/laybasic/laybasic/layCellView.cc index 7c70791cc..f20138ec5 100644 --- a/src/laybasic/laybasic/layCellView.cc +++ b/src/laybasic/laybasic/layCellView.cc @@ -20,478 +20,12 @@ */ - #include "layCellView.h" #include "layLayoutViewBase.h" -#if defined(HAVE_QT) -# include "layStream.h" -#endif -#include "dbLayout.h" -#include "dbWriter.h" -#include "dbReader.h" -#include "tlLog.h" -#include "tlStaticObjects.h" - -#include namespace lay { -// ------------------------------------------------------------- - -static std::string -filename_for_caption (const std::string &fn) -{ - const char *cp = fn.c_str (); - const char *cpp = cp + fn.size (); - while (cpp > cp && cpp [-1] != '\\' && cpp [-1] != '/') { - --cpp; - } - return cpp; -} - -// ------------------------------------------------------------- -// LayoutHandle implementation - - -LayoutHandle::LayoutHandle (db::Layout *layout, const std::string &filename) - : mp_layout (layout), - m_ref_count (0), - m_filename (filename), - m_dirty (false), - m_save_options_valid (false) -{ - layout->technology_changed_event.add (this, &LayoutHandle::on_technology_changed); - - // layouts in the managed layouts space participate in spare proxy cleanup - layout->do_cleanup (true); - - add_file_to_watcher (m_filename); - - if (! m_filename.empty ()) { - rename (filename_for_caption (m_filename)); - } else { - - // create a unique new name - static int nn = 0; - - std::string n; - do { - n = tl::sprintf ("L%d", ++nn); - } while (find (n) != 0); - - m_name = n; - ms_dict.insert (std::make_pair (n, this)); - - } - - mp_layout->hier_changed_event.add (this, &LayoutHandle::layout_changed); - mp_layout->bboxes_changed_any_event.add (this, &LayoutHandle::layout_changed); - mp_layout->cell_name_changed_event.add (this, &LayoutHandle::layout_changed); - mp_layout->prop_ids_changed_event.add (this, &LayoutHandle::layout_changed); - mp_layout->layer_properties_changed_event.add (this, &LayoutHandle::layout_changed); - - if (tl::verbosity () >= 30) { - tl::info << "Created layout " << name (); - } -} - -LayoutHandle::~LayoutHandle () -{ - if (tl::verbosity () >= 30) { - tl::info << "Deleted layout " << name (); - } - - delete mp_layout; - mp_layout = 0; - - if (find (m_name) == this) { - ms_dict.erase (m_name); - } - - remove_file_from_watcher (filename ()); -} - -void -LayoutHandle::remove_file_from_watcher (const std::string &path) -{ -#if defined(HAVE_QT) - file_watcher ().remove_file (path); -#endif -} - -void -LayoutHandle::add_file_to_watcher (const std::string &path) -{ -#if defined(HAVE_QT) - file_watcher ().add_file (path); -#endif -} - -void -LayoutHandle::on_technology_changed () -{ - technology_changed_event (); -} - -void -LayoutHandle::layout_changed () -{ - m_dirty = true; -} - -void -LayoutHandle::rename (const std::string &name, bool force) -{ - std::string n (name); - - if (n != m_name) { - - if (force || find (n) == 0) { - ms_dict.erase (m_name); - if (tl::verbosity () >= 40) { - tl::info << "Renamed layout from " << m_name << " to " << n; - } - m_name = n; - ms_dict.insert (std::make_pair (n, this)); - return; - } - - // rename using suffix "[u]" where u is a unique index - int nn = 0; - int ns = 0x40000000; - do { - n = name + tl::sprintf ("[%d]", nn + ns); - if (find (n) != 0) { - nn += ns; - } - ns /= 2; - } while (ns > 0); - - n = name + tl::sprintf ("[%d]", nn + 1); - - if (tl::verbosity () >= 40) { - tl::info << "Renamed layout from " << m_name << " to " << n; - } - - if (find (m_name) == this) { - ms_dict.erase (m_name); - } - - m_name = n; - ms_dict.insert (std::make_pair (n, this)); - return; - - } -} - -db::Layout & -LayoutHandle::layout () const -{ - return *mp_layout; -} - -void -LayoutHandle::set_filename (const std::string &fn) -{ - remove_file_from_watcher (m_filename); - m_filename = fn; - add_file_to_watcher (m_filename); -} - -const std::string & -LayoutHandle::filename () const -{ - return m_filename; -} - -const std::string & -LayoutHandle::name () const -{ - return m_name; -} - -void -LayoutHandle::add_ref () -{ - if (tl::verbosity () >= 50) { - tl::info << "Add reference to " << m_name; - } - ++m_ref_count; -} - -void -LayoutHandle::remove_ref () -{ - if (tl::verbosity () >= 50) { - tl::info << "Remove reference from " << m_name; - } - if (--m_ref_count <= 0) { - // not nice, but hopefully we can do so: - delete this; - } -} - -const std::string & -LayoutHandle::tech_name () const -{ - static std::string s_empty; - return mp_layout ? mp_layout->technology_name () : s_empty; -} - -const db::Technology * -LayoutHandle::technology () const -{ - return mp_layout ? mp_layout->technology () : 0; -} - -void -LayoutHandle::apply_technology (const std::string &tn) -{ - set_tech_name (tn); - apply_technology_event (); - apply_technology_with_sender_event (this); -} - -void -LayoutHandle::set_tech_name (const std::string &tn) -{ - if (mp_layout && tn != tech_name ()) { - mp_layout->set_technology_name (tn); - } -} - -LayoutHandle * -LayoutHandle::find (const std::string &name) -{ - std::map ::const_iterator h = ms_dict.find (name); - if (h == ms_dict.end ()) { - return 0; - } else { - return h->second; - } -} - -LayoutHandle * -LayoutHandle::find_layout (const db::Layout *layout) -{ - for (auto h = ms_dict.begin (); h != ms_dict.end (); ++h) { - if (h->second->mp_layout == layout) { - return h->second; - } - } - return 0; -} - -void -LayoutHandle::get_names (std::vector &names) -{ - names.clear (); - names.reserve (ms_dict.size ()); - for (std::map ::const_iterator h = ms_dict.begin (); h != ms_dict.end (); ++h) { - names.push_back (h->first); - } -} - -void -LayoutHandle::set_save_options (const db::SaveLayoutOptions &options, bool valid) -{ - m_save_options = options; - m_save_options_valid = valid; -} - -void -LayoutHandle::save_as (const std::string &fn, tl::OutputStream::OutputStreamMode om, const db::SaveLayoutOptions &options, bool update, int keep_backups) -{ - if (update) { - - // We must not load with the original options after we have saved the file - hence we reset the - // reader options. - m_load_options = db::LoadLayoutOptions (); - - remove_file_from_watcher (filename ()); - - rename (filename_for_caption (fn)); - - // NOTE: we don't use set_filename since this would re-attach the file watcher - m_filename = fn; - - } - - try { - - { - // The write needs to be finished before the file watcher gets the new modification time - db::Writer writer (options); - tl::OutputStream stream (fn, om, false, keep_backups); - try { - writer.write (*mp_layout, stream); - } catch (...) { - stream.reject (); - throw; - } - } - - if (update) { - add_file_to_watcher (filename ()); - m_dirty = false; - } - - } catch (...) { - - if (update) { - add_file_to_watcher (filename ()); - } - - throw; - - } -} - -db::LayerMap -LayoutHandle::load (const db::LoadLayoutOptions &options, const std::string &technology) -{ - m_load_options = options; - m_save_options = db::SaveLayoutOptions (); - m_save_options_valid = false; - - set_tech_name (technology); - - tl::InputStream stream (m_filename); - db::Reader reader (stream); - db::LayerMap new_lmap = reader.read (layout (), m_load_options); - - // If there is no technology given and the reader reports one, use this one - if (technology.empty ()) { - std::string tech_from_reader = layout ().technology_name (); - if (! tech_from_reader.empty ()) { - set_tech_name (tech_from_reader); - } - } - - // Update the file's data: - remove_file_from_watcher (filename ()); - add_file_to_watcher (filename ()); - - m_save_options.set_format (reader.format ()); - m_dirty = false; - return new_lmap; -} - -db::LayerMap -LayoutHandle::load () -{ - m_load_options = db::LoadLayoutOptions (); - m_save_options = db::SaveLayoutOptions (); - m_save_options_valid = false; - - set_tech_name (std::string ()); - - tl::InputStream stream (m_filename); - db::Reader reader (stream); - db::LayerMap new_lmap = reader.read (layout (), m_load_options); - - // Attach the technology from the reader if it reports one - std::string tech_from_reader = layout ().technology_name (); - if (! tech_from_reader.empty ()) { - set_tech_name (tech_from_reader); - } - - // Update the file's data: - remove_file_from_watcher (filename ()); - add_file_to_watcher (filename ()); - - m_save_options.set_format (reader.format ()); - m_dirty = false; - return new_lmap; -} - -#if defined(HAVE_QT) -tl::FileSystemWatcher & -LayoutHandle::file_watcher () -{ - if (! mp_file_watcher) { - mp_file_watcher = new tl::FileSystemWatcher (); - tl::StaticObjects::reg (&mp_file_watcher); - } - return *mp_file_watcher; -} - -tl::FileSystemWatcher *LayoutHandle::mp_file_watcher = 0; -#endif - -std::map LayoutHandle::ms_dict; - -// ------------------------------------------------------------- -// LayoutHandleRef implementation - -LayoutHandleRef::LayoutHandleRef () - : mp_handle (0) -{ - // .. nothing yet .. -} - -LayoutHandleRef::LayoutHandleRef (LayoutHandle *h) - : mp_handle (0) -{ - set (h); -} - -LayoutHandleRef::LayoutHandleRef (const LayoutHandleRef &r) - : mp_handle (0) -{ - set (r.mp_handle); -} - -LayoutHandleRef::~LayoutHandleRef () -{ - set (0); -} - -bool -LayoutHandleRef::operator== (const LayoutHandleRef &r) const -{ - return mp_handle == r.mp_handle; -} - -LayoutHandleRef & -LayoutHandleRef::operator= (const LayoutHandleRef &r) -{ - if (&r != this) { - set (r.mp_handle); - } - return *this; -} - -void -LayoutHandleRef::set (LayoutHandle *h) -{ - if (mp_handle == h) { - return; - } - - if (mp_handle) { - mp_handle->remove_ref (); - mp_handle = 0; - } - mp_handle = h; - if (mp_handle) { - mp_handle->add_ref (); - } -} - -LayoutHandle * -LayoutHandleRef::operator-> () const -{ - return mp_handle; -} - -LayoutHandle * -LayoutHandleRef::get () const -{ - return mp_handle; -} - // ------------------------------------------------------------- // CellView implementation diff --git a/src/laybasic/laybasic/layCellView.h b/src/laybasic/laybasic/layCellView.h index 13280e1f9..477d82294 100644 --- a/src/laybasic/laybasic/layCellView.h +++ b/src/laybasic/laybasic/layCellView.h @@ -25,6 +25,7 @@ #define HDR_layCellView #include "laybasicCommon.h" +#include "layLayoutHandle.h" #include #include @@ -33,330 +34,13 @@ #include "dbLayout.h" #include "dbMetaInfo.h" #include "dbReader.h" -#include "dbSaveLayoutOptions.h" -#include "dbLoadLayoutOptions.h" #include "dbInstElement.h" #include "dbTechnology.h" #include "gsi.h" -#if defined(HAVE_QT) -# include "tlFileSystemWatcher.h" -#endif - namespace lay { -class LayoutViewBase; - -/** - * @brief A layout handle - * - * This object controls a layout object. A layout object can be - * identified through a name. Additionally, a reference count - * is maintained that controls when the layout object is deleted. - */ -class LAYBASIC_PUBLIC LayoutHandle - : public tl::Object -{ -public: - /** - * @brief Creates a layout handle to the given object - * - * This constructor creates a new handle to the given - * layout object. The handle takes over the ownership over the - * layout object. - * The initial reference count is zero (see remove_ref). - * The filename is a string that is supposed to identify - * the layout further. It can be retrieved with the filename - * method. - */ - LayoutHandle (db::Layout *layout, const std::string &filename); - - /** - * @brief Destructor - * - * The destructor will delete the layout object that - * was associated with this handle. - */ - ~LayoutHandle (); - - /** - * @brief Renames the layout object - * - * If "force" is set to true, the layout will be given that name, irregardless if - * the name already is being used. If "force" is false, a new unique name is created. - */ - void rename (const std::string &name, bool force = false); - - /** - * @brief Gets the name of the handle - */ - const std::string &name () const; - - /** - * @brief Gets the layout object that this handle points to - */ - db::Layout &layout () const; - - /** - * @brief Sets the file name associated with this handle - */ - void set_filename (const std::string &); - - /** - * @brief Gets the file name associated with this handle - */ - const std::string &filename () const; - - /** - * @brief Gets the technology attached to this layout - */ - const db::Technology *technology () const; - - /** - * @brief Gets the technology name for this layout - * - * An empty name indicates the default technology should be used. - */ - const std::string &tech_name () const; - - /** - * @brief Applies the given technology - * - * This will set the technology to the new one and send the apply event. - * This event is sent always, even if the technology did not change. - */ - void apply_technology (const std::string &tn); - - /** - * @brief Sets the technology name - * - * If there is no technology with that name, the default technology - * will be used. - */ - void set_tech_name (const std::string &tn); - - /** - * @brief Finds a layout object by name - * - * @param name The name under which to find the layout object - * @return 0, if there is no layout object with this name. Otherwise a pointer to its handle - */ - static LayoutHandle *find (const std::string &name); - - /** - * @brief Finds a handle by layout object - * - * @param layout The Layout object bound to the handle - * @return 0, if there is no layout object with this name. Otherwise a pointer to its handle - */ - static LayoutHandle *find_layout (const db::Layout *layout); - - /** - * @brief Gets the names of all registered layout objects - */ - static void get_names (std::vector &names); - - /** - * @brief Gets the reference count - */ - int get_ref_count () const - { - return m_ref_count; - } - - /** - * @brief Adds a reference to the layout handle - * - * This method will increment the reference counter of this handle - */ - void add_ref (); - - /** - * @brief Removes a reference to the layout handle - * - * This method will decrement the reference counter. Once the - * reference count reaches zero, the layout object and the - * handle is deleted. - * Upon initialization, the reference count is zero. - * Hint: it is generally not safe to access the handle after - * a remove_ref was issued. - */ - void remove_ref (); - - /** - * @brief Returns true, if the layout is "dirty" - * - * A layout is "dirty", if it needs to be saved. - * It is set dirty if one of the signal handlers is triggered. - */ - bool is_dirty () const - { - return m_dirty; - } - - /** - * @brief Loads the layout - * - * Load the layout from the file given in the constructor using the given layer map. - * The dirty flag is reset. - * - * @param lmap The layer map specifying the layers to read - * @param technology The technology to use for layer map and other settings or empty for "default technology" - * @return The new layer map (can differ from the input since layers may be created) - */ - db::LayerMap load (const db::LoadLayoutOptions &options, const std::string &technology); - - /** - * @brief Loads the layout - * - * Load the layout from the file given in the constructor. - * The dirty flag is reset. - * - * @return The new layer map - */ - db::LayerMap load (); - - /** - * @brief Saves the layout - * - * Save the layout under the given file name and with the given options. - * If update is true, this method updates the cell view's filename, title, save options and dirty flag. - */ - void save_as (const std::string &filename, tl::OutputStream::OutputStreamMode om, const db::SaveLayoutOptions &options, bool update = true, int keep_backups = 0); - - /** - * @brief Sets the save options and a flag indicating whether they are valid - * - * This method is mainly used by the session restore feature to restore the handle's state. - */ - void set_save_options (const db::SaveLayoutOptions &options, bool valid); - - /** - * @brief Gets the current saving options - * - * The saving options are set by the last save_as method call. - */ - const db::SaveLayoutOptions &save_options () const - { - return m_save_options; - } - - /** - * @brief Gets a flag indicating whether the save options are valid - * - * The save options are valid once the layout has been saved with specific - * options using "save_as" with the "update" options. - */ - bool save_options_valid () const - { - return m_save_options_valid; - } - - /** - * @brief Gets the current reader options - * - * The reader options are set by the load method call. - */ - const db::LoadLayoutOptions &load_options () const - { - return m_load_options; - } - - /** - * @brief An event indicating that the technology has changed - * This event is triggered if the technology was changed. - */ - tl::Event technology_changed_event; - - /** - * @brief An event indicating that a technology shall be applied - * This event is triggered to make the listeners apply a new technology - * to the layout. - */ - tl::Event apply_technology_event; - - /** - * @brief An event indicating that a technology shall be applied - * This event is triggered to make the listeners apply a new technology - * to the layout. This version supplies a sender pointer. - */ - tl::event apply_technology_with_sender_event; - - /** - * @brief An event handler for a layout change - * This handler is attached to a layout changed event and will invalidate the handle. - */ - void layout_changed (); - -#if defined(HAVE_QT) - /** - * @brief Gets the file system watcher that delivers events when one of the layouts gets updated - */ - static tl::FileSystemWatcher &file_watcher (); -#endif - - /** - * @brief Removes a file from the watcher - */ - static void remove_file_from_watcher (const std::string &path); - - /** - * @brief Adds a file to the watcher - */ - static void add_file_to_watcher (const std::string &path); - -private: - db::Layout *mp_layout; - int m_ref_count; - std::string m_name; - std::string m_filename; - bool m_dirty; - db::SaveLayoutOptions m_save_options; - bool m_save_options_valid; - db::LoadLayoutOptions m_load_options; - - void on_technology_changed (); - - static std::map ms_dict; -#if defined(HAVE_QT) - static tl::FileSystemWatcher *mp_file_watcher; -#endif -}; - -/** - * @brief A layout handle reference - * - * This class encapsulates a reference to a layout handle. - * The main purpose for this class is to automate the reference - * counting on the handle. - */ -class LAYBASIC_PUBLIC LayoutHandleRef -{ -public: - LayoutHandleRef (); - LayoutHandleRef (LayoutHandle *h); - LayoutHandleRef (const LayoutHandleRef &r); - ~LayoutHandleRef (); - - LayoutHandleRef &operator= (const LayoutHandleRef &r); - - bool operator== (const LayoutHandleRef &r) const; - - bool operator!= (const LayoutHandleRef &r) const - { - return !operator== (r); - } - - LayoutHandle *operator-> () const; - - LayoutHandle *get () const; - void set (LayoutHandle *h); - -private: - LayoutHandle *mp_handle; -}; - /** * @brief A "cell view" reference * diff --git a/src/laybasic/laybasic/layLayoutHandle.cc b/src/laybasic/laybasic/layLayoutHandle.cc new file mode 100644 index 000000000..a37ff3934 --- /dev/null +++ b/src/laybasic/laybasic/layLayoutHandle.cc @@ -0,0 +1,490 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2026 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "layLayoutHandle.h" + +#include "dbWriter.h" +#include "dbReader.h" +#include "tlStaticObjects.h" + +namespace lay +{ + +// ------------------------------------------------------------- + +static std::string +filename_for_caption (const std::string &fn) +{ + const char *cp = fn.c_str (); + const char *cpp = cp + fn.size (); + while (cpp > cp && cpp [-1] != '\\' && cpp [-1] != '/') { + --cpp; + } + return cpp; +} + +// ------------------------------------------------------------- +// LayoutHandle implementation + + +LayoutHandle::LayoutHandle (db::Layout *layout, const std::string &filename) + : mp_layout (layout), + m_ref_count (0), + m_filename (filename), + m_dirty (false), + m_save_options_valid (false) +{ + // the layout gets held by the LayoutHandle object + layout->keep (); + + layout->technology_changed_event.add (this, &LayoutHandle::on_technology_changed); + + // layouts in the managed layouts space participate in spare proxy cleanup + layout->do_cleanup (true); + + add_file_to_watcher (m_filename); + + if (! m_filename.empty ()) { + rename (filename_for_caption (m_filename)); + } else { + + // create a unique new name + static int nn = 0; + + std::string n; + do { + n = tl::sprintf ("L%d", ++nn); + } while (find (n) != 0); + + m_name = n; + ms_dict.insert (std::make_pair (n, this)); + + } + + mp_layout->hier_changed_event.add (this, &LayoutHandle::layout_changed); + mp_layout->bboxes_changed_any_event.add (this, &LayoutHandle::layout_changed); + mp_layout->cell_name_changed_event.add (this, &LayoutHandle::layout_changed); + mp_layout->prop_ids_changed_event.add (this, &LayoutHandle::layout_changed); + mp_layout->layer_properties_changed_event.add (this, &LayoutHandle::layout_changed); + + if (tl::verbosity () >= 30) { + tl::info << "Created layout " << name (); + } +} + +LayoutHandle::~LayoutHandle () +{ + if (tl::verbosity () >= 30) { + tl::info << "Deleted layout " << name (); + } + + delete mp_layout; + mp_layout = 0; + + if (find (m_name) == this) { + ms_dict.erase (m_name); + } + + remove_file_from_watcher (filename ()); +} + +void +LayoutHandle::remove_file_from_watcher (const std::string &path) +{ +#if defined(HAVE_QT) + file_watcher ().remove_file (path); +#endif +} + +void +LayoutHandle::add_file_to_watcher (const std::string &path) +{ +#if defined(HAVE_QT) + file_watcher ().add_file (path); +#endif +} + +void +LayoutHandle::on_technology_changed () +{ + technology_changed_event (); +} + +void +LayoutHandle::layout_changed () +{ + m_dirty = true; +} + +void +LayoutHandle::rename (const std::string &name, bool force) +{ + std::string n (name); + + if (n != m_name) { + + if (force || find (n) == 0) { + ms_dict.erase (m_name); + if (tl::verbosity () >= 40) { + tl::info << "Renamed layout from " << m_name << " to " << n; + } + m_name = n; + ms_dict.insert (std::make_pair (n, this)); + return; + } + + // rename using suffix "[u]" where u is a unique index + int nn = 0; + int ns = 0x40000000; + do { + n = name + tl::sprintf ("[%d]", nn + ns); + if (find (n) != 0) { + nn += ns; + } + ns /= 2; + } while (ns > 0); + + n = name + tl::sprintf ("[%d]", nn + 1); + + if (tl::verbosity () >= 40) { + tl::info << "Renamed layout from " << m_name << " to " << n; + } + + if (find (m_name) == this) { + ms_dict.erase (m_name); + } + + m_name = n; + ms_dict.insert (std::make_pair (n, this)); + return; + + } +} + +db::Layout & +LayoutHandle::layout () const +{ + return *mp_layout; +} + +void +LayoutHandle::set_filename (const std::string &fn) +{ + remove_file_from_watcher (m_filename); + m_filename = fn; + add_file_to_watcher (m_filename); +} + +const std::string & +LayoutHandle::filename () const +{ + return m_filename; +} + +const std::string & +LayoutHandle::name () const +{ + return m_name; +} + +void +LayoutHandle::add_ref () +{ + if (tl::verbosity () >= 50) { + tl::info << "Add reference to " << m_name; + } + ++m_ref_count; +} + +void +LayoutHandle::remove_ref () +{ + if (tl::verbosity () >= 50) { + tl::info << "Remove reference from " << m_name; + } + if (--m_ref_count <= 0) { + // not nice, but hopefully we can do so: + delete this; + } +} + +const std::string & +LayoutHandle::tech_name () const +{ + static std::string s_empty; + return mp_layout ? mp_layout->technology_name () : s_empty; +} + +const db::Technology * +LayoutHandle::technology () const +{ + return mp_layout ? mp_layout->technology () : 0; +} + +void +LayoutHandle::apply_technology (const std::string &tn) +{ + set_tech_name (tn); + apply_technology_event (); + apply_technology_with_sender_event (this); +} + +void +LayoutHandle::set_tech_name (const std::string &tn) +{ + if (mp_layout && tn != tech_name ()) { + mp_layout->set_technology_name (tn); + } +} + +LayoutHandle * +LayoutHandle::find (const std::string &name) +{ + std::map ::const_iterator h = ms_dict.find (name); + if (h == ms_dict.end ()) { + return 0; + } else { + return h->second; + } +} + +LayoutHandle * +LayoutHandle::find_layout (const db::Layout *layout) +{ + for (auto h = ms_dict.begin (); h != ms_dict.end (); ++h) { + if (h->second->mp_layout == layout) { + return h->second; + } + } + return 0; +} + +void +LayoutHandle::get_names (std::vector &names) +{ + names.clear (); + names.reserve (ms_dict.size ()); + for (std::map ::const_iterator h = ms_dict.begin (); h != ms_dict.end (); ++h) { + names.push_back (h->first); + } +} + +void +LayoutHandle::set_save_options (const db::SaveLayoutOptions &options, bool valid) +{ + m_save_options = options; + m_save_options_valid = valid; +} + +void +LayoutHandle::save_as (const std::string &fn, tl::OutputStream::OutputStreamMode om, const db::SaveLayoutOptions &options, bool update, int keep_backups) +{ + if (update) { + + // We must not load with the original options after we have saved the file - hence we reset the + // reader options. + m_load_options = db::LoadLayoutOptions (); + + remove_file_from_watcher (filename ()); + + rename (filename_for_caption (fn)); + + // NOTE: we don't use set_filename since this would re-attach the file watcher + m_filename = fn; + + } + + try { + + { + // The write needs to be finished before the file watcher gets the new modification time + db::Writer writer (options); + tl::OutputStream stream (fn, om, false, keep_backups); + try { + writer.write (*mp_layout, stream); + } catch (...) { + stream.reject (); + throw; + } + } + + if (update) { + add_file_to_watcher (filename ()); + m_dirty = false; + } + + } catch (...) { + + if (update) { + add_file_to_watcher (filename ()); + } + + throw; + + } +} + +db::LayerMap +LayoutHandle::load (const db::LoadLayoutOptions &options, const std::string &technology) +{ + m_load_options = options; + m_save_options = db::SaveLayoutOptions (); + m_save_options_valid = false; + + set_tech_name (technology); + + tl::InputStream stream (m_filename); + db::Reader reader (stream); + db::LayerMap new_lmap = reader.read (layout (), m_load_options); + + // If there is no technology given and the reader reports one, use this one + if (technology.empty ()) { + std::string tech_from_reader = layout ().technology_name (); + if (! tech_from_reader.empty ()) { + set_tech_name (tech_from_reader); + } + } + + // Update the file's data: + remove_file_from_watcher (filename ()); + add_file_to_watcher (filename ()); + + m_save_options.set_format (reader.format ()); + m_dirty = false; + return new_lmap; +} + +db::LayerMap +LayoutHandle::load () +{ + m_load_options = db::LoadLayoutOptions (); + m_save_options = db::SaveLayoutOptions (); + m_save_options_valid = false; + + set_tech_name (std::string ()); + + tl::InputStream stream (m_filename); + db::Reader reader (stream); + db::LayerMap new_lmap = reader.read (layout (), m_load_options); + + // Attach the technology from the reader if it reports one + std::string tech_from_reader = layout ().technology_name (); + if (! tech_from_reader.empty ()) { + set_tech_name (tech_from_reader); + } + + // Update the file's data: + remove_file_from_watcher (filename ()); + add_file_to_watcher (filename ()); + + m_save_options.set_format (reader.format ()); + m_dirty = false; + return new_lmap; +} + +#if defined(HAVE_QT) +tl::FileSystemWatcher & +LayoutHandle::file_watcher () +{ + if (! mp_file_watcher) { + mp_file_watcher = new tl::FileSystemWatcher (); + tl::StaticObjects::reg (&mp_file_watcher); + } + return *mp_file_watcher; +} + +tl::FileSystemWatcher *LayoutHandle::mp_file_watcher = 0; +#endif + +std::map LayoutHandle::ms_dict; + +// ------------------------------------------------------------- +// LayoutHandleRef implementation + +LayoutHandleRef::LayoutHandleRef () + : mp_handle (0) +{ + // .. nothing yet .. +} + +LayoutHandleRef::LayoutHandleRef (LayoutHandle *h) + : mp_handle (0) +{ + set (h); +} + +LayoutHandleRef::LayoutHandleRef (const LayoutHandleRef &r) + : mp_handle (0) +{ + set (r.mp_handle); +} + +LayoutHandleRef::~LayoutHandleRef () +{ + set (0); +} + +bool +LayoutHandleRef::operator== (const LayoutHandleRef &r) const +{ + return mp_handle == r.mp_handle; +} + +LayoutHandleRef & +LayoutHandleRef::operator= (const LayoutHandleRef &r) +{ + if (&r != this) { + set (r.mp_handle); + } + return *this; +} + +void +LayoutHandleRef::set (LayoutHandle *h) +{ + if (mp_handle == h) { + return; + } + + if (mp_handle) { + mp_handle->remove_ref (); + mp_handle = 0; + } + mp_handle = h; + if (mp_handle) { + mp_handle->add_ref (); + } +} + +LayoutHandle * +LayoutHandleRef::operator-> () const +{ + return mp_handle; +} + +LayoutHandle * +LayoutHandleRef::get () const +{ + return mp_handle; +} + +} diff --git a/src/laybasic/laybasic/layLayoutHandle.h b/src/laybasic/laybasic/layLayoutHandle.h new file mode 100644 index 000000000..f2a927fe5 --- /dev/null +++ b/src/laybasic/laybasic/layLayoutHandle.h @@ -0,0 +1,356 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2026 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef HDR_layLayoutViewHandle +#define HDR_layLayoutViewHandle + +#include "laybasicCommon.h" + +#include "dbLayout.h" +#include "dbStream.h" + +#if defined(HAVE_QT) +# include "tlFileSystemWatcher.h" +#endif + +#include +#include +#include + +namespace lay +{ + +class LayoutViewBase; + +/** + * @brief A layout handle + * + * This object controls a layout object. A layout object can be + * identified through a name. Additionally, a reference count + * is maintained that controls when the layout object is deleted. + */ +class LAYBASIC_PUBLIC LayoutHandle + : public tl::Object +{ +public: + /** + * @brief Creates a layout handle to the given object + * + * This constructor creates a new handle to the given + * layout object. The handle takes over the ownership over the + * layout object. + * The initial reference count is zero (see remove_ref). + * The filename is a string that is supposed to identify + * the layout further. It can be retrieved with the filename + * method. + */ + LayoutHandle (db::Layout *layout, const std::string &filename); + + /** + * @brief Destructor + * + * The destructor will delete the layout object that + * was associated with this handle. + */ + ~LayoutHandle (); + + /** + * @brief Renames the layout object + * + * If "force" is set to true, the layout will be given that name, irregardless if + * the name already is being used. If "force" is false, a new unique name is created. + */ + void rename (const std::string &name, bool force = false); + + /** + * @brief Gets the name of the handle + */ + const std::string &name () const; + + /** + * @brief Gets the layout object that this handle points to + */ + db::Layout &layout () const; + + /** + * @brief Sets the file name associated with this handle + */ + void set_filename (const std::string &); + + /** + * @brief Gets the file name associated with this handle + */ + const std::string &filename () const; + + /** + * @brief Gets the technology attached to this layout + */ + const db::Technology *technology () const; + + /** + * @brief Gets the technology name for this layout + * + * An empty name indicates the default technology should be used. + */ + const std::string &tech_name () const; + + /** + * @brief Applies the given technology + * + * This will set the technology to the new one and send the apply event. + * This event is sent always, even if the technology did not change. + */ + void apply_technology (const std::string &tn); + + /** + * @brief Sets the technology name + * + * If there is no technology with that name, the default technology + * will be used. + */ + void set_tech_name (const std::string &tn); + + /** + * @brief Finds a layout object by name + * + * @param name The name under which to find the layout object + * @return 0, if there is no layout object with this name. Otherwise a pointer to its handle + */ + static LayoutHandle *find (const std::string &name); + + /** + * @brief Finds a handle by layout object + * + * @param layout The Layout object bound to the handle + * @return 0, if there is no layout object with this name. Otherwise a pointer to its handle + */ + static LayoutHandle *find_layout (const db::Layout *layout); + + /** + * @brief Gets the names of all registered layout objects + */ + static void get_names (std::vector &names); + + /** + * @brief Gets the reference count + */ + int get_ref_count () const + { + return m_ref_count; + } + + /** + * @brief Adds a reference to the layout handle + * + * This method will increment the reference counter of this handle + */ + void add_ref (); + + /** + * @brief Removes a reference to the layout handle + * + * This method will decrement the reference counter. Once the + * reference count reaches zero, the layout object and the + * handle is deleted. + * Upon initialization, the reference count is zero. + * Hint: it is generally not safe to access the handle after + * a remove_ref was issued. + */ + void remove_ref (); + + /** + * @brief Returns true, if the layout is "dirty" + * + * A layout is "dirty", if it needs to be saved. + * It is set dirty if one of the signal handlers is triggered. + */ + bool is_dirty () const + { + return m_dirty; + } + + /** + * @brief Loads the layout + * + * Load the layout from the file given in the constructor using the given layer map. + * The dirty flag is reset. + * + * @param lmap The layer map specifying the layers to read + * @param technology The technology to use for layer map and other settings or empty for "default technology" + * @return The new layer map (can differ from the input since layers may be created) + */ + db::LayerMap load (const db::LoadLayoutOptions &options, const std::string &technology); + + /** + * @brief Loads the layout + * + * Load the layout from the file given in the constructor. + * The dirty flag is reset. + * + * @return The new layer map + */ + db::LayerMap load (); + + /** + * @brief Saves the layout + * + * Save the layout under the given file name and with the given options. + * If update is true, this method updates the cell view's filename, title, save options and dirty flag. + */ + void save_as (const std::string &filename, tl::OutputStream::OutputStreamMode om, const db::SaveLayoutOptions &options, bool update = true, int keep_backups = 0); + + /** + * @brief Sets the save options and a flag indicating whether they are valid + * + * This method is mainly used by the session restore feature to restore the handle's state. + */ + void set_save_options (const db::SaveLayoutOptions &options, bool valid); + + /** + * @brief Gets the current saving options + * + * The saving options are set by the last save_as method call. + */ + const db::SaveLayoutOptions &save_options () const + { + return m_save_options; + } + + /** + * @brief Gets a flag indicating whether the save options are valid + * + * The save options are valid once the layout has been saved with specific + * options using "save_as" with the "update" options. + */ + bool save_options_valid () const + { + return m_save_options_valid; + } + + /** + * @brief Gets the current reader options + * + * The reader options are set by the load method call. + */ + const db::LoadLayoutOptions &load_options () const + { + return m_load_options; + } + + /** + * @brief An event indicating that the technology has changed + * This event is triggered if the technology was changed. + */ + tl::Event technology_changed_event; + + /** + * @brief An event indicating that a technology shall be applied + * This event is triggered to make the listeners apply a new technology + * to the layout. + */ + tl::Event apply_technology_event; + + /** + * @brief An event indicating that a technology shall be applied + * This event is triggered to make the listeners apply a new technology + * to the layout. This version supplies a sender pointer. + */ + tl::event apply_technology_with_sender_event; + + /** + * @brief An event handler for a layout change + * This handler is attached to a layout changed event and will invalidate the handle. + */ + void layout_changed (); + +#if defined(HAVE_QT) + /** + * @brief Gets the file system watcher that delivers events when one of the layouts gets updated + */ + static tl::FileSystemWatcher &file_watcher (); +#endif + + /** + * @brief Removes a file from the watcher + */ + static void remove_file_from_watcher (const std::string &path); + + /** + * @brief Adds a file to the watcher + */ + static void add_file_to_watcher (const std::string &path); + +private: + db::Layout *mp_layout; + int m_ref_count; + std::string m_name; + std::string m_filename; + bool m_dirty; + db::SaveLayoutOptions m_save_options; + bool m_save_options_valid; + db::LoadLayoutOptions m_load_options; + + void on_technology_changed (); + + static std::map ms_dict; +#if defined(HAVE_QT) + static tl::FileSystemWatcher *mp_file_watcher; +#endif +}; + +/** + * @brief A layout handle reference + * + * This class encapsulates a reference to a layout handle. + * The main purpose for this class is to automate the reference + * counting on the handle. + */ +class LAYBASIC_PUBLIC LayoutHandleRef +{ +public: + LayoutHandleRef (); + LayoutHandleRef (LayoutHandle *h); + LayoutHandleRef (const LayoutHandleRef &r); + ~LayoutHandleRef (); + + LayoutHandleRef &operator= (const LayoutHandleRef &r); + + bool operator== (const LayoutHandleRef &r) const; + + bool operator!= (const LayoutHandleRef &r) const + { + return !operator== (r); + } + + LayoutHandle *operator-> () const; + + LayoutHandle *get () const; + void set (LayoutHandle *h); + +private: + LayoutHandle *mp_handle; +}; + +} + +#endif + diff --git a/src/laybasic/laybasic/laybasic.pro b/src/laybasic/laybasic/laybasic.pro index bb15d949a..a7bec543b 100644 --- a/src/laybasic/laybasic/laybasic.pro +++ b/src/laybasic/laybasic/laybasic.pro @@ -28,8 +28,10 @@ DEFINES += MAKE_LAYBASIC_LIBRARY SOURCES += \ gsiDeclLayAdded.cc \ + gsiDeclLayCellView.cc \ gsiDeclLayLayers.cc \ gsiDeclLayDispatcher.cc \ + gsiDeclLayLayoutHandle.cc \ gsiDeclLayLayoutViewBase.cc \ gsiDeclLayMarker.cc \ gsiDeclLayMenu.cc \ @@ -39,6 +41,7 @@ SOURCES += \ layAbstractMenu.cc \ layEditorOptionsPage.cc \ layEditorUtils.cc \ + layLayoutHandle.cc \ layLayoutViewConfig.cc \ layMargin.cc \ laybasicForceLink.cc \ @@ -93,6 +96,7 @@ SOURCES += \ HEADERS += \ layEditorOptionsPage.h \ layEditorUtils.h \ + layLayoutHandle.h \ layMargin.h \ laybasicConfig.h \ laybasicForceLink.h \ diff --git a/testdata/ruby/layLayoutView.rb b/testdata/ruby/layLayoutView.rb index b60997827..e6aa49093 100644 --- a/testdata/ruby/layLayoutView.rb +++ b/testdata/ruby/layLayoutView.rb @@ -622,6 +622,109 @@ class LAYLayoutView_TestClass < TestBase end + # layout handles + def test_20 + + name = "LayoutViewTest::test_20" + + hh = RBA::LayoutHandle.find("DOESNOTEXIST???") + assert_equal(hh == nil, true) + + h = RBA::LayoutHandle::new + assert_equal(h.is_valid?, false) + + lv = RBA::LayoutView::new(true) + + ly = RBA::Layout::new + hh = RBA::LayoutHandle.find(ly) # not yet + assert_equal(hh == nil, true) + + ly.create_cell("TOPCELL") + + h = RBA::LayoutHandle::new(ly) + assert_equal(h.is_valid?, true) + h.name = name + assert_equal(h.name, name) + assert_equal(RBA::LayoutHandle.names.member?(name), true) + hh = RBA::LayoutHandle.find(name) + assert_equal(hh.name, name) + hh._destroy + hh = RBA::LayoutHandle.find(ly) + assert_equal(hh.name, name) + hh._destroy + assert_equal(h.filename, "") + assert_equal(h.ref_count, 1) + assert_equal(h.is_dirty?, false) + assert_equal(h.layout.top_cell.name, "TOPCELL") + + # smoke test + assert_equal(h.save_options, nil) + assert_equal(h.load_options.class, RBA::LoadLayoutOptions) + + l1 = ly.layer(1, 0) + ly.top_cell.shapes(l1).insert(RBA::Box::new(0, 0, 1000, 2000)) + assert_equal(h.is_dirty?, true) + + hh = RBA::LayoutHandle::find(ly) + assert_equal(hh.name, name) + assert_equal(hh.layout.top_cell.name, "TOPCELL") + hh._destroy + + lv.show_layout(h, true) + assert_equal(h.ref_count, 2) + + hh = RBA::LayoutHandle::find(name) + assert_equal(hh.name, name) + assert_equal(hh.layout.top_cell.name, "TOPCELL") + hh._destroy + + cv = lv.cellview(0) + hh = cv.layout_handle + assert_equal(hh.name, name) + assert_equal(hh.layout.top_cell.name, "TOPCELL") + hh._destroy + assert_equal(RBA::LayoutHandle.names.member?(name), true) + + lv.erase_cellview(0) + + # still there? + assert_equal(h.layout.top_cell.name, "TOPCELL") + + assert_equal(h.ref_count, 1) + h._destroy + assert_equal(RBA::LayoutHandle.names.member?(name), false) + + end + + # layout handles + def test_21 + + name = "LayoutViewTest::test_21" + + h = RBA::LayoutHandle::new(RBA::Layout::new) + h.name = name + assert_equal(h.is_valid?, true) + assert_equal(h.filename, "") + + h.layout.create_cell("TOP_CELL") + assert_equal(h.is_dirty?, true) + + tmp = File.join($ut_testtmp, "layout_handles.gds") + h.save_as(tmp, RBA::SaveLayoutOptions::new) + assert_equal(h.filename, tmp) + assert_equal(h.save_options.class, RBA::SaveLayoutOptions) + assert_equal(h.is_dirty?, false) + + h._destroy + + h = RBA::LayoutHandle::new(tmp, RBA::LoadLayoutOptions::new) + assert_equal(h.filename, tmp) + assert_equal(h.layout.top_cell.name, "TOP_CELL") + + h._destroy + + end + end load("test_epilogue.rb")