/* KLayout Layout Viewer Copyright (C) 2006-2017 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 "layCellView.h" #include "layLayoutView.h" #include "layStream.h" #include "dbLayout.h" #include "dbWriter.h" #include "dbReader.h" #include "tlLog.h" #include "tlStaticObjects.h" #include namespace lay { // ------------------------------------------------------------- static std::string filename_for_caption (const std::string &fn) { const char *cp = fn.c_str (); const char *cpp = cp + fn.size (); while (cpp > cp && cpp [-1] != '\\' && cpp [-1] != '/') { --cpp; } return cpp; } // ------------------------------------------------------------- // LayoutHandle implementation LayoutHandle::LayoutHandle (db::Layout *layout, const std::string &filename) : mp_layout (layout), m_ref_count (0), m_filename (filename), m_dirty (false), m_save_options_valid (false) { file_watcher ().add_file (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); } file_watcher ().remove_file (filename ()); } void LayoutHandle::layout_changed () { m_dirty = true; } void LayoutHandle::rename (const std::string &name, bool force) throw (tl::Exception) { 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; } 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) { file_watcher ().remove_file (m_filename); m_filename = fn; file_watcher ().add_file (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 lay::Technology * LayoutHandle::technology () const { return lay::Technologies::instance ()->technology_by_name (m_tech_name); } 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 (tn != m_tech_name) { m_tech_name = tn; technology_changed_event (); } } LayoutHandle * LayoutHandle::find (const std::string &name) { std::map ::const_iterator h = ms_dict.find (name); if (h == ms_dict.end ()) { return 0; } else { return h->second; } } void LayoutHandle::get_names (std::vector &names) { names.clear (); names.reserve (ms_dict.size ()); for (std::map ::const_iterator h = ms_dict.begin (); h != ms_dict.end (); ++h) { names.push_back (h->first); } } void LayoutHandle::set_save_options (const db::SaveLayoutOptions &options, bool valid) { m_save_options = options; m_save_options_valid = valid; } void LayoutHandle::save_as (const std::string &fn, tl::OutputStream::OutputStreamMode om, const db::SaveLayoutOptions &options, bool update) { if (update) { m_save_options = options; m_save_options_valid = true; // 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 (); file_watcher ().remove_file (filename ()); rename (filename_for_caption (fn)); set_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); writer.write (*mp_layout, stream); } if (update) { file_watcher ().add_file (filename ()); m_dirty = false; } } catch (...) { if (update) { file_watcher ().add_file (filename ()); } throw; } } db::LayerMap LayoutHandle::load (const db::LoadLayoutOptions &options, const std::string &technology) { m_load_options = options; set_tech_name (technology); tl::InputStream stream (m_filename); db::Reader reader (stream); db::LayerMap new_lmap = reader.read (layout (), m_load_options); // Update the file's data: file_watcher ().remove_file (filename ()); file_watcher ().add_file (filename ()); m_dirty = false; return new_lmap; } db::LayerMap LayoutHandle::load () { m_load_options = db::LoadLayoutOptions (); set_tech_name (std::string ()); tl::InputStream stream (m_filename); db::Reader reader (stream); db::LayerMap new_lmap = reader.read (layout (), m_load_options); // Update the file's data: file_watcher ().remove_file (filename ()); file_watcher ().add_file (filename ()); m_dirty = false; return new_lmap; } 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; } std::map LayoutHandle::ms_dict; tl::FileSystemWatcher *LayoutHandle::mp_file_watcher = 0; // ------------------------------------------------------------- // 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) { 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 CellView::CellView () : mp_ctx_cell (0), m_ctx_cell_index (0), mp_cell (0), m_cell_index (cell_index_type (-1)) { } bool CellView::operator== (const CellView &cv) const { return m_layout_href == cv.m_layout_href && mp_ctx_cell == cv.mp_ctx_cell && m_ctx_cell_index == cv.m_ctx_cell_index && mp_cell == cv.mp_cell && m_cell_index == cv.m_cell_index && m_unspecific_path == cv.m_unspecific_path && m_specific_path == cv.m_specific_path; } bool CellView::is_valid () const { if (m_layout_href.get () == 0 || mp_cell == 0) { return false; } // check, if the path references valid cell indices. for (specific_cell_path_type::const_iterator pp = m_specific_path.begin (); pp != m_specific_path.end (); ++pp) { if (! m_layout_href.get ()->layout ().is_valid_cell_index (pp->inst_ptr.cell_index ())) { return false; } } for (unspecific_cell_path_type::const_iterator pp = m_unspecific_path.begin (); pp != m_unspecific_path.end (); ++pp) { if (! m_layout_href.get ()->layout ().is_valid_cell_index (*pp)) { return false; } } return true; } void CellView::set_unspecific_path (const unspecific_cell_path_type &p) { tl_assert (m_layout_href.get () != 0); mp_cell = 0; m_cell_index = 0; m_unspecific_path = p; m_specific_path.clear (); if (p.size () > 0 && m_layout_href.get () && p.back () < m_layout_href->layout ().cells ()) { m_cell_index = p.back (); mp_cell = &m_layout_href->layout ().cell (p.back ()); } mp_ctx_cell = mp_cell; m_ctx_cell_index = m_cell_index; } void CellView::set_specific_path (const specific_cell_path_type &p) { tl_assert (m_layout_href.get () != 0); m_specific_path = p; for (specific_cell_path_type::iterator pp = m_specific_path.begin (); pp != m_specific_path.end (); ++pp) { // fix elements of the path not associated with a certain array instance (this may happen if // unspecific selections are put into the path) if (pp->array_inst.at_end ()) { pp->array_inst = pp->inst_ptr.begin (); } } if (p.empty ()) { m_cell_index = m_ctx_cell_index; mp_cell = mp_ctx_cell; } else if (m_layout_href.get () && p.back ().inst_ptr.cell_index () < m_layout_href->layout ().cells ()) { m_cell_index = p.back ().inst_ptr.cell_index (); mp_cell = &m_layout_href->layout ().cell (m_cell_index); } else { reset_cell (); } } CellView::unspecific_cell_path_type CellView::combined_unspecific_path () const { CellView::unspecific_cell_path_type path; path.reserve (m_unspecific_path.size () + m_specific_path.size ()); path.insert (path.end (), m_unspecific_path.begin (), m_unspecific_path.end ()); for (CellView::specific_cell_path_type::const_iterator p = m_specific_path.begin (); p != m_specific_path.end (); ++p) { path.push_back (p->inst_ptr.cell_index ()); } return path; } void CellView::set_cell (cell_index_type index) { tl_assert (m_layout_href.get () != 0); db::Layout &layout = m_layout_href->layout (); if (! layout.is_valid_cell_index (index)) { reset_cell (); } else { m_cell_index = index; mp_cell = &layout.cell (m_cell_index); m_unspecific_path.clear (); m_specific_path.clear (); m_unspecific_path.push_back (index); while (! layout.cell (index).is_top ()) { index = *layout.cell (index).begin_parent_cells (); m_unspecific_path.push_back (index); } std::reverse (m_unspecific_path.begin (), m_unspecific_path.end ()); mp_ctx_cell = mp_cell; m_ctx_cell_index = m_cell_index; } } void CellView::set_cell (const std::string &name) { tl_assert (m_layout_href.get () != 0); std::pair cp = m_layout_href->layout ().cell_by_name (name.c_str ()); if (cp.first) { set_cell (cp.second); } else { reset_cell (); } } void CellView::reset_cell () { mp_cell = 0; m_cell_index = cell_index_type (-1); mp_ctx_cell = 0; m_ctx_cell_index = 0; m_unspecific_path.clear (); m_specific_path.clear (); } void CellView::set (lay::LayoutHandle *handle) { reset_cell (); m_layout_href.set (handle); } CellView CellView::deep_copy (db::Manager *manager) const { CellView r; r.set (new lay::LayoutHandle (new db::Layout (manager), "")); r->layout () = (*this)->layout (); r.set_unspecific_path (unspecific_path ()); r.set_specific_path (specific_path ()); return r; } db::ICplxTrans CellView::context_trans () const { db::ICplxTrans trans; for (std::vector ::const_iterator p = specific_path ().begin (); p != specific_path ().end (); ++p) { trans = trans * p->complex_trans (); } return trans; } // ------------------------------------------------------------- // CellView implementation CellViewRef::CellViewRef () { // .. nothing yet .. } CellViewRef::CellViewRef (lay::CellView *cv, lay::LayoutView *view) : mp_cv (cv), mp_view (view) { // .. nothing yet .. } bool CellViewRef::operator== (const CellView &cv) const { if (! is_valid ()) { return false; } else { return mp_cv->operator== (cv); } } bool CellViewRef::is_valid () const { return mp_view && mp_cv; } int CellViewRef::index () const { if (!is_valid ()) { return -1; } else { return mp_view->index_of_cellview (mp_cv.get ()); } } lay::LayoutView * CellViewRef::view () { return mp_view.get (); } lay::LayoutHandle * CellViewRef::operator-> () const { if (mp_cv) { return mp_cv->handle (); } else { return 0; } } void CellViewRef::set_name (const std::string &name) { if (is_valid ()) { mp_view->rename_cellview (name, mp_view->index_of_cellview (mp_cv.get ())); } } void CellViewRef::set_unspecific_path (const CellViewRef::unspecific_cell_path_type &p) { if (is_valid ()) { lay::CellView cv = *mp_cv; cv.set_unspecific_path (p); mp_view->select_cellview (mp_view->index_of_cellview (mp_cv.get ()), cv); } } void CellViewRef::set_specific_path (const CellViewRef::specific_cell_path_type &p) { if (is_valid ()) { lay::CellView cv = *mp_cv; cv.set_specific_path (p); mp_view->select_cellview (mp_view->index_of_cellview (mp_cv.get ()), cv); } } void CellViewRef::set_cell (cell_index_type ci) { if (is_valid ()) { lay::CellView cv = *mp_cv; cv.set_cell (ci); mp_view->select_cellview (mp_view->index_of_cellview (mp_cv.get ()), cv); } } void CellViewRef::set_cell (const std::string &name) { if (is_valid ()) { lay::CellView cv = *mp_cv; cv.set_cell (name); mp_view->select_cellview (mp_view->index_of_cellview (mp_cv.get ()), cv); } } void CellViewRef::reset_cell () { if (is_valid ()) { lay::CellView cv = *mp_cv; cv.reset_cell (); mp_view->select_cellview (mp_view->index_of_cellview (mp_cv.get ()), cv); } } db::Cell * CellViewRef::ctx_cell () const { return is_valid () ? mp_cv->ctx_cell () : 0; } db::Cell * CellViewRef::cell () const { return is_valid () ? mp_cv->cell () : 0; } CellViewRef::unspecific_cell_path_type CellViewRef::combined_unspecific_path () const { if (is_valid ()) { return mp_cv->combined_unspecific_path (); } else { return CellViewRef::unspecific_cell_path_type (); } } const CellViewRef::unspecific_cell_path_type & CellViewRef::unspecific_path () const { if (is_valid ()) { return mp_cv->unspecific_path (); } else { static CellViewRef::unspecific_cell_path_type empty_path; return empty_path; } } const CellViewRef::specific_cell_path_type & CellViewRef::specific_path () const { if (is_valid ()) { return mp_cv->specific_path (); } else { static CellViewRef::specific_cell_path_type empty_path; return empty_path; } } db::ICplxTrans CellViewRef::context_trans () const { if (is_valid ()) { return mp_cv->context_trans (); } else { return db::ICplxTrans (); } } }