mirror of https://github.com/KLayout/klayout.git
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:
parent
5c26821d1f
commit
aaa165df32
|
|
@ -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."
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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."
|
||||
);
|
||||
|
||||
}
|
||||
|
|
@ -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."
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
Loading…
Reference in New Issue