Providing a solution for #2248

The solution is to introduce a new object, the layout handle.
A layout handle is a reference counting pointer to a layout.
It can be obtained from a cell view and stored somewhere.
If the cell view is closed, the layout will still exists
as long as a layout handle exists. A layout handle can be
used in LayoutView#show_layout in addition to the Layout
object to re-open a layout that existed in a closed view.
This commit is contained in:
Matthias Koefferlein 2026-03-01 21:13:40 +01:00
parent 5c26821d1f
commit aaa165df32
9 changed files with 1600 additions and 1178 deletions

View File

@ -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<db::InstElement> &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<lay::CellViewRef> decl_CellView ("lay", "CellView",
method ("==", static_cast<bool (lay::CellViewRef::*) (const lay::CellViewRef &) const> (&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 @<a href=\"/programming/application_api.xml\">The Application API@</a> for more details about the "
"cellview objects."
);
}

View File

@ -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 <std::string> get_names ()
{
std::vector <std::string> 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<lay::LayoutHandle> 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<lay::LayoutHandleRef> 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."
);
}

View File

@ -281,8 +281,6 @@ static void delete_layers1 (lay::LayoutViewBase *view, const std::vector<lay::La
static unsigned int show_layout1 (lay::LayoutViewBase *view, db::Layout *layout, bool add_cellview)
{
// 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 ());
@ -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<lay::LayerPropertiesConstIterator> &iters)
{
std::vector<lay::LayerPropertiesConstIterator> sorted (iters);
@ -773,23 +762,7 @@ LAYBASIC_PUBLIC Class<lay::LayoutViewBase> 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<lay::LayoutViewBase> 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<void (lay::LayoutViewBase::*) (unsigned int)> (&lay::LayoutViewBase::erase_cellview), gsi::arg ("index"),
"@brief Erases the cellview with the given index\n"
"\n"
@ -2083,366 +2067,5 @@ gsi::EnumIn<lay::LayoutViewBase, lay::Editable::SelectionMode> decl_layLayoutVie
// Inject the NetlistCrossReference::Status declarations into NetlistCrossReference:
gsi::ClassExt<lay::LayoutViewBase> 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<db::InstElement> &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<lay::CellViewRef> decl_CellView ("lay", "CellView",
method ("==", static_cast<bool (lay::CellViewRef::*) (const lay::CellViewRef &) const> (&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 @<a href=\"/programming/application_api.xml\">The Application API@</a> for more details about the "
"cellview objects."
);
}

View File

@ -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 <algorithm>
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 <std::string, LayoutHandle *>::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 <std::string> &names)
{
names.clear ();
names.reserve (ms_dict.size ());
for (std::map <std::string, LayoutHandle *>::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 <std::string, LayoutHandle *> 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

View File

@ -25,6 +25,7 @@
#define HDR_layCellView
#include "laybasicCommon.h"
#include "layLayoutHandle.h"
#include <string>
#include <vector>
@ -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 <std::string> &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<lay::LayoutHandle *> 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 <std::string, LayoutHandle *> 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
*

View File

@ -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 <std::string, LayoutHandle *>::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 <std::string> &names)
{
names.clear ();
names.reserve (ms_dict.size ());
for (std::map <std::string, LayoutHandle *>::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 <std::string, LayoutHandle *> 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;
}
}

View File

@ -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 <string>
#include <vector>
#include <map>
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 <std::string> &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<lay::LayoutHandle *> 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 <std::string, LayoutHandle *> 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

View File

@ -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 \

View File

@ -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")