mirror of https://github.com/KLayout/klayout.git
WIP: undo/redo for applying a technology.
This commit is contained in:
parent
740f550964
commit
86e7fa56f0
|
|
@ -77,9 +77,9 @@ std::string
|
|||
ColdProxy::get_basic_name () const
|
||||
{
|
||||
if (! mp_context_info->pcell_name.empty ()) {
|
||||
return "<defunc>" + mp_context_info->pcell_name;
|
||||
return "<defunct>" + mp_context_info->pcell_name;
|
||||
} else if (! mp_context_info->cell_name.empty ()) {
|
||||
return "<defunc>" + mp_context_info->cell_name;
|
||||
return "<defunct>" + mp_context_info->cell_name;
|
||||
} else {
|
||||
return Cell::get_basic_name ();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,6 +86,27 @@ private:
|
|||
db::properties_id_type m_from, m_to;
|
||||
};
|
||||
|
||||
struct SetLayoutTechName
|
||||
: public LayoutOp
|
||||
{
|
||||
SetLayoutTechName (const std::string &from, const std::string &to)
|
||||
: m_from (from), m_to (to)
|
||||
{ }
|
||||
|
||||
virtual void redo (db::Layout *layout) const
|
||||
{
|
||||
layout->set_technology_name_without_update (m_to);
|
||||
}
|
||||
|
||||
virtual void undo (db::Layout *layout) const
|
||||
{
|
||||
layout->set_technology_name_without_update (m_from);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_from, m_to;
|
||||
};
|
||||
|
||||
struct SetLayoutDBU
|
||||
: public LayoutOp
|
||||
{
|
||||
|
|
@ -509,6 +530,18 @@ Layout::technology () const
|
|||
return db::Technologies::instance ()->technology_by_name (m_tech_name);
|
||||
}
|
||||
|
||||
void
|
||||
Layout::set_technology_name_without_update (const std::string &tech)
|
||||
{
|
||||
if (tech != m_tech_name) {
|
||||
if (manager () && manager ()->transacting ()) {
|
||||
manager ()->queue (this, new SetLayoutTechName (m_tech_name, tech));
|
||||
}
|
||||
m_tech_name = tech;
|
||||
technology_changed_event ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Layout::set_technology_name (const std::string &tech)
|
||||
{
|
||||
|
|
@ -666,7 +699,7 @@ Layout::set_technology_name (const std::string &tech)
|
|||
|
||||
}
|
||||
|
||||
m_tech_name = tech;
|
||||
set_technology_name_without_update (tech);
|
||||
|
||||
// we may have re-established a connection for pending ("cold") proxies so we can try to restore them
|
||||
restore_proxies ();
|
||||
|
|
@ -2043,9 +2076,20 @@ Layout::replace_cell (cell_index_type target_cell_index, db::Cell *new_cell, boo
|
|||
}
|
||||
}
|
||||
|
||||
m_cells.erase (iterator (old_cell));
|
||||
if (manager () && manager ()->transacting ()) {
|
||||
// note the "take" method - this takes out the cell but does not delete it (we need it inside undo)
|
||||
m_cells.take (iterator (old_cell));
|
||||
manager ()->queue (this, new NewRemoveCellOp (target_cell_index, cell_name (target_cell_index), true /*remove*/, old_cell));
|
||||
} else {
|
||||
m_cells.erase (iterator (old_cell));
|
||||
}
|
||||
|
||||
m_cells.push_back_ptr (new_cell);
|
||||
m_cell_ptrs [target_cell_index] = new_cell;
|
||||
|
||||
if (manager () && manager ()->transacting ()) {
|
||||
manager ()->queue (this, new NewRemoveCellOp (target_cell_index, m_cell_names [target_cell_index], false /*new*/, 0));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -2060,7 +2104,6 @@ Layout::get_pcell_variant_as (pcell_id_type pcell_id, const std::vector<tl::Vari
|
|||
// this variant must not exist yet for "get as" semantics
|
||||
tl_assert (header->get_variant (*this, parameters) == 0);
|
||||
|
||||
tl_assert (! (manager () && manager ()->transacting ()));
|
||||
tl_assert (m_cell_ptrs [target_cell_index] != 0);
|
||||
|
||||
pcell_variant_type *variant = new pcell_variant_type (target_cell_index, *this, pcell_id, parameters);
|
||||
|
|
@ -2628,7 +2671,6 @@ Layout::unregister_lib_proxy (db::LibraryProxy *lib_proxy)
|
|||
void
|
||||
Layout::get_lib_proxy_as (Library *lib, cell_index_type cell_index, cell_index_type target_cell_index, ImportLayerMapping *layer_mapping, bool retain_layout)
|
||||
{
|
||||
tl_assert (! (manager () && manager ()->transacting ()));
|
||||
tl_assert (m_cell_ptrs [target_cell_index] != 0);
|
||||
|
||||
LibraryProxy *proxy = new LibraryProxy (target_cell_index, *this, lib->get_id (), cell_index);
|
||||
|
|
@ -2710,7 +2752,6 @@ Layout::create_cold_proxy (const db::ProxyContextInfo &info)
|
|||
void
|
||||
Layout::create_cold_proxy_as (const db::ProxyContextInfo &info, cell_index_type target_cell_index)
|
||||
{
|
||||
tl_assert (! (manager () && manager ()->transacting ()));
|
||||
tl_assert (m_cell_ptrs [target_cell_index] != 0);
|
||||
|
||||
ColdProxy *proxy = new ColdProxy (target_cell_index, *this, info);
|
||||
|
|
|
|||
|
|
@ -593,6 +593,13 @@ public:
|
|||
*/
|
||||
void set_technology_name (const std::string &tech);
|
||||
|
||||
/**
|
||||
* @brief Changes the technology name
|
||||
* This method will only change the technology name, but does not re-assess the library links.
|
||||
* It's provided mainly to support undo/redo and testing.
|
||||
*/
|
||||
void set_technology_name_without_update (const std::string &tech);
|
||||
|
||||
/**
|
||||
* @brief Accessor to the array repository
|
||||
*/
|
||||
|
|
@ -1816,6 +1823,11 @@ public:
|
|||
*/
|
||||
const std::string &meta_info_value (const std::string &name) const;
|
||||
|
||||
/**
|
||||
* @brief This event is triggered when the technology changes
|
||||
*/
|
||||
tl::Event technology_changed_event;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Establish the graph's internals according to the dirty flags
|
||||
|
|
|
|||
|
|
@ -69,19 +69,25 @@ LibraryManager::~LibraryManager ()
|
|||
std::pair<bool, lib_id_type>
|
||||
LibraryManager::lib_by_name (const std::string &name, const std::set<std::string> &for_technologies) const
|
||||
{
|
||||
iterator l = m_lib_by_name.find (name);
|
||||
while (l != m_lib_by_name.end () && l->first == name) {
|
||||
const db::Library *lptr = lib (l->second);
|
||||
bool found = lptr->for_technologies ();
|
||||
for (std::set<std::string>::const_iterator t = for_technologies.begin (); t != for_technologies.end () && found; ++t) {
|
||||
if (! lptr->is_for_technology (*t)) {
|
||||
found = false;
|
||||
iterator l;
|
||||
|
||||
if (! for_technologies.empty ()) {
|
||||
|
||||
l = m_lib_by_name.find (name);
|
||||
while (l != m_lib_by_name.end () && l->first == name) {
|
||||
const db::Library *lptr = lib (l->second);
|
||||
bool found = lptr->for_technologies ();
|
||||
for (std::set<std::string>::const_iterator t = for_technologies.begin (); t != for_technologies.end () && found; ++t) {
|
||||
if (! lptr->is_for_technology (*t)) {
|
||||
found = false;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
return std::make_pair (true, l->second);
|
||||
}
|
||||
++l;
|
||||
}
|
||||
if (found) {
|
||||
return std::make_pair (true, l->second);
|
||||
}
|
||||
++l;
|
||||
|
||||
}
|
||||
|
||||
// fallback: technology-unspecific libs
|
||||
|
|
|
|||
|
|
@ -20,9 +20,12 @@
|
|||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "dbLayout.h"
|
||||
#include "dbLibraryManager.h"
|
||||
#include "dbLibrary.h"
|
||||
#include "dbColdProxy.h"
|
||||
#include "dbLibraryProxy.h"
|
||||
#include "dbTextWriter.h"
|
||||
#include "tlString.h"
|
||||
#include "tlUnitTest.h"
|
||||
|
||||
|
|
@ -464,3 +467,170 @@ TEST(4)
|
|||
prop_id = g.properties_repository ().properties_id (ps);
|
||||
EXPECT_EQ (el.property_ids_dirty, true);
|
||||
}
|
||||
|
||||
static std::string l2s (const db::Layout &layout)
|
||||
{
|
||||
tl::OutputStringStream os;
|
||||
tl::OutputStream ostream (os);
|
||||
db::TextWriter writer (ostream);
|
||||
writer.write (layout);
|
||||
return os.string ();
|
||||
}
|
||||
|
||||
TEST(5)
|
||||
{
|
||||
// Technology management and library substitution
|
||||
|
||||
db::cell_index_type ci;
|
||||
unsigned int li;
|
||||
db::Cell *cell;
|
||||
|
||||
db::Library *lib_a = new db::Library ();
|
||||
lib_a->set_name ("LIB");
|
||||
ci = lib_a->layout ().add_cell ("LIBCELL");
|
||||
li = lib_a->layout ().insert_layer (db::LayerProperties (1, 0));
|
||||
lib_a->layout ().cell (ci).shapes (li).insert (db::Box (0, 0, 100, 200));
|
||||
lib_a->add_technology ("A");
|
||||
db::LibraryManager::instance ().register_lib (lib_a);
|
||||
|
||||
EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("LIB", "A").first, true);
|
||||
EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("LIB", "A").second, lib_a->get_id ());
|
||||
EXPECT_EQ (db::LibraryManager::instance ().lib_ptr_by_name ("LIB", "A") == lib_a, true);
|
||||
|
||||
db::Library *lib_b = new db::Library ();
|
||||
lib_b->set_name ("LIB");
|
||||
ci = lib_b->layout ().add_cell ("LIBCELL");
|
||||
li = lib_b->layout ().insert_layer (db::LayerProperties (2, 0));
|
||||
lib_b->layout ().cell (ci).shapes (li).insert (db::Box (0, 0, 200, 100));
|
||||
lib_b->add_technology ("B");
|
||||
db::LibraryManager::instance ().register_lib (lib_b);
|
||||
|
||||
EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("LIB", "B").first, true);
|
||||
EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("LIB", "B").second, lib_b->get_id ());
|
||||
EXPECT_EQ (db::LibraryManager::instance ().lib_ptr_by_name ("LIB", "B") == lib_b, true);
|
||||
|
||||
db::Library *lib_c = new db::Library ();
|
||||
lib_c->set_name ("LIB");
|
||||
ci = lib_c->layout ().add_cell ("LIBCELL2");
|
||||
li = lib_c->layout ().insert_layer (db::LayerProperties (2, 0));
|
||||
lib_c->layout ().cell (ci).shapes (li).insert (db::Box (0, 0, 200, 100));
|
||||
lib_c->add_technology ("C");
|
||||
db::LibraryManager::instance ().register_lib (lib_c);
|
||||
|
||||
EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("LIB", "C").first, true);
|
||||
EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("LIB", "C").second, lib_c->get_id ());
|
||||
EXPECT_EQ (db::LibraryManager::instance ().lib_ptr_by_name ("LIB", "C") == lib_c, true);
|
||||
|
||||
db::Manager m;
|
||||
db::Layout l (&m);
|
||||
EXPECT_EQ (l.technology_name (), "");
|
||||
|
||||
db::ProxyContextInfo info;
|
||||
info.lib_name = "LIB";
|
||||
info.cell_name = "LIBCELL";
|
||||
|
||||
cell = l.recover_proxy (info);
|
||||
EXPECT_EQ (dynamic_cast<db::ColdProxy *> (cell) != 0, true);
|
||||
EXPECT_EQ (cell->get_qualified_name (), "<defunct>LIB.LIBCELL");
|
||||
EXPECT_EQ (cell->get_basic_name (), "<defunct>LIBCELL");
|
||||
EXPECT_EQ (cell->get_display_name (), "<defunct>LIB.LIBCELL");
|
||||
|
||||
EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {LIBCELL}\nend_cell\nend_lib\n");
|
||||
|
||||
// now restore the proxies
|
||||
l.set_technology_name ("A");
|
||||
EXPECT_EQ (l.technology_name (), "A");
|
||||
|
||||
EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {LIBCELL}\nbox 1 0 {0 0} {100 200}\nend_cell\nend_lib\n");
|
||||
|
||||
// now switch to cold proxies again as the technology does not have "LIBCELL" (but rather LIBCELL2)
|
||||
l.set_technology_name ("C");
|
||||
EXPECT_EQ (l.technology_name (), "C");
|
||||
|
||||
cell = &l.cell (l.cell_by_name ("LIBCELL").second);
|
||||
EXPECT_EQ (dynamic_cast<db::ColdProxy *> (cell) != 0, true);
|
||||
EXPECT_EQ (cell->get_qualified_name (), "<defunct>LIB.LIBCELL");
|
||||
EXPECT_EQ (cell->get_basic_name (), "<defunct>LIBCELL");
|
||||
EXPECT_EQ (cell->get_display_name (), "<defunct>LIB.LIBCELL");
|
||||
|
||||
// NOTE: the box on 1/0 retained
|
||||
EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {LIBCELL}\nbox 1 0 {0 0} {100 200}\nend_cell\nend_lib\n");
|
||||
|
||||
// switch to another LIBCELL, this time using layer 2/0
|
||||
m.transaction ("switch_to_b");
|
||||
l.set_technology_name ("B");
|
||||
m.commit ();
|
||||
|
||||
EXPECT_EQ (l.technology_name (), "B");
|
||||
cell = &l.cell (l.cell_by_name ("LIBCELL").second);
|
||||
EXPECT_EQ (dynamic_cast<db::LibraryProxy *> (cell) != 0, true);
|
||||
EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {LIBCELL}\nbox 2 0 {0 0} {200 100}\nend_cell\nend_lib\n");
|
||||
|
||||
m.undo ();
|
||||
EXPECT_EQ (l.technology_name (), "C");
|
||||
|
||||
cell = &l.cell (l.cell_by_name ("LIBCELL").second);
|
||||
EXPECT_EQ (dynamic_cast<db::ColdProxy *> (cell) != 0, true);
|
||||
EXPECT_EQ (cell->get_qualified_name (), "<defunct>LIB.LIBCELL");
|
||||
EXPECT_EQ (cell->get_basic_name (), "<defunct>LIBCELL");
|
||||
EXPECT_EQ (cell->get_display_name (), "<defunct>LIB.LIBCELL");
|
||||
EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {LIBCELL}\nbox 1 0 {0 0} {100 200}\nend_cell\nend_lib\n");
|
||||
|
||||
m.redo ();
|
||||
|
||||
EXPECT_EQ (l.technology_name (), "B");
|
||||
cell = &l.cell (l.cell_by_name ("LIBCELL").second);
|
||||
EXPECT_EQ (dynamic_cast<db::LibraryProxy *> (cell) != 0, true);
|
||||
EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {LIBCELL}\nbox 2 0 {0 0} {200 100}\nend_cell\nend_lib\n");
|
||||
|
||||
db::LibraryManager::instance ().delete_lib (lib_a);
|
||||
db::LibraryManager::instance ().delete_lib (lib_b);
|
||||
db::LibraryManager::instance ().delete_lib (lib_c);
|
||||
}
|
||||
|
||||
TEST(6)
|
||||
{
|
||||
// Cold proxies and context serialization
|
||||
db::Cell *cell;
|
||||
|
||||
db::Manager m;
|
||||
db::Layout l (&m);
|
||||
|
||||
EXPECT_EQ (l.technology_name (), "");
|
||||
|
||||
db::ProxyContextInfo info;
|
||||
info.lib_name = "Basic";
|
||||
info.pcell_name = "CIRCLE";
|
||||
info.pcell_parameters ["actual_radius"] = tl::Variant (10.0);
|
||||
info.pcell_parameters ["npoints"] = tl::Variant (8);
|
||||
info.pcell_parameters ["layer"] = tl::Variant (db::LayerProperties (1, 0));
|
||||
|
||||
m.transaction ("import");
|
||||
cell = l.recover_proxy (info);
|
||||
m.commit ();
|
||||
EXPECT_EQ (cell->get_qualified_name (), "Basic.CIRCLE");
|
||||
EXPECT_EQ (cell->get_basic_name (), "CIRCLE");
|
||||
EXPECT_EQ (cell->get_display_name (), "Basic.CIRCLE(l=1/0,r=10,n=8)");
|
||||
|
||||
EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {CIRCLE}\nboundary 1 0 {-4142 -10000} {-10000 -4142} {-10000 4142} {-4142 10000} {4142 10000} {10000 4142} {10000 -4142} {4142 -10000} {-4142 -10000}\nend_cell\nend_lib\n");
|
||||
|
||||
db::ProxyContextInfo info2;
|
||||
l.get_context_info (cell->cell_index (), info2);
|
||||
info2.pcell_parameters ["actual_radius"] = tl::Variant (5.0);
|
||||
|
||||
m.transaction ("modify");
|
||||
db::cell_index_type ci = cell->cell_index ();
|
||||
l.recover_proxy_as (ci, info2);
|
||||
m.commit ();
|
||||
cell = &l.cell (ci);
|
||||
EXPECT_EQ (cell->get_qualified_name (), "Basic.CIRCLE");
|
||||
EXPECT_EQ (cell->get_basic_name (), "CIRCLE");
|
||||
EXPECT_EQ (cell->get_display_name (), "Basic.CIRCLE(l=1/0,r=5,n=8)");
|
||||
|
||||
EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {CIRCLE}\nboundary 1 0 {-2071 -5000} {-5000 -2071} {-5000 2071} {-2071 5000} {2071 5000} {5000 2071} {5000 -2071} {2071 -5000} {-2071 -5000}\nend_cell\nend_lib\n");
|
||||
|
||||
m.undo ();
|
||||
EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {CIRCLE}\nboundary 1 0 {-4142 -10000} {-10000 -4142} {-10000 4142} {-4142 10000} {4142 10000} {10000 4142} {10000 -4142} {4142 -10000} {-4142 -10000}\nend_cell\nend_lib\n");
|
||||
m.redo ();
|
||||
EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {CIRCLE}\nboundary 1 0 {-2071 -5000} {-5000 -2071} {-5000 2071} {-2071 5000} {2071 5000} {5000 2071} {5000 -2071} {2071 -5000} {-2071 -5000}\nend_cell\nend_lib\n");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -285,15 +285,33 @@ bool
|
|||
TechnologyController::menu_activated (const std::string &symbol) const
|
||||
{
|
||||
if (symbol == "technology_selector:apply_technology") {
|
||||
|
||||
if (lay::LayoutView::current () && lay::LayoutView::current ()->active_cellview ().is_valid ()) {
|
||||
// Cancels the current modes - changing the technology may make libraries unavailable
|
||||
// for example.
|
||||
|
||||
if (mp_mw) {
|
||||
|
||||
// Cancels the current modes - changing the technology may make libraries unavailable
|
||||
// for example.
|
||||
mp_mw->cancel ();
|
||||
|
||||
// apply technology with undo
|
||||
mp_mw->manager ().transaction (tl::sprintf (tl::to_string (tr ("Apply technology '%s'")), m_current_technology));
|
||||
try {
|
||||
lay::LayoutView::current ()->active_cellview ()->apply_technology (m_current_technology);
|
||||
mp_mw->manager ().commit ();
|
||||
} catch (...) {
|
||||
mp_mw->manager ().cancel ();
|
||||
throw;
|
||||
}
|
||||
|
||||
} else {
|
||||
lay::LayoutView::current ()->active_cellview ()->apply_technology (m_current_technology);
|
||||
}
|
||||
lay::LayoutView::current ()->active_cellview ()->apply_technology (m_current_technology);
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
return lay::PluginDeclaration::menu_activated (symbol);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,6 +59,8 @@ LayoutHandle::LayoutHandle (db::Layout *layout, const std::string &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);
|
||||
|
||||
|
|
@ -108,6 +110,12 @@ LayoutHandle::~LayoutHandle ()
|
|||
file_watcher ().remove_file (filename ());
|
||||
}
|
||||
|
||||
void
|
||||
LayoutHandle::on_technology_changed ()
|
||||
{
|
||||
technology_changed_event ();
|
||||
}
|
||||
|
||||
void
|
||||
LayoutHandle::layout_changed ()
|
||||
{
|
||||
|
|
@ -232,7 +240,6 @@ LayoutHandle::set_tech_name (const std::string &tn)
|
|||
{
|
||||
if (mp_layout && tn != tech_name ()) {
|
||||
mp_layout->set_technology_name (tn);
|
||||
technology_changed_event ();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -302,6 +302,8 @@ private:
|
|||
bool m_save_options_valid;
|
||||
db::LoadLayoutOptions m_load_options;
|
||||
|
||||
void on_technology_changed ();
|
||||
|
||||
static std::map <std::string, LayoutHandle *> ms_dict;
|
||||
static tl::FileSystemWatcher *mp_file_watcher;
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue