diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index 90ba1e748..cdde943f9 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -1186,7 +1186,7 @@ Layout::allocate_new_cell () } void -Layout::cleanup () +Layout::cleanup (const std::set &keep) { // deleting cells may create new top cells which need to be deleted as well, hence we iterate // until there are no more cells to delete @@ -1200,6 +1200,10 @@ Layout::cleanup () } } + for (std::set::const_iterator k = keep.begin (); k != keep.end (); ++k) { + cells_to_delete.erase (*k); + } + if (cells_to_delete.empty ()) { break; } diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index efce2c173..13bf6cee6 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -45,6 +45,7 @@ #include #include +#include #include #include #include @@ -1446,9 +1447,10 @@ public: /** * @brief Cleans up the layout * - * This method removes proxy objects which are no longer used + * This method removes proxy objects which are no longer used. + * It can be given a list of cells which need to be kept. */ - void cleanup (); + void cleanup (const std::set &keep = std::set ()); /** * @brief Implementation of the undo operations diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 9138a3e4f..bb7399b35 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -208,7 +208,7 @@ size_t LayoutToNetlist::link_net_to_parent_circuit (const Net *subcircuit_net, C connected_clusters &parent_net_clusters = m_net_clusters.clusters_per_cell (parent_circuit->cell_index ()); - size_t id = parent_net_clusters.insert_dummy (); // @@@ + size_t id = parent_net_clusters.insert_dummy (); parent_net_clusters.add_connection (id, db::ClusterInstance (subcircuit_net->cluster_id (), subcircuit_net->circuit ()->cell_index (), trans, 0)); return id; diff --git a/src/db/db/dbLibrary.cc b/src/db/db/dbLibrary.cc index 7ea6b28ed..71989274e 100644 --- a/src/db/db/dbLibrary.cc +++ b/src/db/db/dbLibrary.cc @@ -51,6 +51,7 @@ Library::register_proxy (db::LibraryProxy *lib_proxy, db::Layout *ly) { m_referrers.insert (std::make_pair (ly, 0)).first->second += 1; m_refcount.insert (std::make_pair (lib_proxy->library_cell_index (), 0)).first->second += 1; + retired_state_changed_event (); } void @@ -73,10 +74,38 @@ Library::unregister_proxy (db::LibraryProxy *lib_proxy, db::Layout *ly) layout ().delete_cell (ci); } } + retired_state_changed_event (); } } -void +void +Library::retire_proxy (db::LibraryProxy *lib_proxy) +{ + m_retired_count.insert (std::make_pair (lib_proxy->library_cell_index (), 0)).first->second += 1; + retired_state_changed_event (); +} + +void +Library::unretire_proxy (db::LibraryProxy *lib_proxy) +{ + std::map::iterator c = m_retired_count.find (lib_proxy->library_cell_index ()); + if (c != m_retired_count.end ()) { + if (! --c->second) { + m_retired_count.erase (c); + } + retired_state_changed_event (); + } +} + +bool +Library::is_retired (const db::cell_index_type library_cell_index) const +{ + std::map::const_iterator i = m_refcount.find (library_cell_index); + std::map::const_iterator j = m_retired_count.find (library_cell_index); + return (i != m_refcount.end () && j != m_retired_count.end () && i->second == j->second); +} + +void Library::remap_to (db::Library *other) { // Hint: in the loop over the referrers we might unregister (delete from m_referrers) a referrer because no more cells refer to us. diff --git a/src/db/db/dbLibrary.h b/src/db/db/dbLibrary.h index 9798f381e..3d1794413 100644 --- a/src/db/db/dbLibrary.h +++ b/src/db/db/dbLibrary.h @@ -28,6 +28,7 @@ #include "gsiObject.h" #include "dbLayout.h" #include "tlTypeTraits.h" +#include "tlObject.h" #include @@ -45,7 +46,7 @@ class Layout; * is provided. To do so, this class must be reimplemented. */ class DB_PUBLIC Library - : public gsi::ObjectBase + : public gsi::ObjectBase, public tl::Object { public: /** @@ -162,6 +163,26 @@ public: */ void unregister_proxy (db::LibraryProxy *lib_proxy, db::Layout *layout); + /** + * @brief Retires a LibraryProxy in the given layout + * + * A proxy becomes entirely retired if the refcount is equal to the + * retired count. This feature is used to decide whether a proxy + * is actually used or only present as a shadow object for the transaction + * management. + */ + void retire_proxy (db::LibraryProxy *lib_proxy); + + /** + * @brief Unretires the Library proxy + */ + void unretire_proxy (db::LibraryProxy *lib_proxy); + + /** + * @brief Gets a value indicating whether a proxy is entirely retired + */ + bool is_retired (const cell_index_type library_cell_index) const; + /** * @brief Remap the library proxies to a different library * @@ -169,6 +190,11 @@ public: */ void remap_to (db::Library *other); + /** + * @brief This event is fired if proxies get retired on unretired + */ + tl::Event retired_state_changed_event; + private: std::string m_name; std::string m_description; @@ -176,7 +202,7 @@ private: lib_id_type m_id; db::Layout m_layout; std::map m_referrers; - std::map m_refcount; + std::map m_refcount, m_retired_count; // no copying. Library &operator=(const Library &); diff --git a/src/db/db/dbLibraryProxy.cc b/src/db/db/dbLibraryProxy.cc index 85eaf17f7..14de4c14c 100644 --- a/src/db/db/dbLibraryProxy.cc +++ b/src/db/db/dbLibraryProxy.cc @@ -59,6 +59,12 @@ LibraryProxy::unregister () if (layout ()) { layout ()->unregister_lib_proxy (this); } + if (db::LibraryManager::initialized ()) { + db::Library *lib = db::LibraryManager::instance ().lib (m_lib_id); + if (lib) { + lib->retire_proxy (this); + } + } } void @@ -67,6 +73,12 @@ LibraryProxy::reregister () if (layout ()) { layout ()->register_lib_proxy (this); } + if (db::LibraryManager::initialized ()) { + db::Library *lib = db::LibraryManager::instance ().lib (m_lib_id); + if (lib) { + lib->unretire_proxy (this); + } + } } void diff --git a/src/db/db/dbManager.h b/src/db/db/dbManager.h index 959720a8b..014fb49c4 100644 --- a/src/db/db/dbManager.h +++ b/src/db/db/dbManager.h @@ -262,10 +262,18 @@ class DB_PUBLIC Transaction { public: Transaction (db::Manager *manager, const std::string &desc) - : mp_manager (manager) + : mp_manager (manager), m_transaction_id (0) { if (mp_manager) { - mp_manager->transaction (desc); + m_transaction_id = mp_manager->transaction (desc); + } + } + + Transaction (db::Manager *manager, const std::string &desc, db::Manager::transaction_id_t join_with) + : mp_manager (manager), m_transaction_id (0) + { + if (mp_manager) { + m_transaction_id = mp_manager->transaction (desc, join_with); } } @@ -277,8 +285,14 @@ public: } } + db::Manager::transaction_id_t id () const + { + return m_transaction_id; + } + private: db::Manager *mp_manager; + db::Manager::transaction_id_t m_transaction_id; // no copying. Transaction (const Transaction &); diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index c1ee28855..0af802934 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -3005,9 +3005,6 @@ NetlistComparer::do_device_assignment (const db::Circuit *c1, const db::NetGraph ++j; } - if (i - ii == size_t(2)) { - printf("@@@1\n"); fflush(stdout); - } align (ii, i, jj, j, DeviceConnectionDistance ()); for ( ; ii != i && jj != j; ++ii, ++jj) { diff --git a/src/db/db/dbPCellDeclaration.cc b/src/db/db/dbPCellDeclaration.cc index cb579aea0..98c67cc24 100644 --- a/src/db/db/dbPCellDeclaration.cc +++ b/src/db/db/dbPCellDeclaration.cc @@ -111,5 +111,22 @@ PCellDeclaration::map_parameters (const std::map ¶ return new_param; } +std::map +PCellDeclaration::named_parameters (const pcell_parameters_type &pv) const +{ + std::map np; + + const std::vector &pcp = parameter_declarations (); + for (std::vector::const_iterator pd = pcp.begin (); pd != pcp.end (); ++pd) { + size_t index = pd - pcp.begin (); + if (index >= pv.size ()) { + break; + } + np.insert (std::make_pair (pd->get_name (), pv [index])); + } + + return np; +} + } diff --git a/src/db/db/dbPCellDeclaration.h b/src/db/db/dbPCellDeclaration.h index 403e7bec6..d5ea922ca 100644 --- a/src/db/db/dbPCellDeclaration.h +++ b/src/db/db/dbPCellDeclaration.h @@ -496,6 +496,11 @@ public: */ pcell_parameters_type map_parameters (const std::map &named_parameters) const; + /** + * @brief Converts a parameter vector to named parameters + */ + std::map named_parameters (const pcell_parameters_type &pv) const; + protected: /** * @brief Gets a value indicating whether the PCell wants caching of the parameter declarations diff --git a/src/db/db/dbPCellHeader.h b/src/db/db/dbPCellHeader.h index e49a41f03..cbae06690 100644 --- a/src/db/db/dbPCellHeader.h +++ b/src/db/db/dbPCellHeader.h @@ -51,6 +51,7 @@ class DB_PUBLIC PCellHeader { public: typedef std::map variant_map_t; + typedef variant_map_t::const_iterator variant_iterator; /** * @brief The default constructor @@ -112,6 +113,22 @@ public: */ void register_variant (PCellVariant *variant); + /** + * @brief Iterates the variants (begin) + */ + variant_iterator begin () const + { + return m_variant_map.begin (); + } + + /** + * @brief Iterates the variants (end) + */ + variant_iterator end () const + { + return m_variant_map.end (); + } + /** * @brief Get the PCell Id for this variant */ diff --git a/src/edt/edt/PCellParametersDialog.ui b/src/edt/edt/PCellParametersDialog.ui new file mode 100644 index 000000000..ddfe5ab55 --- /dev/null +++ b/src/edt/edt/PCellParametersDialog.ui @@ -0,0 +1,73 @@ + + + PCellParametersDialog + + + + 0 + 0 + 469 + 429 + + + + Instantiation Path + + + + + + + + + QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + edt::PCellParametersPage + QWidget +
edtPCellParametersPage.h
+ 1 +
+
+ + + + buttons + rejected() + PCellParametersDialog + reject() + + + 321 + 405 + + + 337 + 423 + + + + + buttons + accepted() + PCellParametersDialog + accept() + + + 427 + 405 + + + 443 + 425 + + + + +
diff --git a/src/edt/edt/edt.pro b/src/edt/edt/edt.pro index a88af9d5b..f72811940 100644 --- a/src/edt/edt/edt.pro +++ b/src/edt/edt/edt.pro @@ -20,7 +20,8 @@ HEADERS = \ edtService.h \ edtServiceImpl.h \ edtUtils.h \ - edtCommon.h + edtCommon.h \ + edtPCellParametersDialog.h FORMS = \ AlignOptionsDialog.ui \ @@ -41,6 +42,7 @@ FORMS = \ PolygonPropertiesPage.ui \ RoundCornerOptionsDialog.ui \ TextPropertiesPage.ui \ + PCellParametersDialog.ui SOURCES = \ edtConfig.cc \ @@ -57,6 +59,7 @@ SOURCES = \ edtServiceImpl.cc \ edtUtils.cc \ gsiDeclEdt.cc \ + edtPCellParametersDialog.cc INCLUDEPATH += $$TL_INC $$GSI_INC $$LAYBASIC_INC $$DB_INC DEPENDPATH += $$TL_INC $$GSI_INC $$LAYBASIC_INC $$DB_INC diff --git a/src/edt/edt/edtEditorOptionsPages.cc b/src/edt/edt/edtEditorOptionsPages.cc index 22893aafe..b47af42b7 100644 --- a/src/edt/edt/edtEditorOptionsPages.cc +++ b/src/edt/edt/edtEditorOptionsPages.cc @@ -28,6 +28,7 @@ #include "edtEditorOptionsPages.h" #include "edtPCellParametersPage.h" #include "edtConfig.h" +#include "edtService.h" #include "tlExceptions.h" #include "layPlugin.h" #include "layLayoutView.h" @@ -643,13 +644,7 @@ EditorOptionsInst::apply (lay::Plugin *root) if (pc.first) { const db::PCellDeclaration *pc_decl = layout->pcell_declaration (pc.second); if (pc_decl) { - std::vector pv = mp_pcell_parameters->get_parameters (); - const std::vector &pcp = pc_decl->parameter_declarations (); - for (size_t i = 0; i < std::min (pv.size (), pcp.size ()); ++i) { - param += tl::to_word_or_quoted_string (pcp [i].get_name ()) + ":"; - param += pv [i].to_parsable_string (); - param += ";"; - } + param = pcell_parameters_to_string (pc_decl->named_parameters (mp_pcell_parameters->get_parameters ())); } } } @@ -742,6 +737,7 @@ EditorOptionsInst::setup (lay::Plugin *root) std::map parameters; try { tl::Extractor ex (param.c_str ()); + ex.test ("!"); // used to flag PCells while (! ex.at_end ()) { std::string n; ex.read_word_or_quoted (n); diff --git a/src/edt/edt/edtPCellParametersDialog.cc b/src/edt/edt/edtPCellParametersDialog.cc new file mode 100644 index 000000000..0b74ab42e --- /dev/null +++ b/src/edt/edt/edtPCellParametersDialog.cc @@ -0,0 +1,65 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 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 "edtPCellParametersDialog.h" + +#include + +namespace edt +{ + +PCellParametersDialog::PCellParametersDialog (QWidget *parent) + : QDialog (parent) +{ + setupUi (this); + + connect (buttons->button (QDialogButtonBox::Apply), SIGNAL (clicked ()), this, SLOT (apply_pressed ())); +} + +void +PCellParametersDialog::apply_pressed () +{ + emit parameters_changed (); + parameters_changed_event (); +} + +std::vector +PCellParametersDialog::get_parameters () +{ + return parameters->get_parameters (); +} + +void +PCellParametersDialog::set_parameters (const std::vector &p) +{ + parameters->set_parameters (p); +} + +int +PCellParametersDialog::exec (const db::Layout *layout, lay::LayoutView *view, int cv_index, const db::PCellDeclaration *pcell_decl, const db::pcell_parameters_type &p) +{ + parameters->setup (layout, view, cv_index, pcell_decl, p); + return QDialog::exec (); +} + +} diff --git a/src/edt/edt/edtPCellParametersDialog.h b/src/edt/edt/edtPCellParametersDialog.h new file mode 100644 index 000000000..58c162cbb --- /dev/null +++ b/src/edt/edt/edtPCellParametersDialog.h @@ -0,0 +1,86 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 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_edtPCellParametersDialog +#define HDR_edtPCellParametersDialog + +#include "dbPCellDeclaration.h" +#include "ui_PCellParametersDialog.h" + +#include + +namespace lay +{ + class LayoutView; +} + +namespace edt +{ + +/** + * @brief A QScrollArea that displays and allows editing PCell parameters + */ +class PCellParametersDialog + : public QDialog, private Ui::PCellParametersDialog +{ +Q_OBJECT + +public: + /** + * @brief Constructor: create a dialog showing the given parameters + * @param parent The parent widget + */ + PCellParametersDialog (QWidget *parent); + + /** + * @brief Executes the parameter dialog + * @param layout The layout in which the PCell instance resides + * @param view The layout view from which to take layers for example + * @param cv_index The index of the cellview in "view" + * @param pcell_decl The PCell declaration + * @param parameters The parameter values to show (if empty, the default values are used) + */ + int exec (const db::Layout *layout, lay::LayoutView *view, int cv_index, const db::PCellDeclaration *pcell_decl, const db::pcell_parameters_type &p); + + /** + * @brief Get the current parameters + */ + std::vector get_parameters (); + + /** + * @brief Sets the given parameters as values + */ + void set_parameters (const std::vector &values); + + tl::Event parameters_changed_event; + +signals: + void parameters_changed (); + +private slots: + void apply_pressed (); +}; + +} + +#endif diff --git a/src/edt/edt/edtPCellParametersPage.cc b/src/edt/edt/edtPCellParametersPage.cc index a851e3508..6c00105e4 100644 --- a/src/edt/edt/edtPCellParametersPage.cc +++ b/src/edt/edt/edtPCellParametersPage.cc @@ -129,15 +129,30 @@ static void set_value (const db::PCellParameterDeclaration &p, const db::Layout } PCellParametersPage::PCellParametersPage (QWidget *parent, const db::Layout *layout, lay::LayoutView *view, int cv_index, const db::PCellDeclaration *pcell_decl, const db::pcell_parameters_type ¶meters) - : QFrame (parent), mp_pcell_decl (pcell_decl), mp_layout (layout), mp_view (view), m_cv_index (cv_index) + : QFrame (parent) { + init (); + setup (layout, view, cv_index, pcell_decl, parameters); +} + +PCellParametersPage::PCellParametersPage (QWidget *parent) + : QFrame (parent) +{ + init (); +} + +void +PCellParametersPage::init () +{ + mp_pcell_decl = 0; + mp_layout = 0; + mp_view = 0; + m_cv_index = 0; + mp_parameters_area = 0; + QGridLayout *frame_layout = new QGridLayout (this); setLayout (frame_layout); - mp_parameters_area = new QScrollArea (this); - frame_layout->addWidget (mp_parameters_area, 0, 0, 1, 2); - frame_layout->setRowStretch (0, 1); - mp_error_icon = new QLabel (this); mp_error_icon->setPixmap (QPixmap (":/warn.png")); mp_error_icon->hide (); @@ -153,9 +168,28 @@ PCellParametersPage::PCellParametersPage (QWidget *parent, const db::Layout *lay mp_error_label->hide (); frame_layout->addWidget (mp_error_label, 1, 1, 1, 1); frame_layout->setColumnStretch (1, 1); +} +void +PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int cv_index, const db::PCellDeclaration *pcell_decl, const db::pcell_parameters_type ¶meters) +{ + mp_pcell_decl = pcell_decl; + mp_layout = layout; + mp_view = view; + m_cv_index = cv_index; m_parameters = parameters; + if (mp_parameters_area) { + delete mp_parameters_area; + } + + m_widgets.clear (); + + mp_parameters_area = new QScrollArea (this); + QGridLayout *frame_layout = dynamic_cast (QFrame::layout ()); + frame_layout->addWidget (mp_parameters_area, 0, 0, 1, 2); + frame_layout->setRowStretch (0, 1); + QFrame *fi = new QFrame (mp_parameters_area); QWidget *inner_frame = fi; fi->setFrameShape (QFrame::NoFrame); diff --git a/src/edt/edt/edtPCellParametersPage.h b/src/edt/edt/edtPCellParametersPage.h index 5da75cfbf..020dda3b0 100644 --- a/src/edt/edt/edtPCellParametersPage.h +++ b/src/edt/edt/edtPCellParametersPage.h @@ -57,7 +57,7 @@ public: }; /** - * @brief Constructor: create a page showing the given parameters + * @brief Constructor: creates a page showing the given parameters * * @param parent The parent widget * @param layout The layout in which the PCell instance resides @@ -68,6 +68,20 @@ public: */ PCellParametersPage (QWidget *parent, const db::Layout *layout, lay::LayoutView *view, int cv_index, const db::PCellDeclaration *pcell_decl, const db::pcell_parameters_type ¶meters); + /** + * @brief Default constructor + * + * Use "setup" to configure the page. + */ + PCellParametersPage (QWidget *parent); + + /** + * @brief Delayed initialization + * + * Use this method to setup when the arguments are not available in the constructor + */ + void setup (const db::Layout *layout, lay::LayoutView *view, int cv_index, const db::PCellDeclaration *pcell_decl, const db::pcell_parameters_type ¶meters); + /** * @brief Gets the pages current state */ @@ -110,6 +124,8 @@ private: lay::LayoutView *mp_view; int m_cv_index; db::pcell_parameters_type m_parameters; + + void init (); }; } diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc index 8047460cd..254f3976c 100644 --- a/src/edt/edt/edtService.cc +++ b/src/edt/edt/edtService.cc @@ -60,6 +60,45 @@ ac_from_buttons (unsigned int buttons) // ------------------------------------------------------------- +std::string pcell_parameters_to_string (const std::map ¶meters) +{ + std::string param; + + param = "!"; // flags PCells + for (std::map::const_iterator p = parameters.begin (); p != parameters.end (); ++p) { + param += tl::to_word_or_quoted_string (p->first); + param += ":"; + param += p->second.to_parsable_string (); + param += ";"; + } + + return param; +} + +std::map pcell_parameters_from_string (const std::string &s) +{ + tl::Extractor ex (s.c_str ()); + std::map pm; + + ex.test ("!"); + + try { + while (! ex.at_end ()) { + std::string n; + ex.read_word_or_quoted (n); + ex.test (":"); + ex.read (pm.insert (std::make_pair (n, tl::Variant ())).first->second); + ex.test (";"); + } + } catch (...) { + // ignore errors + } + + return pm; +} + +// ------------------------------------------------------------- + Service::Service (db::Manager *manager, lay::LayoutView *view, db::ShapeIterator::flags_type flags) : lay::ViewService (view->view_object_widget ()), lay::Editable (view), diff --git a/src/edt/edt/edtService.h b/src/edt/edt/edtService.h index a4c8aae54..b5861407d 100644 --- a/src/edt/edt/edtService.h +++ b/src/edt/edt/edtService.h @@ -57,6 +57,18 @@ extern lay::angle_constraint_type ac_from_buttons (unsigned int buttons); // ------------------------------------------------------------- +/** + * @brief Utility function: serialize PCell parameters into a string + */ +std::string pcell_parameters_to_string (const std::map ¶meters); + +/** + * @brief Utility: deserialize PCell parameters from a string + */ +std::map pcell_parameters_from_string (const std::string &s); + +// ------------------------------------------------------------- + class EDT_PUBLIC Service : public lay::ViewService, public lay::Editable, diff --git a/src/edt/edt/edtServiceImpl.cc b/src/edt/edt/edtServiceImpl.cc index f8ab4eac6..8de181dc9 100644 --- a/src/edt/edt/edtServiceImpl.cc +++ b/src/edt/edt/edtServiceImpl.cc @@ -25,6 +25,8 @@ #include "edtServiceImpl.h" #include "edtPropertiesPages.h" #include "edtInstPropertiesPage.h" +#include "edtPCellParametersDialog.h" +#include "edtService.h" #include "dbEdge.h" #include "dbLibrary.h" #include "dbLibraryManager.h" @@ -1107,12 +1109,12 @@ PathService::config_finalize () InstService::InstService (db::Manager *manager, lay::LayoutView *view) : edt::Service (manager, view), m_angle (0.0), m_scale (1.0), - m_mirror (false), m_cell_name (""), m_lib_name (""), m_pcell_parameters (""), + m_mirror (false), m_is_pcell (false), m_array (false), m_rows (1), m_columns (1), m_row_x (0.0), m_row_y (0.0), m_column_x (0.0), m_column_y (0.0), m_place_origin (false), m_reference_transaction_id (0), m_needs_update (true), m_has_valid_cell (false), m_in_drag_drop (false), - m_current_cell (0), m_drag_drop_cell (0), m_cv_index (-1) + m_current_cell (0), mp_current_layout (0), mp_pcell_decl (0), m_cv_index (-1) { // .. nothing yet .. } @@ -1138,26 +1140,75 @@ InstService::do_activated () return true; // start editing immediately } +tl::Variant +InstService::get_default_layer_for_pcell () +{ + lay::LayerPropertiesConstIterator cl = view ()->current_layer (); + if (! cl.is_null () && ! cl->has_children () && (cl->source (true).cv_index() < 0 || cl->source (true).cv_index () == view ()->active_cellview_index ())) { + db::LayerProperties lp = cl->source (true).layer_props (); + if (! lp.is_null ()) { + return tl::Variant (lp); + } + } + + return tl::Variant (); +} + bool InstService::drag_enter_event (const db::DPoint &p, const lay::DragDropDataBase *data) { const lay::CellDragDropData *cd = dynamic_cast (data); - if (view ()->is_editable () && cd && cd->layout () == & view ()->active_cellview ()->layout ()) { + if (view ()->is_editable () && cd && (cd->layout () == & view ()->active_cellview ()->layout () || cd->library ())) { view ()->cancel (); - // NOTE: the cancel above might delete the cell we are dragging (if that is - // a non-placed PCell). Hence we need to check whether the cell still is valid - if (cd->layout ()->is_valid_cell_index (cd->cell_index ())) { + set_edit_marker (0); - set_edit_marker (0); + m_cv_index = view ()->active_cellview_index (); + m_in_drag_drop = true; - m_cv_index = view ()->active_cellview_index (); - m_in_drag_drop = true; - m_drag_drop_cell = cd->cell_index (); + if (cd->library ()) { + if (m_lib_name != cd->library ()->get_name ()) { + m_lib_name = cd->library ()->get_name (); + m_pcell_parameters.clear (); + } + } else { + m_lib_name.clear (); + } + m_is_pcell = false; + + if (cd->is_pcell ()) { + + const db::PCellDeclaration *pcell_decl = cd->layout ()->pcell_declaration (cd->cell_index ()); + if (pcell_decl) { + + if (m_cell_or_pcell_name != pcell_decl->name ()) { + m_cell_or_pcell_name = pcell_decl->name (); + m_pcell_parameters.clear (); + } + + m_is_pcell = true; + + // NOTE: we reuse previous parameters for convenience unless PCell or library has changed + const std::vector &pd = pcell_decl->parameter_declarations(); + for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { + if (i->get_type () == db::PCellParameterDeclaration::t_layer && !i->is_hidden () && !i->is_readonly () && i->get_default ().is_nil ()) { + m_pcell_parameters.insert (std::make_pair (i->get_name (), get_default_layer_for_pcell ())); + } else { + m_pcell_parameters.insert (std::make_pair (i->get_name (), i->get_default ())); + } + } + + do_begin_edit (p); + return true; + + } + + } else if (cd->layout ()->is_valid_cell_index (cd->cell_index ())) { + + m_cell_or_pcell_name = cd->layout ()->cell_name (cd->cell_index ()); do_begin_edit (p); - return true; } @@ -1197,14 +1248,69 @@ bool InstService::drop_event (const db::DPoint & /*p*/, const lay::DragDropDataBase * /*data*/) { if (m_in_drag_drop) { + + const lay::CellView &cv = view ()->cellview (m_cv_index); + if (! cv.is_valid ()) { + return false; + } + + make_cell (cv); + + bool accepted = true; + + if (m_has_valid_cell && mp_pcell_decl) { + + std::vector pv = mp_pcell_decl->map_parameters (m_pcell_parameters); + + // Turn off the drag cursor for the modal dialog + QApplication::restoreOverrideCursor (); + + // for PCells dragged show the parameter dialog for a chance to edit the initial parameters + if (! mp_pcell_parameters_dialog.get ()) { + mp_pcell_parameters_dialog.reset (new edt::PCellParametersDialog (view ())); + mp_pcell_parameters_dialog->parameters_changed_event.add (this, &InstService::apply_edits); + } + + if (! mp_pcell_parameters_dialog->exec (mp_current_layout, view (), m_cv_index, mp_pcell_decl, pv)) { + accepted = false; + } else { + m_has_valid_cell = false; + m_pcell_parameters = mp_pcell_decl->named_parameters (mp_pcell_parameters_dialog->get_parameters ()); + } + + } + set_edit_marker (0); - do_finish_edit (); + + if (accepted) { + do_finish_edit (); + } else { + do_cancel_edit (); + } + + sync_to_config (); return true; + } else { return false; } } +void +InstService::sync_to_config () +{ + // push the current setup to configuration so the instance dialog will take these as default + // and "apply" of these instance properties doesn't fail because of insistency. + plugin_root ()->config_set (cfg_edit_inst_lib_name, m_lib_name); + plugin_root ()->config_set (cfg_edit_inst_cell_name, m_cell_or_pcell_name); + if (m_is_pcell) { + plugin_root ()->config_set (cfg_edit_inst_pcell_parameters, pcell_parameters_to_string (m_pcell_parameters)); + } else { + plugin_root ()->config_set (cfg_edit_inst_pcell_parameters, std::string ()); + } + plugin_root ()->config_end (); +} + void InstService::do_begin_edit (const db::DPoint &p) { @@ -1260,89 +1366,79 @@ InstService::do_begin_edit (const db::DPoint &p) std::pair InstService::make_cell (const lay::CellView &cv) { - if (m_in_drag_drop) { - return std::make_pair (true, m_drag_drop_cell); - } - if (m_has_valid_cell) { return std::make_pair (true, m_current_cell); } + // NOTE: do this at the beginning: creating a transaction might delete transactions behind the + // head transaction, hence releasing (thus: deleting) cells. To prevert interference, create + // the transaction at the beginning. + db::Transaction tr (manager (), tl::to_string (QObject::tr ("Create reference cell")), m_reference_transaction_id); + m_reference_transaction_id = tr.id (); + lay::LayerState layer_state = view ()->layer_snapshot (); - db::Layout *layout; db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (m_lib_name); // find the layout the cell has to be looked up: that is either the layout of the current instance or // the library selected if (lib) { - layout = &lib->layout (); + mp_current_layout = &lib->layout (); } else { - layout = &cv->layout (); + mp_current_layout = &cv->layout (); + } + + std::pair ci (false, db::cell_index_type (0)); + std::pair pci (false, db::pcell_id_type (0)); + + if (! m_is_pcell) { + ci = mp_current_layout->cell_by_name (m_cell_or_pcell_name.c_str ()); + } else { + pci = mp_current_layout->pcell_by_name (m_cell_or_pcell_name.c_str ()); } - std::pair ci = layout->cell_by_name (m_cell_name.c_str ()); - std::pair pci = layout->pcell_by_name (m_cell_name.c_str ()); if (! ci.first && ! pci.first) { - // throw tl::Exception (tl::to_string (QObject::tr ("Not a valid cell name: %s")).c_str (), tl::to_string (cell_name_le->text ()).c_str ()); return std::pair (false, 0); } - m_reference_transaction_id = manager ()->transaction (tl::to_string (QObject::tr ("Create reference cell")), m_reference_transaction_id); db::cell_index_type inst_cell_index = ci.second; - try { + mp_pcell_decl = 0; - // instantiate the PCell - if (pci.first) { + // instantiate the PCell + if (pci.first) { - std::vector pv; + std::vector pv; - const db::PCellDeclaration *pc_decl = layout->pcell_declaration (pci.second); - if (pc_decl) { + mp_pcell_decl = mp_current_layout->pcell_declaration (pci.second); + if (mp_pcell_decl) { - std::map parameters; - tl::Extractor ex (m_pcell_parameters.c_str ()); - while (! ex.at_end ()) { - std::string n; - ex.read_word_or_quoted (n); - ex.test (":"); - ex.read (parameters.insert (std::make_pair (n, tl::Variant ())).first->second); - ex.test (";"); - } + pv = mp_pcell_decl->map_parameters (m_pcell_parameters); - const std::vector &pcp = pc_decl->parameter_declarations (); - for (std::vector::const_iterator pd = pcp.begin (); pd != pcp.end (); ++pd) { - std::map::const_iterator p = parameters.find (pd->get_name ()); - if (p != parameters.end ()) { - pv.push_back (p->second); - } else { - pv.push_back (pd->get_default ()); - } - } - - } - - inst_cell_index = layout->get_pcell_variant (pci.second, pv); + // make the parameters fit (i.e. PCells may not define consistent default parameters) + mp_pcell_decl->coerce_parameters (*mp_current_layout, pv); } - // reference the library - if (lib) { - layout = & cv->layout (); - layout->cleanup (); - inst_cell_index = layout->get_lib_proxy (lib, inst_cell_index); - } + inst_cell_index = mp_current_layout->get_pcell_variant (pci.second, pv); - view ()->add_new_layers (layer_state); - - manager ()->commit (); - - } catch (...) { - manager ()->commit (); - throw; } + // reference the library + if (lib) { + + mp_current_layout = & cv->layout (); + inst_cell_index = mp_current_layout->get_lib_proxy (lib, inst_cell_index); + + // remove unused references + std::set keep; + keep.insert (inst_cell_index); + mp_current_layout->cleanup (keep); + + } + + view ()->add_new_layers (layer_state); + m_has_valid_cell = true; m_current_cell = inst_cell_index; @@ -1479,7 +1575,7 @@ bool InstService::configure (const std::string &name, const std::string &value) { if (name == cfg_edit_inst_cell_name) { - m_cell_name = value; + m_cell_or_pcell_name = value; m_needs_update = true; return true; // taken } @@ -1491,9 +1587,13 @@ InstService::configure (const std::string &name, const std::string &value) } if (name == cfg_edit_inst_pcell_parameters) { - m_pcell_parameters = value; + + m_pcell_parameters = pcell_parameters_from_string (value); + m_is_pcell = ! value.empty (); + m_needs_update = true; return true; // taken + } if (name == cfg_edit_inst_place_origin) { @@ -1577,6 +1677,16 @@ InstService::config_finalize () edt::Service::config_finalize (); } +void +InstService::apply_edits() +{ + if (mp_pcell_decl && mp_pcell_parameters_dialog.get ()) { + m_pcell_parameters = mp_pcell_decl->named_parameters (mp_pcell_parameters_dialog->get_parameters ()); + } + + sync_to_config (); +} + void InstService::update_marker () { diff --git a/src/edt/edt/edtServiceImpl.h b/src/edt/edt/edtServiceImpl.h index 6a1b05e1e..ca31dd672 100644 --- a/src/edt/edt/edtServiceImpl.h +++ b/src/edt/edt/edtServiceImpl.h @@ -27,6 +27,8 @@ #include "edtService.h" #include "edtConfig.h" +#include + namespace lay { class CellView; @@ -35,6 +37,8 @@ namespace lay namespace edt { +class PCellParametersDialog; + /** * @brief Implementation of the edt::Service for generic shape editing */ @@ -223,7 +227,9 @@ private: double m_scale; bool m_mirror; db::DPoint m_disp; - std::string m_cell_name, m_lib_name, m_pcell_parameters; + std::string m_cell_or_pcell_name, m_lib_name; + std::map m_pcell_parameters; + bool m_is_pcell; bool m_array; unsigned int m_rows, m_columns; double m_row_x, m_row_y, m_column_x, m_column_y; @@ -232,13 +238,19 @@ private: bool m_needs_update; bool m_has_valid_cell; bool m_in_drag_drop; - db::cell_index_type m_current_cell, m_drag_drop_cell; + db::cell_index_type m_current_cell; + db::Layout *mp_current_layout; + const db::PCellDeclaration *mp_pcell_decl; int m_cv_index; db::ICplxTrans m_trans; + std::auto_ptr mp_pcell_parameters_dialog; void update_marker (); + void apply_edits (); bool get_inst (db::CellInstArray &inst); std::pair make_cell (const lay::CellView &cv); + tl::Variant get_default_layer_for_pcell (); + void sync_to_config (); }; } diff --git a/src/klayout.pro b/src/klayout.pro index e8e3bfafa..fbf20df44 100644 --- a/src/klayout.pro +++ b/src/klayout.pro @@ -101,6 +101,3 @@ plugins.depends += lib rdb db unit_tests.depends += plugins $$MAIN_DEPENDS -RESOURCES += \ - laybasic/laybasic/layResources.qrc - diff --git a/src/lay/lay/doc/manual/basic.xml b/src/lay/lay/doc/manual/basic.xml index fcceaac52..92969e177 100644 --- a/src/lay/lay/doc/manual/basic.xml +++ b/src/lay/lay/doc/manual/basic.xml @@ -17,6 +17,7 @@ + diff --git a/src/lay/lay/doc/manual/library_view.png b/src/lay/lay/doc/manual/library_view.png new file mode 100644 index 000000000..a012c31a6 Binary files /dev/null and b/src/lay/lay/doc/manual/library_view.png differ diff --git a/src/lay/lay/doc/manual/library_view.xml b/src/lay/lay/doc/manual/library_view.xml new file mode 100644 index 000000000..0ec682152 --- /dev/null +++ b/src/lay/lay/doc/manual/library_view.xml @@ -0,0 +1,41 @@ + + + + + + The Library View + + + +

+ Beside the cell list, a library view is provided as a support view + for the layout panel. This sub-panel displays the libraries available + and allows browsing the cells and PCells inside a library. +

+ +

+ By default, the library view is shown below the cell tree. You can + rearrange the views by dragging them at their title bar and docking + them in other places of the main window. + To reset the window arrangement to the default configuration, use "Restore Window" from the + "View" menu. +

+ +

+ The library view shows the cells and PCells of one library. To select a library, + choose it from the selection box at the top of the library view. +

+ +

+ PCells are shown with a small "gear" icon in the library view. If PCells are + instantiated, the variants in use are shown as entries below the PCell entry. +

+ +

+ In edit mode, cells can conveniently be placed by dragging them from the + library view to the layout canvas. If a PCell variant is dragged, another + instance of this PCell variant is created. If a PCell master is dragged, + KLayout will pop up the PCell parameter definition dialog on drop. +

+ +
diff --git a/src/lay/lay/doc/manual/main_window.png b/src/lay/lay/doc/manual/main_window.png index 92452c828..f617f9ec0 100644 Binary files a/src/lay/lay/doc/manual/main_window.png and b/src/lay/lay/doc/manual/main_window.png differ diff --git a/src/lay/lay/doc/manual/main_window.xml b/src/lay/lay/doc/manual/main_window.xml index 571e6cbc9..d4a7f6ae6 100644 --- a/src/lay/lay/doc/manual/main_window.xml +++ b/src/lay/lay/doc/manual/main_window.xml @@ -18,7 +18,7 @@

-

Left Part - The Hierarchy Browser and Navigator

+

Left Part - The Hierarchy Browser, Library View and Navigator

The left panel is the @@ -45,6 +45,20 @@

+

+ The sub-panel below the hierarchy browser is the library view. This view + shows the libraries registered in the system and their content: +

+ +

+ +

+ +

+ The library view is used to browse and place library cells, which can + be normal cells or PCells. Read more about the library view here: . +

+

The navigator is invisible by default but can be activated by checking the "Navigator" menu item in the "View" menu. The navigator shows an overview diff --git a/src/lay/lay/layConfig.h b/src/lay/lay/layConfig.h index ade67ad0b..2181893aa 100644 --- a/src/lay/lay/layConfig.h +++ b/src/lay/lay/layConfig.h @@ -49,6 +49,7 @@ static const std::string cfg_navigator_all_hier_levels ("navigator-show-all-hier static const std::string cfg_navigator_show_images ("navigator-show-images"); static const std::string cfg_show_layer_toolbox ("show-layer-toolbox"); static const std::string cfg_show_hierarchy_panel ("show-hierarchy-panel"); +static const std::string cfg_show_libraries_view ("show-libraries-view"); static const std::string cfg_show_layer_panel ("show-layer-panel"); static const std::string cfg_window_state ("window-state"); static const std::string cfg_layout_file_watcher_enabled ("layout-file-watcher-enabled"); diff --git a/src/lay/lay/layHelpResources.qrc b/src/lay/lay/layHelpResources.qrc index 22848c6aa..34dbc7efd 100644 --- a/src/lay/lay/layHelpResources.qrc +++ b/src/lay/lay/layHelpResources.qrc @@ -147,6 +147,8 @@ doc/manual/browse_instances.xml doc/manual/browse_shapes.xml doc/manual/cell_list.xml + doc/manual/library_view.xml + doc/manual/library_view.png doc/manual/cell.xml doc/manual/clip.xml doc/manual/copypaste_cell.xml diff --git a/src/lay/lay/layMainConfigPages.cc b/src/lay/lay/layMainConfigPages.cc index 67a85bacd..5b95b0229 100644 --- a/src/lay/lay/layMainConfigPages.cc +++ b/src/lay/lay/layMainConfigPages.cc @@ -68,6 +68,7 @@ public: options.push_back (std::pair (cfg_show_toolbar, "true")); options.push_back (std::pair (cfg_show_layer_toolbox, "true")); options.push_back (std::pair (cfg_show_hierarchy_panel, "true")); + options.push_back (std::pair (cfg_show_libraries_view, "true")); options.push_back (std::pair (cfg_show_layer_panel, "true")); options.push_back (std::pair (cfg_layout_file_watcher_enabled, "true")); options.push_back (std::pair (cfg_window_state, "")); diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 349ad61ec..67475dff5 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -506,6 +506,13 @@ MainWindow::MainWindow (QApplication *app, lay::Plugin *plugin_parent, const cha connect (mp_hp_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); m_hp_visible = true; + mp_libs_dock_widget = new QDockWidget (QObject::tr ("Libraries"), this); + mp_libs_dock_widget->setObjectName (QString::fromUtf8 ("libs_dock_widget")); + mp_libs_stack = new ControlWidgetStack (mp_libs_dock_widget, "libs_stack"); + mp_libs_dock_widget->setWidget (mp_libs_stack); + connect (mp_libs_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); + m_libs_visible = true; + mp_view_stack = new ViewWidgetStack (mp_main_frame); mp_view_stack->setObjectName (QString::fromUtf8 ("view_stack")); vbl->addWidget (mp_view_stack); @@ -537,6 +544,7 @@ MainWindow::MainWindow (QApplication *app, lay::Plugin *plugin_parent, const cha #endif addDockWidget(Qt::LeftDockWidgetArea, mp_navigator_dock_widget); addDockWidget(Qt::LeftDockWidgetArea, mp_hp_dock_widget); + addDockWidget(Qt::LeftDockWidgetArea, mp_libs_dock_widget); addDockWidget(Qt::RightDockWidgetArea, mp_lp_dock_widget); addDockWidget(Qt::RightDockWidgetArea, mp_layer_toolbox_dock_widget); @@ -849,6 +857,7 @@ MainWindow::init_menu () MenuLayoutEntry ("show_layer_panel", tl::to_string (QObject::tr ("Layers")), std::make_pair (cfg_show_layer_panel, "?")), MenuLayoutEntry ("show_layer_toolbox", tl::to_string (QObject::tr ("Layer Toolbox")), std::make_pair (cfg_show_layer_toolbox, "?")), MenuLayoutEntry ("show_hierarchy_panel", tl::to_string (QObject::tr ("Cells")), std::make_pair (cfg_show_hierarchy_panel, "?")), + MenuLayoutEntry ("show_libraries_view", tl::to_string (QObject::tr ("Libraries")), std::make_pair (cfg_show_libraries_view, "?")), MenuLayoutEntry ("reset_window_state", tl::to_string (QObject::tr ("Restore Window")), SLOT (cm_reset_window_state ())), MenuLayoutEntry::separator ("selection_group"), MenuLayoutEntry ("transient_selection", tl::to_string (QObject::tr ("Highlight Object Under Mouse")), std::make_pair (cfg_sel_transient_mode, "?")), @@ -1073,6 +1082,8 @@ MainWindow::dock_widget_visibility_changed (bool /*visible*/) plugin_root ()->config_set (cfg_show_layer_panel, tl::to_string (!mp_lp_dock_widget->isHidden ())); } else if (sender () == mp_hp_dock_widget) { plugin_root ()->config_set (cfg_show_hierarchy_panel, tl::to_string (!mp_hp_dock_widget->isHidden ())); + } else if (sender () == mp_libs_dock_widget) { + plugin_root ()->config_set (cfg_show_libraries_view, tl::to_string (!mp_libs_dock_widget->isHidden ())); } else if (sender () == mp_navigator_dock_widget) { plugin_root ()->config_set (cfg_show_navigator, tl::to_string (!mp_navigator_dock_widget->isHidden ())); } else if (sender () == mp_layer_toolbox_dock_widget) { @@ -1237,6 +1248,7 @@ MainWindow::close_all () mp_views.pop_back (); mp_lp_stack->removeWidget (mp_views.size ()); mp_hp_stack->removeWidget (mp_views.size ()); + mp_libs_stack->removeWidget (mp_views.size ()); mp_view_stack->removeWidget (mp_views.size ()); delete view; @@ -1685,6 +1697,17 @@ MainWindow::configure (const std::string &name, const std::string &value) return true; + } else if (name == cfg_show_libraries_view) { + + tl::from_string (value, m_libs_visible); + if (m_libs_visible) { + mp_libs_dock_widget->show (); + } else { + mp_libs_dock_widget->hide (); + } + + return true; + } else if (name == cfg_show_layer_panel) { tl::from_string (value, m_lp_visible); @@ -1797,6 +1820,7 @@ MainWindow::read_dock_widget_state () { plugin_root ()->config_set (cfg_show_layer_panel, tl::to_string (!mp_lp_dock_widget->isHidden ())); plugin_root ()->config_set (cfg_show_hierarchy_panel, tl::to_string (!mp_hp_dock_widget->isHidden ())); + plugin_root ()->config_set (cfg_show_libraries_view, tl::to_string (!mp_libs_dock_widget->isHidden ())); plugin_root ()->config_set (cfg_show_navigator, tl::to_string (!mp_navigator_dock_widget->isHidden ())); plugin_root ()->config_set (cfg_show_layer_toolbox, tl::to_string (!mp_layer_toolbox_dock_widget->isHidden ())); } @@ -1810,6 +1834,12 @@ MainWindow::update_dock_widget_state () mp_hp_dock_widget->hide (); } + if (m_libs_visible) { + mp_libs_dock_widget->show (); + } else { + mp_libs_dock_widget->hide (); + } + if (m_lp_visible) { mp_lp_dock_widget->show (); } else { @@ -3270,6 +3300,7 @@ MainWindow::select_view (int index) mp_view_stack->raiseWidget (index); mp_hp_stack->raiseWidget (index); mp_lp_stack->raiseWidget (index); + mp_libs_stack->raiseWidget (index); mp_setup_form->setup (); } @@ -3694,6 +3725,7 @@ MainWindow::clone_current_view () mp_view_stack->addWidget (view); mp_lp_stack->addWidget (view->layer_control_frame ()); mp_hp_stack->addWidget (view->hierarchy_control_frame ()); + mp_libs_stack->addWidget (view->libraries_frame ()); bool f = m_disable_tab_selected; m_disable_tab_selected = true; @@ -3940,6 +3972,7 @@ MainWindow::close_view (int index) mp_view_stack->removeWidget (index); mp_lp_stack->removeWidget (index); mp_hp_stack->removeWidget (index); + mp_libs_stack->removeWidget (index); view_closed_event (int (index)); @@ -4312,6 +4345,7 @@ MainWindow::create_view () mp_view_stack->addWidget (mp_views.back ()); mp_lp_stack->addWidget (mp_views.back ()->layer_control_frame ()); mp_hp_stack->addWidget (mp_views.back ()->hierarchy_control_frame ()); + mp_libs_stack->addWidget (mp_views.back ()->libraries_frame ()); bool f = m_disable_tab_selected; m_disable_tab_selected = true; @@ -4373,6 +4407,7 @@ MainWindow::create_or_load_layout (const std::string *filename, const db::LoadLa mp_view_stack->addWidget (mp_views.back ()); mp_lp_stack->addWidget (mp_views.back ()->layer_control_frame ()); mp_hp_stack->addWidget (mp_views.back ()->hierarchy_control_frame ()); + mp_libs_stack->addWidget (mp_views.back ()->libraries_frame ()); bool f = m_disable_tab_selected; m_disable_tab_selected = true; diff --git a/src/lay/lay/layMainWindow.h b/src/lay/lay/layMainWindow.h index 782dc512e..de4ef64ec 100644 --- a/src/lay/lay/layMainWindow.h +++ b/src/lay/lay/layMainWindow.h @@ -872,9 +872,9 @@ private: QToolBar *mp_tool_bar; QDockWidget *mp_navigator_dock_widget; lay::Navigator *mp_navigator; - QDockWidget *mp_hp_dock_widget, *mp_lp_dock_widget; - ControlWidgetStack *mp_hp_stack, *mp_lp_stack; - bool m_hp_visible, m_lp_visible, m_navigator_visible, m_layer_toolbox_visible; + QDockWidget *mp_hp_dock_widget, *mp_lp_dock_widget, *mp_libs_dock_widget; + ControlWidgetStack *mp_hp_stack, *mp_lp_stack, *mp_libs_stack; + bool m_hp_visible, m_lp_visible, m_libs_visible, m_navigator_visible, m_layer_toolbox_visible; QDockWidget *mp_layer_toolbox_dock_widget; lay::LayerToolbox *mp_layer_toolbox; ViewWidgetStack *mp_view_stack; diff --git a/src/laybasic/laybasic/layCellSelectionForm.cc b/src/laybasic/laybasic/layCellSelectionForm.cc index 8ce58c1b8..8122e686c 100644 --- a/src/laybasic/laybasic/layCellSelectionForm.cc +++ b/src/laybasic/laybasic/layCellSelectionForm.cc @@ -384,7 +384,7 @@ CellSelectionForm::select_entry (lay::CellView::cell_index_type ci) QModelIndex mi; for (int c = 0; c < model->toplevel_items (); ++c) { lay::CellTreeItem *item = model->toplevel_item (c); - if (item->cell_index () == ci) { + if (item->cell_or_pcell_index () == ci) { mi = model->model_index (item); break; } @@ -736,7 +736,7 @@ LibraryCellSelectionForm::select_pcell_entry (db::pcell_id_type pci) QModelIndex mi; for (int c = 0; c < model->toplevel_items (); ++c) { lay::CellTreeItem *item = model->toplevel_item (c); - if (item->is_pcell () && item->cell_index () == pci) { + if (item->is_pcell () && item->cell_or_pcell_index () == pci) { mi = model->model_index (item); break; } @@ -775,7 +775,7 @@ LibraryCellSelectionForm::select_entry (lay::CellView::cell_index_type ci) QModelIndex mi; for (int c = 0; c < model->toplevel_items (); ++c) { lay::CellTreeItem *item = model->toplevel_item (c); - if (item->cell_index () == ci) { + if (item->cell_or_pcell_index () == ci) { mi = model->model_index (item); break; } diff --git a/src/laybasic/laybasic/layCellTreeModel.cc b/src/laybasic/laybasic/layCellTreeModel.cc index 09ab23d67..819dba8a8 100644 --- a/src/laybasic/laybasic/layCellTreeModel.cc +++ b/src/laybasic/laybasic/layCellTreeModel.cc @@ -25,6 +25,9 @@ #include "layLayoutView.h" #include "tlGlobPattern.h" #include "dbPCellHeader.h" +#include "dbPCellVariant.h" +#include "dbLibraryProxy.h" +#include "dbLibrary.h" #include #include @@ -82,11 +85,11 @@ struct cmp_cell_tree_item_vs_name_f // -------------------------------------------------------------------- // CellTreeItem implementation -CellTreeItem::CellTreeItem (const db::Layout *layout, CellTreeItem *parent, bool is_pcell, size_t cell_or_pcell_index, bool flat, CellTreeModel::Sorting s) - : mp_layout (layout), mp_parent (parent), m_sorting (s), m_is_pcell (is_pcell), m_index (0), m_children (), m_cell_or_pcell_index (cell_or_pcell_index) +CellTreeItem::CellTreeItem (const db::Layout *layout, bool is_pcell, size_t cell_or_pcell_index, bool flat, CellTreeModel::Sorting s) + : mp_layout (layout), mp_parent (0), m_sorting (s), m_is_pcell (is_pcell), m_index (0), m_children (), m_cell_or_pcell_index (cell_or_pcell_index) { if (! flat && ! is_pcell) { - m_child_count = int (mp_layout->cell (cell_index ()).child_cells ()); + m_child_count = int (mp_layout->cell (cell_or_pcell_index).child_cells ()); } else { m_child_count = 0; } @@ -100,13 +103,19 @@ CellTreeItem::~CellTreeItem () m_children.clear (); } +bool +CellTreeItem::is_valid () const +{ + return m_is_pcell || mp_layout->is_valid_cell_index (cell_or_pcell_index ()); +} + std::string CellTreeItem::display_text () const { if (m_is_pcell) { return name (); - } else if (mp_layout->is_valid_cell_index (cell_index ())) { - return mp_layout->cell (cell_index ()).get_display_name (); + } else if (mp_layout->is_valid_cell_index (cell_or_pcell_index ())) { + return mp_layout->cell (cell_or_pcell_index ()).get_display_name (); } else { return std::string (); } @@ -125,28 +134,45 @@ CellTreeItem::child (int index) // create a list of child sub-item - const db::Cell *cell = & mp_layout->cell (cell_index ()); + const db::Cell *cell = & mp_layout->cell (cell_or_pcell_index ()); m_children.reserve (m_child_count); for (db::Cell::child_cell_iterator child = cell->begin_child_cells (); ! child.at_end (); ++child) { - CellTreeItem *child_item = new CellTreeItem (mp_layout, this, false, *child, false, m_sorting); - m_children.push_back (child_item); + add_child (new CellTreeItem (mp_layout, false, *child, false, m_sorting)); } - std::sort (m_children.begin (), m_children.end (), cmp_cell_tree_items_f (m_sorting)); - - for (size_t i = 0; i < m_children.size (); ++i) { - m_children [i]->set_index (i); - } + finish_children (); } return m_children [index]; } +void +CellTreeItem::add_child (CellTreeItem *item) +{ + // explicitly added + if (size_t (m_child_count) == m_children.size ()) { + ++m_child_count; + } + + item->mp_parent = this; + m_children.push_back (item); +} + +void +CellTreeItem::finish_children () +{ + std::sort (m_children.begin (), m_children.end (), cmp_cell_tree_items_f (m_sorting)); + + for (size_t i = 0; i < m_children.size (); ++i) { + m_children [i]->set_index (i); + } +} + db::cell_index_type -CellTreeItem::cell_index () const +CellTreeItem::cell_or_pcell_index () const { return db::cell_index_type (m_cell_or_pcell_index); } @@ -161,7 +187,7 @@ const char * CellTreeItem::name () const { if (! m_is_pcell) { - return mp_layout->cell_name (cell_index ()); + return mp_layout->cell_name (cell_or_pcell_index ()); } else { return mp_layout->pcell_header (m_cell_or_pcell_index)->get_name ().c_str (); } @@ -214,7 +240,7 @@ CellTreeItem::by_area_less_than (const CellTreeItem *b) const return m_is_pcell > b->is_pcell (); } // Hint: since mp_layout == b.mp_layout, not conversion to um^2 is required because of different DBU - return mp_layout->cell (cell_index ()).bbox ().area () < b->mp_layout->cell (b->cell_index ()).bbox ().area (); + return mp_layout->cell (cell_or_pcell_index ()).bbox ().area () < b->mp_layout->cell (b->cell_or_pcell_index ()).bbox ().area (); } bool @@ -224,7 +250,7 @@ CellTreeItem::by_area_equal_than (const CellTreeItem *b) const return false; } // Hint: since mp_layout == b.mp_layout, not conversion to um^2 is required because of different DBU - return mp_layout->cell (cell_index ()).bbox ().area () == b->mp_layout->cell (b->cell_index ()).bbox ().area (); + return mp_layout->cell (cell_or_pcell_index ()).bbox ().area () == b->mp_layout->cell (b->cell_or_pcell_index ()).bbox ().area (); } // -------------------------------------------------------------------- @@ -249,6 +275,7 @@ CellTreeModel::CellTreeModel (QWidget *parent, lay::LayoutView *view, int cv_ind m_pad = ((flags & NoPadding) == 0); mp_layout = & view->cellview (cv_index)->layout (); + mp_library = 0; tl_assert (! mp_layout->under_construction () && ! (mp_layout->manager () && mp_layout->manager ()->transacting ())); build_top_level (); @@ -269,6 +296,28 @@ CellTreeModel::CellTreeModel (QWidget *parent, db::Layout *layout, unsigned int m_pad = ((flags & NoPadding) == 0); mp_layout = layout; + mp_library = 0; + tl_assert (! mp_layout->under_construction () && ! (mp_layout->manager () && mp_layout->manager ()->transacting ())); + + build_top_level (); + + m_current_index = m_selected_indexes.begin (); +} + +CellTreeModel::CellTreeModel (QWidget *parent, db::Library *library, unsigned int flags, const db::Cell *base, Sorting sorting) + : QAbstractItemModel (parent), + m_flags (flags), + m_sorting (sorting), + mp_parent (parent), + mp_view (0), + m_cv_index (-1), + mp_base (base) +{ + m_flat = ((flags & Flat) != 0) && ((flags & TopCells) == 0); + m_pad = ((flags & NoPadding) == 0); + + mp_layout = &library->layout (); + mp_library = library; tl_assert (! mp_layout->under_construction () && ! (mp_layout->manager () && mp_layout->manager ()->transacting ())); build_top_level (); @@ -284,8 +333,26 @@ CellTreeModel::~CellTreeModel () void CellTreeModel::configure (lay::LayoutView *view, int cv_index, unsigned int flags, const db::Cell *base, Sorting sorting) { - bool flat = ((flags & Flat) != 0) && ((flags & TopCells) == 0); db::Layout *layout = & view->cellview (cv_index)->layout (); + do_configure (layout, 0, view, cv_index, flags, base, sorting); +} + +void +CellTreeModel::configure (db::Layout *layout, unsigned int flags, const db::Cell *base, Sorting sorting) +{ + do_configure (layout, 0, 0, -1, flags, base, sorting); +} + +void +CellTreeModel::configure (db::Library *library, unsigned int flags, const db::Cell *base, Sorting sorting) +{ + do_configure (& library->layout (), library, 0, -1, flags, base, sorting); +} + +void +CellTreeModel::do_configure (db::Layout *layout, db::Library *library, lay::LayoutView *view, int cv_index, unsigned int flags, const db::Cell *base, Sorting sorting) +{ + bool flat = ((flags & Flat) != 0) && ((flags & TopCells) == 0); bool need_reset = false; if (flat != m_flat || layout != mp_layout || view != mp_view) { @@ -300,13 +367,17 @@ CellTreeModel::configure (lay::LayoutView *view, int cv_index, unsigned int flag if (view != mp_view) { - mp_view->cell_visibility_changed_event.remove (this, &CellTreeModel::signal_data_changed); - mp_view->cellview_changed_event.remove (this, &CellTreeModel::signal_data_changed_with_int); + if (mp_view) { + mp_view->cell_visibility_changed_event.remove (this, &CellTreeModel::signal_data_changed); + mp_view->cellview_changed_event.remove (this, &CellTreeModel::signal_data_changed_with_int); + } mp_view = view; - mp_view->cell_visibility_changed_event.add (this, &CellTreeModel::signal_data_changed); - mp_view->cellview_changed_event.add (this, &CellTreeModel::signal_data_changed_with_int); + if (mp_view) { + mp_view->cell_visibility_changed_event.add (this, &CellTreeModel::signal_data_changed); + mp_view->cellview_changed_event.add (this, &CellTreeModel::signal_data_changed_with_int); + } } @@ -321,6 +392,7 @@ CellTreeModel::configure (lay::LayoutView *view, int cv_index, unsigned int flag m_pad = ((flags & NoPadding) == 0); mp_layout = layout; + mp_library = library; tl_assert (! mp_layout->under_construction () && ! (mp_layout->manager () && mp_layout->manager ()->transacting ())); build_top_level (); @@ -339,10 +411,10 @@ CellTreeModel::configure (lay::LayoutView *view, int cv_index, unsigned int flag for (QModelIndexList::const_iterator index = indexes.begin (); index != indexes.end (); ++index) { - std::vector path; + std::vector > path; CellTreeItem *item = (CellTreeItem *) index->internalPointer (); while (item) { - path.push_back (item->cell_index ()); + path.push_back (std::make_pair (item->is_pcell (), item->cell_or_pcell_index ())); item = item->parent (); } @@ -354,22 +426,22 @@ CellTreeModel::configure (lay::LayoutView *view, int cv_index, unsigned int flag // because we push_back'd on our way up: std::reverse (path.begin (), path.end ()); - for (std::vector::const_iterator ci = path.begin (); ci != path.end (); ++ci) { + for (std::vector >::const_iterator ci = path.begin (); ci != path.end (); ++ci) { CellTreeItem *new_parent = 0; - if (! layout->is_valid_cell_index (*ci)) { + if ((! ci->first && ! layout->is_valid_cell_index (ci->second)) || (ci->first && ! layout->pcell_declaration (ci->second))) { // can't translate this index } else if (parent == 0) { for (int i = 0; i < int (m_toplevel.size ()) && !new_parent; ++i) { - if (m_toplevel [i]->cell_index () == *ci) { + if (m_toplevel [i]->cell_or_pcell_index () == ci->second && m_toplevel [i]->is_pcell () == ci->first) { new_parent = m_toplevel [i]; row = i; } } } else { for (int i = 0; i < parent->children () && !new_parent; ++i) { - if (parent->child (i)->cell_index () == *ci) { + if (parent->child (i)->cell_or_pcell_index () == ci->second && parent->child (i)->is_pcell () == ci->first) { new_parent = parent->child (i); row = i; } @@ -406,7 +478,7 @@ void CellTreeModel::set_sorting (Sorting s) { if (s != m_sorting) { - configure (mp_view, m_cv_index, m_flags, mp_base, s); + do_configure (mp_layout, mp_library, mp_view, m_cv_index, m_flags, mp_base, s); } } @@ -435,7 +507,7 @@ CellTreeModel::build_top_level () if (mp_base) { m_toplevel.reserve (mp_base->child_cells ()); for (db::Cell::child_cell_iterator child = mp_base->begin_child_cells (); ! child.at_end (); ++child) { - CellTreeItem *item = new CellTreeItem (mp_layout, 0, false, *child, true, m_sorting); + CellTreeItem *item = new CellTreeItem (mp_layout, false, *child, true, m_sorting); m_toplevel.push_back (item); } } @@ -447,7 +519,7 @@ CellTreeModel::build_top_level () if (mp_base) { m_toplevel.reserve (mp_base->parent_cells ()); for (db::Cell::parent_cell_iterator parent = mp_base->begin_parent_cells (); parent != mp_base->end_parent_cells (); ++parent) { - CellTreeItem *item = new CellTreeItem (mp_layout, 0, false, *parent, true, m_sorting); + CellTreeItem *item = new CellTreeItem (mp_layout, false, *parent, true, m_sorting); m_toplevel.push_back (item); } } @@ -462,11 +534,11 @@ CellTreeModel::build_top_level () while (top != mp_layout->end_top_down ()) { if (m_flat) { - CellTreeItem *item = new CellTreeItem (mp_layout, 0, false, *top, true, m_sorting); + CellTreeItem *item = new CellTreeItem (mp_layout, false, *top, true, m_sorting); m_toplevel.push_back (item); } else if (mp_layout->cell (*top).is_top ()) { if ((m_flags & BasicCells) == 0 || ! mp_layout->cell (*top).is_proxy ()) { - CellTreeItem *item = new CellTreeItem (mp_layout, 0, false, *top, (m_flags & TopCells) != 0, m_sorting); + CellTreeItem *item = new CellTreeItem (mp_layout, false, *top, (m_flags & TopCells) != 0, m_sorting); m_toplevel.push_back (item); } } else { @@ -478,10 +550,30 @@ CellTreeModel::build_top_level () } if ((m_flags & BasicCells) != 0) { + for (db::Layout::pcell_iterator pc = mp_layout->begin_pcells (); pc != mp_layout->end_pcells (); ++pc) { - CellTreeItem *item = new CellTreeItem (mp_layout, 0, true, pc->second, true, m_sorting); + + CellTreeItem *item = new CellTreeItem (mp_layout, true, pc->second, true, m_sorting); m_toplevel.push_back (item); + + if ((m_flags & WithVariants) != 0) { + + const db::PCellHeader *pcell_header = mp_layout->pcell_header (pc->second); + for (db::PCellHeader::variant_iterator v = pcell_header->begin (); v != pcell_header->end (); ++v) { + if (mp_library && mp_library->is_retired (v->second->cell_index ())) { + // skip retired cells - this means we won't show variants which are just kept + // as shadow variants for the transactions. + } else { + item->add_child (new CellTreeItem (mp_layout, false, v->second->cell_index (), true, m_sorting)); + } + } + + item->finish_children (); + + } + } + } } @@ -510,19 +602,23 @@ CellTreeModel::mimeTypes () const QMimeData * CellTreeModel::mimeData(const QModelIndexList &indexes) const { - const db::Cell *c = 0; - for (QModelIndexList::const_iterator i = indexes.begin (); i != indexes.end () && !c; ++i) { + for (QModelIndexList::const_iterator i = indexes.begin (); i != indexes.end (); ++i) { + if (i->isValid()) { - c = cell (*i); + + if (is_pcell (*i)) { + lay::CellDragDropData data (mp_layout, mp_library, pcell_id (*i), true); + return data.to_mime_data (); + } else if (cell (*i)) { + lay::CellDragDropData data (mp_layout, mp_library, cell_index (*i), false); + return data.to_mime_data (); + } + } + } - if (c) { - lay::CellDragDropData data (mp_layout, c->cell_index ()); - return data.to_mime_data (); - } else { - return 0; - } + return 0; } int @@ -561,7 +657,7 @@ CellTreeModel::data (const QModelIndex &index, int role) const const lay::CellView::specific_cell_path_type &ctx_path = mp_view->cellview (m_cv_index).specific_path (); if (! path.empty ()) { - if (item->cell_index () == path.back ()) { + if (item->cell_or_pcell_index () == path.back ()) { if (m_flat) { f.setBold (true); } else { @@ -569,7 +665,7 @@ CellTreeModel::data (const QModelIndex &index, int role) const lay::CellView::unspecific_cell_path_type::const_iterator p = path.end (); while (it && p != path.begin ()) { --p; - if (it->cell_index () != *p) { + if (it->cell_or_pcell_index () != *p) { break; } it = it->parent (); @@ -578,7 +674,7 @@ CellTreeModel::data (const QModelIndex &index, int role) const f.setBold (true); } } - } else if (! ctx_path.empty () && item->cell_index () == ctx_path.back ().inst_ptr.cell_index ()) { + } else if (! ctx_path.empty () && item->cell_or_pcell_index () == ctx_path.back ().inst_ptr.cell_index ()) { if (m_flat) { f.setUnderline (true); } else { @@ -586,7 +682,7 @@ CellTreeModel::data (const QModelIndex &index, int role) const lay::CellView::specific_cell_path_type::const_iterator cp = ctx_path.end (); while (it && cp != ctx_path.begin ()) { --cp; - if (it->cell_index () != cp->inst_ptr.cell_index ()) { + if (it->cell_or_pcell_index () != cp->inst_ptr.cell_index ()) { break; } it = it->parent (); @@ -595,7 +691,7 @@ CellTreeModel::data (const QModelIndex &index, int role) const lay::CellView::unspecific_cell_path_type::const_iterator p = path.end (); while (it && p != path.begin ()) { --p; - if (it->cell_index () != *p) { + if (it->cell_or_pcell_index () != *p) { break; } it = it->parent (); @@ -608,7 +704,7 @@ CellTreeModel::data (const QModelIndex &index, int role) const } } - if (mp_view->is_cell_hidden (item->cell_index (), m_cv_index)) { + if (mp_view->is_cell_hidden (item->cell_or_pcell_index (), m_cv_index)) { f.setStrikeOut (true); } @@ -647,6 +743,17 @@ CellTreeModel::data (const QModelIndex &index, int role) const return QVariant (); #endif + } else if (role == Qt::DecorationRole && (m_flags & WithIcons) != 0) { + + // TODO: icons for normal cells too? + if (item->is_pcell ()) { + QIcon icon (":/setup.png"); + return QVariant (icon); + } else { + QIcon icon (":/instance.png"); + return QVariant (icon); + } + } else { return QVariant (); @@ -660,19 +767,6 @@ CellTreeModel::headerData (int /*section*/, Qt::Orientation /*orientation*/, int return QVariant (); } -bool searchItemPtr(CellTreeItem *parent, CellTreeItem *search) -{ - if (parent == search) { - return true; - } - for (int i = 0; i < parent->children(); ++i) { - if (searchItemPtr(parent->child(i), search)) { - return true; - } - } - return false; -} - int CellTreeModel::rowCount (const QModelIndex &parent) const { @@ -682,7 +776,7 @@ CellTreeModel::rowCount (const QModelIndex &parent) const CellTreeItem *item = (CellTreeItem *) parent.internalPointer (); if (! item) { return 0; - } else if (! mp_layout->is_valid_cell_index (item->cell_index ())) { + } else if (! item->is_valid ()) { // for safety we return 0 children for invalid cells return 0; } else { @@ -702,7 +796,7 @@ CellTreeModel::index (int row, int column, const QModelIndex &parent) const CellTreeItem *item = (CellTreeItem *) parent.internalPointer (); if (! item) { return QModelIndex (); - } else if (! mp_layout->is_valid_cell_index (item->cell_index ())) { + } else if (! item->is_valid ()) { // for safety we don't return valid child indexes for invalid cells return QModelIndex (); } else { @@ -784,7 +878,7 @@ CellTreeModel::pcell_id (const QModelIndex &index) const return 0; } else { CellTreeItem *item = (CellTreeItem *) index.internalPointer (); - return item->cell_index (); + return item->cell_or_pcell_index (); } } @@ -795,7 +889,7 @@ CellTreeModel::cell_index (const QModelIndex &index) const return 0; } else { CellTreeItem *item = (CellTreeItem *) index.internalPointer (); - return item->cell_index (); + return item->cell_or_pcell_index (); } } @@ -804,7 +898,7 @@ CellTreeModel::cell (const QModelIndex &index) const { if (index.isValid () && ! mp_layout->under_construction () && ! (mp_layout->manager () && mp_layout->manager ()->transacting ())) { CellTreeItem *item = (CellTreeItem *) index.internalPointer (); - return & mp_layout->cell (item->cell_index ()); + return & mp_layout->cell (item->cell_or_pcell_index ()); } else { return 0; } @@ -816,9 +910,9 @@ CellTreeModel::cell_name (const QModelIndex &index) const if (index.isValid () && ! mp_layout->under_construction () && ! (mp_layout->manager () && mp_layout->manager ()->transacting ())) { CellTreeItem *item = (CellTreeItem *) index.internalPointer (); if (item->is_pcell ()) { - return mp_layout->pcell_header (item->cell_index ())->get_name ().c_str (); + return mp_layout->pcell_header (item->cell_or_pcell_index ())->get_name ().c_str (); } else { - return mp_layout->cell_name (item->cell_index ()); + return mp_layout->cell_name (item->cell_or_pcell_index ()); } } else { return 0; diff --git a/src/laybasic/laybasic/layCellTreeModel.h b/src/laybasic/laybasic/layCellTreeModel.h index 721df481b..966693e43 100644 --- a/src/laybasic/laybasic/layCellTreeModel.h +++ b/src/laybasic/laybasic/layCellTreeModel.h @@ -37,6 +37,11 @@ namespace tl class GlobPattern; } +namespace db +{ + class Library; +} + namespace lay { @@ -56,18 +61,20 @@ class CellTreeModel { public: enum Flags { - Flat = 1, // flat list (rather than hierarchy) - Children = 2, // direct children of cell "base" - Parents = 4, // direct parents of cell "base" - TopCells = 8, // show top cells only - BasicCells = 16, // show basic cells (PCells included, no proxies) - NoPadding = 32 // enable padding of display string with a blank at the beginning and end + Flat = 1, // flat list (rather than hierarchy) + Children = 2, // direct children of cell "base" + Parents = 4, // direct parents of cell "base" + TopCells = 8, // show top cells only + BasicCells = 16, // show basic cells (PCells included, no proxies) + WithVariants = 32, // show PCell variants below PCells + WithIcons = 64, // show icons for the top level cell type + NoPadding = 128 // disable padding of display string with a blank at the beginning and end }; enum Sorting { - ByName, // sort by name - ByArea, // sort by cell area (small to large) - ByAreaReverse // sort by cell area (large to small) + ByName, // sort by name + ByArea, // sort by cell area (small to large) + ByAreaReverse // sort by cell area (large to small) }; /** @@ -87,6 +94,13 @@ public: */ CellTreeModel (QWidget *parent, db::Layout *layout, unsigned int flags = 0, const db::Cell *base = 0, Sorting sorting = ByName); + /** + * @brief Constructor + * + * This constructor does not take a view but rather a layout from a library. It does not display hidden status or similar. + */ + CellTreeModel (QWidget *parent, db::Library *library, unsigned int flags = 0, const db::Cell *base = 0, Sorting sorting = ByName); + /** * @brief Dtor */ @@ -104,10 +118,20 @@ public: virtual QMimeData *mimeData (const QModelIndexList &indexes) const; /** - * @brief Reconfigures the model + * @brief Reconfigures the model with a LayoutView */ void configure (lay::LayoutView *view, int cv_index, unsigned int flags = 0, const db::Cell *base = 0, Sorting sorting = ByName); + /** + * @brief Reconfigures the model with a pure Layout + */ + void configure (db::Layout *layout, unsigned int flags = 0, const db::Cell *base = 0, Sorting sorting = ByName); + + /** + * @brief Reconfigures the model with a pure Layout from a library + */ + void configure (db::Library *library, unsigned int flags = 0, const db::Cell *base = 0, Sorting sorting = ByName); + /** * @brief Gets the layout this model is connected to */ @@ -219,7 +243,8 @@ private: Sorting m_sorting; QWidget *mp_parent; lay::LayoutView *mp_view; - const db::Layout *mp_layout; + db::Layout *mp_layout; + db::Library *mp_library; int m_cv_index; const db::Cell *mp_base; std::vector m_toplevel; @@ -230,6 +255,7 @@ private: void build_top_level (); void clear_top_level (); void search_children (const tl::GlobPattern &pattern, CellTreeItem *item); + void do_configure (db::Layout *layout, db::Library *library, lay::LayoutView *view, int cv_index, unsigned int flags, const db::Cell *base, Sorting sorting); }; /** @@ -241,12 +267,12 @@ private: class CellTreeItem { public: - CellTreeItem (const db::Layout *layout, CellTreeItem *parent, bool is_pcell, size_t cell_or_pcell_index, bool flat, CellTreeModel::Sorting sorting); + CellTreeItem (const db::Layout *layout, bool is_pcell, size_t cell_or_pcell_index, bool flat, CellTreeModel::Sorting sorting); ~CellTreeItem (); int children () const; CellTreeItem *child (int index); - db::cell_index_type cell_index () const; + db::cell_index_type cell_or_pcell_index () const; CellTreeItem *parent () const; bool by_name_less_than (const CellTreeItem *b) const; bool by_area_less_than (const CellTreeItem *b) const; @@ -255,6 +281,9 @@ public: bool name_equals (const char *name) const; bool name_matches (const tl::GlobPattern &p) const; std::string display_text () const; + void add_child (CellTreeItem *item); + void finish_children (); + bool is_valid () const; bool is_pcell () const { diff --git a/src/laybasic/laybasic/layHierarchyControlPanel.cc b/src/laybasic/laybasic/layHierarchyControlPanel.cc index f8e391e7e..83a8a47ae 100644 --- a/src/laybasic/laybasic/layHierarchyControlPanel.cc +++ b/src/laybasic/laybasic/layHierarchyControlPanel.cc @@ -526,6 +526,10 @@ HierarchyControlPanel::search_prev () void HierarchyControlPanel::search_editing_finished () { + if (! mp_search_frame->isVisible ()) { + return; + } + for (std::vector ::const_iterator v = mp_cell_lists.begin (); v != mp_cell_lists.end (); ++v) { CellTreeModel *m = dynamic_cast ((*v)->model ()); if (m) { @@ -572,13 +576,13 @@ HierarchyControlPanel::path_from_index (const QModelIndex &index, int cv_index, // construct a path in the flat case lay::CellView cv (m_cellviews [cv_index]); - cv.set_cell (item->cell_index ()); + cv.set_cell (item->cell_or_pcell_index ()); path = cv.unspecific_path (); } else { while (item) { - path.push_back (item->cell_index ()); + path.push_back (item->cell_or_pcell_index ()); item = item->parent (); } @@ -630,10 +634,10 @@ HierarchyControlPanel::double_clicked (const QModelIndex &index) set_active_celltree_from_sender (); mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Show or hide cell"))); CellTreeItem *item = (CellTreeItem *) index.internalPointer (); - if (mp_view->is_cell_hidden (item->cell_index (), m_active_index)) { - mp_view->show_cell (item->cell_index (), m_active_index); + if (mp_view->is_cell_hidden (item->cell_or_pcell_index (), m_active_index)) { + mp_view->show_cell (item->cell_or_pcell_index (), m_active_index); } else { - mp_view->hide_cell (item->cell_index (), m_active_index); + mp_view->hide_cell (item->cell_or_pcell_index (), m_active_index); } mp_view->manager ()->commit (); } @@ -733,6 +737,8 @@ HierarchyControlPanel::selection_changed (int index) { if (index != m_active_index) { + search_editing_finished (); + m_active_index = index; bool split_mode = m_split_mode; @@ -744,6 +750,9 @@ HierarchyControlPanel::selection_changed (int index) int i = 0; for (std::vector ::const_iterator f = mp_cell_list_frames.begin (); f != mp_cell_list_frames.end (); ++f, ++i) { (*f)->setVisible (i == index || split_mode); + if (i == index) { + mp_cell_lists [i]->setFocus (); + } } i = 0; @@ -771,7 +780,7 @@ HierarchyControlPanel::index_from_path (const cell_path_type &path, int cv_index // TODO: linear search might not be effective enough .. for (int c = 0; c < model->toplevel_items (); ++c) { CellTreeItem *item = model->toplevel_item (c); - if (item->cell_index () == path.back ()) { + if (item->cell_or_pcell_index () == path.back ()) { return model->model_index (item); } } @@ -780,7 +789,7 @@ HierarchyControlPanel::index_from_path (const cell_path_type &path, int cv_index for (int c = 0; c < model->toplevel_items (); ++c) { CellTreeItem *item = model->toplevel_item (c); - if (item->cell_index () == path.front ()) { + if (item->cell_or_pcell_index () == path.front ()) { item = find_child_item (path.begin () + 1, path.end (), item); if (item) { return model->model_index (item); @@ -804,7 +813,7 @@ HierarchyControlPanel::find_child_item (cell_path_type::const_iterator start, ce for (int n = 0; n < p->children (); ++n) { CellTreeItem *item = p->child (n); - if (item && item->cell_index () == *start) { + if (item && item->cell_or_pcell_index () == *start) { return find_child_item (start + 1, end, item); } } diff --git a/src/laybasic/laybasic/layLayoutView.cc b/src/laybasic/laybasic/layLayoutView.cc index 54b498936..3930c678c 100644 --- a/src/laybasic/laybasic/layLayoutView.cc +++ b/src/laybasic/laybasic/layLayoutView.cc @@ -61,6 +61,7 @@ #include "layLayoutCanvas.h" #include "layLayerControlPanel.h" #include "layHierarchyControlPanel.h" +#include "layLibrariesView.h" #include "layBrowser.h" #include "layRedrawThread.h" #include "layRedrawThreadWorker.h" @@ -72,6 +73,7 @@ #include "dbRecursiveShapeIterator.h" #include "dbManager.h" #include "dbEdgeProcessor.h" +#include "dbLibrary.h" #include "rdb.h" #include "rdbMarkerBrowserDialog.h" #include "dbLayoutToNetlist.h" @@ -356,6 +358,8 @@ LayoutView::init (db::Manager *mgr, lay::PluginRoot *root, QWidget * /*parent*/) mp_control_frame = 0; mp_hierarchy_panel = 0; mp_hierarchy_frame = 0; + mp_libraries_view = 0; + mp_libraries_frame = 0; mp_min_hier_spbx = 0; mp_max_hier_spbx = 0; m_from_level = 0; @@ -445,6 +449,17 @@ LayoutView::init (db::Manager *mgr, lay::PluginRoot *root, QWidget * /*parent*/) vbl->setMargin (0); vbl->setSpacing (0); + mp_canvas = new lay::LayoutCanvas (this, this); + vbl->addWidget (mp_canvas); + connect (mp_canvas, SIGNAL (left_arrow_key_pressed ()), this, SLOT (pan_left ())); + connect (mp_canvas, SIGNAL (up_arrow_key_pressed ()), this, SLOT (pan_up ())); + connect (mp_canvas, SIGNAL (right_arrow_key_pressed ()), this, SLOT (pan_right ())); + connect (mp_canvas, SIGNAL (down_arrow_key_pressed ()), this, SLOT (pan_down ())); + connect (mp_canvas, SIGNAL (left_arrow_key_pressed_with_shift ()), this, SLOT (pan_left_fast ())); + connect (mp_canvas, SIGNAL (up_arrow_key_pressed_with_shift ()), this, SLOT (pan_up_fast ())); + connect (mp_canvas, SIGNAL (right_arrow_key_pressed_with_shift ()), this, SLOT (pan_right_fast ())); + connect (mp_canvas, SIGNAL (down_arrow_key_pressed_with_shift ()), this, SLOT (pan_down_fast ())); + if ((m_options & LV_NoHierarchyPanel) == 0 && (m_options & LV_Naked) == 0) { QFrame *hierarchy_frame = new QFrame (0); @@ -491,16 +506,21 @@ LayoutView::init (db::Manager *mgr, lay::PluginRoot *root, QWidget * /*parent*/) } - mp_canvas = new lay::LayoutCanvas (this, this); - vbl->addWidget (mp_canvas); - connect (mp_canvas, SIGNAL (left_arrow_key_pressed ()), this, SLOT (pan_left ())); - connect (mp_canvas, SIGNAL (up_arrow_key_pressed ()), this, SLOT (pan_up ())); - connect (mp_canvas, SIGNAL (right_arrow_key_pressed ()), this, SLOT (pan_right ())); - connect (mp_canvas, SIGNAL (down_arrow_key_pressed ()), this, SLOT (pan_down ())); - connect (mp_canvas, SIGNAL (left_arrow_key_pressed_with_shift ()), this, SLOT (pan_left_fast ())); - connect (mp_canvas, SIGNAL (up_arrow_key_pressed_with_shift ()), this, SLOT (pan_up_fast ())); - connect (mp_canvas, SIGNAL (right_arrow_key_pressed_with_shift ()), this, SLOT (pan_right_fast ())); - connect (mp_canvas, SIGNAL (down_arrow_key_pressed_with_shift ()), this, SLOT (pan_down_fast ())); + if ((m_options & LV_NoLibrariesView) == 0 && (m_options & LV_Naked) == 0) { + + QFrame *libraries_frame = new QFrame (0); + libraries_frame->setObjectName (QString::fromUtf8 ("libs_frame")); + mp_libraries_frame = libraries_frame; + QVBoxLayout *left_frame_ly = new QVBoxLayout (libraries_frame); + left_frame_ly->setMargin (0); + left_frame_ly->setSpacing (0); + + mp_libraries_view = new lay::LibrariesView (this, libraries_frame, "libs"); + left_frame_ly->addWidget (mp_libraries_view, 1 /*stretch*/); + + connect (mp_libraries_view, SIGNAL (active_library_changed (int)), this, SLOT (active_library_changed (int))); + + } // occupy services and editables: // these services get deleted by the canvas destructor automatically: @@ -628,6 +648,12 @@ LayoutView::~LayoutView () } mp_hierarchy_frame = 0; mp_hierarchy_panel = 0; + + if (mp_libraries_frame) { + delete mp_libraries_frame; + } + mp_libraries_frame = 0; + mp_libraries_view = 0; } void LayoutView::hideEvent (QHideEvent *) @@ -790,6 +816,7 @@ LayoutView::init_menu (lay::AbstractMenu &menu) { lay::LayerControlPanel::init_menu (menu); lay::HierarchyControlPanel::init_menu (menu); + lay::LibrariesView::init_menu (menu); } void @@ -951,6 +978,22 @@ LayoutView::configure (const std::string &name, const std::string &value) } return true; + } else if (name == cfg_split_lib_views) { + + bool f; + tl::from_string (value, f); + if (mp_libraries_view) { + mp_libraries_view->set_split_mode (f); + } + return true; + + } else if (name == cfg_current_lib_view) { + + if (mp_libraries_view) { + mp_libraries_view->select_active_lib_by_name (value); + } + return true; + } else if (name == cfg_cell_list_sorting) { if (mp_hierarchy_panel) { @@ -4472,6 +4515,11 @@ LayoutView::background_color (QColor c) mp_hierarchy_panel->set_text_color (contrast); } + if (mp_libraries_view) { + mp_libraries_view->set_background_color (c); + mp_libraries_view->set_text_color (contrast); + } + if (mp_selection_service) { mp_selection_service->set_colors (c, contrast); } @@ -4550,6 +4598,19 @@ LayoutView::active_cellview_changed (int index) } } +void +LayoutView::active_library_changed (int /*index*/) +{ + std::string lib_name; + if (mp_libraries_view->active_lib ()) { + lib_name = mp_libraries_view->active_lib ()->get_name (); + } + + // commit the new active library to the other views and persist this state + // TODO: could be passed through the LibraryController (like through some LibraryController::active_library) + plugin_root ()->config_set (cfg_current_lib_view, lib_name); +} + void LayoutView::cellview_changed (unsigned int index) { @@ -7243,7 +7304,7 @@ LayoutView::sizeHint () const { if ((m_options & LV_Naked) != 0) { return QSize (200, 200); - } else if ((m_options & LV_NoLayers) != 0 || (m_options & LV_NoHierarchyPanel) != 0) { + } else if ((m_options & LV_NoLayers) != 0 || (m_options & LV_NoHierarchyPanel) != 0 || (m_options & LV_NoLibrariesView) != 0) { return QSize (400, 200); } else { return QSize (600, 200); diff --git a/src/laybasic/laybasic/layLayoutView.h b/src/laybasic/laybasic/layLayoutView.h index 5b871e7a4..cf5cbc8b9 100644 --- a/src/laybasic/laybasic/layLayoutView.h +++ b/src/laybasic/laybasic/layLayoutView.h @@ -70,6 +70,7 @@ namespace lay { class AbstractMenu; class LayerControlPanel; class HierarchyControlPanel; +class LibrariesView; class MouseTracker; class ZoomService; class SelectionService; @@ -168,13 +169,14 @@ public: LV_Normal = 0, LV_NoLayers = 1, LV_NoHierarchyPanel = 2, - LV_Naked = 4, - LV_NoZoom = 8, - LV_NoGrid = 16, - LV_NoMove = 32, - LV_NoTracker = 64, - LV_NoSelection = 128, - LV_NoPlugins = 256, + LV_NoLibrariesView = 4, + LV_Naked = 8, + LV_NoZoom = 16, + LV_NoGrid = 32, + LV_NoMove = 64, + LV_NoTracker = 128, + LV_NoSelection = 256, + LV_NoPlugins = 512, LV_NoServices = LV_NoMove + LV_NoTracker + LV_NoSelection + LV_NoPlugins }; @@ -221,7 +223,7 @@ public: bool has_selection (); /** - * @brief Get the container with the layer control panel + * @brief Gets the container with the layer control panel */ QWidget *layer_control_frame () { @@ -229,7 +231,7 @@ public: } /** - * @brief Get the container with the hierarchy control panel + * @brief Gets the container with the hierarchy control panel */ QWidget *hierarchy_control_frame () { @@ -237,7 +239,15 @@ public: } /** - * @brief Paste from clipboard + * @brief Gets the container with the libraries view + */ + QWidget *libraries_frame () + { + return mp_libraries_frame; + } + + /** + * @brief Pastes from clipboard * * This reimplementation of the lay::Editables interface additionally * looks for paste receivers in the tree views for example. @@ -245,7 +255,7 @@ public: void paste (); /** - * @brief Copy to clipboard + * @brief Copies to clipboard * * This reimplementation of the lay::Editables interface additionally * looks for copy providers in the tree views for example. @@ -253,7 +263,7 @@ public: void copy (); /** - * @brief Cut to clipboard + * @brief Cuts to clipboard * * This reimplementation of the lay::Editables interface additionally * looks for cut & copy providers in the tree views for example. @@ -261,7 +271,7 @@ public: void cut (); /** - * @brief Get the explicit title string of the view + * @brief Gets the explicit title string of the view * * This is the one explicitly set, not the one displayed. The displayed text is composed of internal information * if no title string is set. @@ -272,17 +282,17 @@ public: } /** - * @brief Get the window title of the view + * @brief Gets the window title of the view */ std::string title () const; /** - * @brief Set the window title to an explicit string + * @brief Sets the window title to an explicit string */ void set_title (const std::string &t); /** - * @brief Reset the explicit title and enable the automatic naming + * @brief Resets the explicit title and enable the automatic naming */ void reset_title (); @@ -2627,6 +2637,7 @@ public slots: private slots: void active_cellview_changed (int index); + void active_library_changed (int index); void goto_bookmark (); signals: @@ -2695,7 +2706,8 @@ private: QFrame *mp_left_frame; lay::LayerControlPanel *mp_control_panel; lay::HierarchyControlPanel *mp_hierarchy_panel; - QWidget *mp_control_frame, *mp_hierarchy_frame; + lay::LibrariesView *mp_libraries_view; + QWidget *mp_control_frame, *mp_hierarchy_frame, *mp_libraries_frame; QSpinBox *mp_min_hier_spbx; QSpinBox *mp_max_hier_spbx; std::list m_cellviews; diff --git a/src/laybasic/laybasic/layLayoutViewConfigPages.cc b/src/laybasic/laybasic/layLayoutViewConfigPages.cc index 2fcb6ebf6..3c0ca93b4 100644 --- a/src/laybasic/laybasic/layLayoutViewConfigPages.cc +++ b/src/laybasic/laybasic/layLayoutViewConfigPages.cc @@ -1486,6 +1486,8 @@ public: options.push_back (std::pair (cfg_flat_cell_list, "false")); options.push_back (std::pair (cfg_split_cell_list, "false")); options.push_back (std::pair (cfg_cell_list_sorting, "by-name")); + options.push_back (std::pair (cfg_split_lib_views, "false")); + options.push_back (std::pair (cfg_current_lib_view, "")); options.push_back (std::pair (cfg_hide_empty_layers, "false")); options.push_back (std::pair (cfg_min_inst_label_size, "16")); options.push_back (std::pair (cfg_cell_box_text_font, "0")); diff --git a/src/laybasic/laybasic/layLibrariesView.cc b/src/laybasic/laybasic/layLibrariesView.cc new file mode 100644 index 000000000..d0bc69894 --- /dev/null +++ b/src/laybasic/laybasic/layLibrariesView.cc @@ -0,0 +1,820 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dbClipboard.h" +#include "dbClipboardData.h" +#include "dbLibraryManager.h" +#include "dbLibrary.h" +#include "layLibrariesView.h" +#include "layCellTreeModel.h" +#include "layLayoutView.h" +#include "layAbstractMenu.h" +#include "layAbstractMenuProvider.h" +#include "layDialogs.h" +#include "tlExceptions.h" +#include "laybasicConfig.h" +#include "tlInternational.h" +#include "tlString.h" +#include "gtf.h" + +namespace lay +{ + +// -------------------------------------------------------------------- +// A helper class the identifies clipboard data + +class CellClipboardData + : public db::ClipboardData +{ +public: + CellClipboardData () { } +}; + +// -------------------------------------------------------------------- +// LibraryTreeWidget implementation + +LibraryTreeWidget::LibraryTreeWidget (QWidget *parent, const char *name, QWidget *key_event_receiver) + : QTreeView (parent), mp_key_event_receiver (key_event_receiver) +{ + // Allow dragging from here to + setDragDropMode (QAbstractItemView::DragOnly); + + setObjectName (QString::fromUtf8 (name)); +} + + +bool +LibraryTreeWidget::event (QEvent *event) +{ +#if 0 + // Handling this event makes the widget receive all keystrokes. + // Without this code, shortcuts override the search function. + if (event->type () == QEvent::ShortcutOverride) { + QKeyEvent *ke = static_cast (event); + QString t = ke->text (); + if (!t.isEmpty () && t[0].isPrint ()) { + ke->accept (); + } + } +#endif + return QTreeView::event (event); +} + +bool +LibraryTreeWidget::focusNextPrevChild (bool /*next*/) +{ + return false; +} + +void +LibraryTreeWidget::keyPressEvent (QKeyEvent *event) +{ + QString t = event->text (); + if (! t.isEmpty () && t[0].isPrint ()) { + // "/" is a search initiator + if (t == QString::fromUtf8 ("/")) { + t.clear (); + } + emit search_triggered (t); + } else if (mp_key_event_receiver) { + // send other key events to the alternative receiver - this way we can make the + // view receive arrow keys for panning. + QCoreApplication::sendEvent (mp_key_event_receiver, event); + } else { + return QTreeView::keyPressEvent (event); + } +} + +void +LibraryTreeWidget::startDrag (Qt::DropActions supportedActions) +{ + QModelIndex index = selectionModel ()->currentIndex (); + if (index.isValid ()) { + + QModelIndexList indexes; + indexes << index; + QMimeData *data = model ()->mimeData (indexes); + if (!data) { + return; + } + + QDrag *drag = new QDrag (this); + drag->setMimeData(data); + QPixmap px (1, 1); + px.fill (QColor (0, 0, 0)); + px.createMaskFromColor (QColor (0, 0, 0), Qt::MaskOutColor); + drag->setPixmap (px); + + Qt::DropAction defaultDropAction = Qt::IgnoreAction; + if (supportedActions & Qt::CopyAction) { + defaultDropAction = Qt::CopyAction; + } + + drag->exec(supportedActions, defaultDropAction); + + } +} + +void +LibraryTreeWidget::mouseDoubleClickEvent (QMouseEvent *event) +{ + QModelIndex index (indexAt (event->pos ())); + if (index.isValid ()) { + emit cell_double_clicked (index); + } +} + +void +LibraryTreeWidget::mousePressEvent (QMouseEvent *event) +{ + if (event->button () == Qt::MidButton) { + // eat this event. + } else { + QModelIndex index (indexAt (event->pos ())); + if (index.isValid ()) { + emit cell_clicked (index); + } + QTreeView::mousePressEvent (event); + } +} + +void +LibraryTreeWidget::mouseReleaseEvent (QMouseEvent *event) +{ + if (event->button () == Qt::MidButton) { + QModelIndex index (indexAt (event->pos ())); + if (index.isValid ()) { + emit cell_middle_clicked (index); + } + } else { + QTreeView::mouseReleaseEvent (event); + } +} + + +// -------------------------------------------------------------------- +// LibrariesView implementation + +const int max_cellviews_in_split_mode = 5; + +void +LibrariesView::init_menu (lay::AbstractMenu &menu) +{ + MenuLayoutEntry context_menu [] = { +#if 0 + // doesn't make sense for many libs + MenuLayoutEntry ("split_mode", tl::to_string (QObject::tr ("Split Mode")), std::make_pair (cfg_split_lib_views, "?")), +#endif + MenuLayoutEntry::last () + }; + + MenuLayoutEntry main_menu [] = { + MenuLayoutEntry ("@lib_context_menu", "", context_menu), + MenuLayoutEntry::last () + }; + + menu.init (main_menu); +} + +LibrariesView::LibrariesView (lay::LayoutView *view, QWidget *parent, const char *name) + : QFrame (parent), + m_enable_cb (true), + mp_view (view), + m_split_mode (false), + m_do_update_content_dm (this, &LibrariesView::do_update_content), + m_do_full_update_content_dm (this, &LibrariesView::do_full_update_content) +{ + setObjectName (QString::fromUtf8 (name)); + + QVBoxLayout *ly = new QVBoxLayout (this); + ly->setSpacing (0); + ly->setContentsMargins (0, 0, 0, 0); + + mp_selector = new QComboBox (this); + mp_selector->setObjectName (QString::fromUtf8 ("cellview_selection")); + ly->addWidget (mp_selector); + + mp_search_frame = new QFrame (this); + ly->addWidget (mp_search_frame); + mp_search_frame->hide (); + mp_search_frame->setAutoFillBackground (true); + mp_search_frame->setObjectName (QString::fromUtf8 ("panel")); + mp_search_frame->setFrameStyle (QFrame::Panel | QFrame::Raised); + mp_search_frame->setLineWidth (1); + mp_search_frame->setBackgroundRole (QPalette::Highlight); + + QHBoxLayout *sf_ly = new QHBoxLayout (mp_search_frame); + sf_ly->setMargin (0); + sf_ly->setContentsMargins (0, 0, 0, 0); + sf_ly->setSpacing (0); + + mp_search_close_cb = new QCheckBox (mp_search_frame); + sf_ly->addWidget (mp_search_close_cb); + + mp_search_close_cb->setFocusPolicy (Qt::NoFocus); + mp_search_close_cb->setBackgroundRole (QPalette::Highlight); + mp_search_close_cb->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Preferred)); + QPalette pl (mp_search_close_cb->palette ()); + pl.setColor (QPalette::Foreground, pl.color (QPalette::Active, QPalette::HighlightedText)); + mp_search_close_cb->setPalette (pl); + mp_search_close_cb->setMaximumSize (QSize (mp_search_close_cb->maximumSize ().width (), mp_search_close_cb->sizeHint ().height () - 4)); + connect (mp_search_close_cb, SIGNAL (clicked ()), this, SLOT (search_editing_finished ())); + + mp_search_model = 0; + mp_search_edit_box = new lay::DecoratedLineEdit (mp_search_frame); + mp_search_edit_box->setObjectName (QString::fromUtf8 ("cellview_search_edit_box")); + mp_search_edit_box->set_escape_signal_enabled (true); + mp_search_edit_box->set_tab_signal_enabled (true); + connect (mp_search_edit_box, SIGNAL (returnPressed ()), this, SLOT (search_editing_finished ())); + connect (mp_search_edit_box, SIGNAL (textEdited (const QString &)), this, SLOT (search_edited ())); + connect (mp_search_edit_box, SIGNAL (esc_pressed ()), this, SLOT (search_editing_finished ())); + connect (mp_search_edit_box, SIGNAL (tab_pressed ()), this, SLOT (search_next ())); + connect (mp_search_edit_box, SIGNAL (backtab_pressed ()), this, SLOT (search_prev ())); + sf_ly->addWidget (mp_search_edit_box); + + mp_use_regular_expressions = new QAction (this); + mp_use_regular_expressions->setCheckable (true); + mp_use_regular_expressions->setChecked (true); + mp_use_regular_expressions->setText (tr ("Use expressions (use * and ? for any character)")); + + mp_case_sensitive = new QAction (this); + mp_case_sensitive->setCheckable (true); + mp_case_sensitive->setChecked (true); + mp_case_sensitive->setText (tr ("Case sensitive search")); + + QMenu *m = new QMenu (mp_search_edit_box); + m->addAction (mp_use_regular_expressions); + m->addAction (mp_case_sensitive); + connect (mp_use_regular_expressions, SIGNAL (triggered ()), this, SLOT (search_edited ())); + connect (mp_case_sensitive, SIGNAL (triggered ()), this, SLOT (search_edited ())); + + mp_search_edit_box->set_clear_button_enabled (true); + mp_search_edit_box->set_options_button_enabled (true); + mp_search_edit_box->set_options_menu (m); + + QToolButton *sf_next = new QToolButton (mp_search_frame); + sf_next->setAutoRaise (true); + sf_next->setToolTip (tr ("Find next")); + sf_next->setIcon (QIcon (QString::fromUtf8 (":/find.png"))); + connect (sf_next, SIGNAL (clicked ()), this, SLOT (search_next ())); + sf_ly->addWidget (sf_next); + + mp_splitter = new QSplitter (Qt::Vertical, this); + ly->addWidget (mp_splitter); + + connect (mp_selector, SIGNAL (activated (int)), this, SLOT (selection_changed (int))); + + QSizePolicy sp (QSizePolicy::Minimum, QSizePolicy::Preferred); + sp.setHorizontalStretch (0); + sp.setVerticalStretch (0); + setSizePolicy (sp); + + do_update_content (); +} + +LibrariesView::~LibrariesView () +{ + // .. nothing yet .. +} + +QSize +LibrariesView::sizeHint () const +{ + int w = 120; // TODO: better(?): mp_cell_list->sizeHint ().width (); + return QSize (w, 0); +} + +bool +LibrariesView::event (QEvent *e) +{ + if (e->type () == QEvent::MaxUser) { + // GTF probe event + e->accept (); + return true; + } else { + return QFrame::event (e); + } +} + +void +LibrariesView::context_menu (const QPoint &p) +{ + tl_assert (lay::AbstractMenuProvider::instance () != 0); + + QTreeView *cell_list = dynamic_cast (sender ()); + if (cell_list) { + QMenu *ctx_menu = lay::AbstractMenuProvider::instance ()->menu ()->detached_menu ("lib_context_menu"); + ctx_menu->exec (cell_list->mapToGlobal (p)); + } +} + +void +LibrariesView::set_split_mode (bool f) +{ + if (f != m_split_mode) { + m_split_mode = f; + m_do_update_content_dm (); + } +} + +void +LibrariesView::clear_all () +{ + m_libraries.clear (); + m_needs_update.clear (); + m_force_close.clear (); + + for (size_t i = 0; i < mp_cell_list_frames.size (); ++i) { + delete mp_cell_list_frames [i]; + } + mp_cell_list_frames.clear (); + mp_cell_list_headers.clear (); + mp_cell_lists.clear (); +} + +void +LibrariesView::search_triggered (const QString &t) +{ + mp_search_model = 0; + lay::LibraryTreeWidget *w = dynamic_cast (sender ()); + if (w) { + for (size_t i = 0; i < mp_cell_lists.size (); ++i) { + if (mp_cell_lists [i] == w) { + // Switch the active list for split mode -> CAUTION: this may trigger a search_editing_finished call + select_active (int (i)); + mp_search_model = dynamic_cast (w->model ()); + break; + } + } + } + + if (mp_search_model) { + mp_search_close_cb->setChecked (true); + mp_search_frame->show (); + mp_search_edit_box->setText (t); + mp_search_edit_box->setFocus (); + search_edited (); + } +} + +void +LibrariesView::search_edited () +{ + QString t = mp_search_edit_box->text (); + + for (std::vector ::const_iterator v = mp_cell_lists.begin (); v != mp_cell_lists.end (); ++v) { + if ((*v)->model () == mp_search_model) { + if (t.isEmpty ()) { + mp_search_model->clear_locate (); + (*v)->setCurrentIndex (QModelIndex ()); + } else { + QModelIndex found = mp_search_model->locate (t.toUtf8 ().constData (), mp_use_regular_expressions->isChecked (), mp_case_sensitive->isChecked (), false); + (*v)->setCurrentIndex (found); + if (found.isValid ()) { + (*v)->scrollTo (found); + } + } + break; + } + } +} + +void +LibrariesView::search_next () +{ + for (std::vector ::const_iterator v = mp_cell_lists.begin (); v != mp_cell_lists.end (); ++v) { + if ((*v)->model () == mp_search_model) { + QModelIndex found = mp_search_model->locate_next (); + if (found.isValid ()) { + (*v)->setCurrentIndex (found); + (*v)->scrollTo (found); + } + break; + } + } +} + +void +LibrariesView::search_prev () +{ + for (std::vector ::const_iterator v = mp_cell_lists.begin (); v != mp_cell_lists.end (); ++v) { + if ((*v)->model () == mp_search_model) { + QModelIndex found = mp_search_model->locate_prev (); + if (found.isValid ()) { + (*v)->setCurrentIndex (found); + (*v)->scrollTo (found); + } + break; + } + } +} + +void +LibrariesView::search_editing_finished () +{ + if (! mp_search_frame->isVisible ()) { + return; + } + + for (std::vector ::const_iterator v = mp_cell_lists.begin (); v != mp_cell_lists.end (); ++v) { + CellTreeModel *m = dynamic_cast ((*v)->model ()); + if (m) { + m->clear_locate (); + } + } + + // give back the focus to the cell list + for (size_t i = 0; i < mp_cell_lists.size (); ++i) { + if (mp_cell_lists [i]->model () == mp_search_model) { + mp_cell_lists [i]->setFocus (); + break; + } + } + + mp_search_frame->hide (); + mp_search_model = 0; +} + +void +LibrariesView::middle_clicked (const QModelIndex &index) +{ + // ... nothing yet .. +} + +void +LibrariesView::header_clicked () +{ + // ... nothing yet .. +} + +void +LibrariesView::clicked (const QModelIndex & /*index*/) +{ + // ... nothing yet .. +} + +void +LibrariesView::double_clicked (const QModelIndex &index) +{ + // ... nothing yet .. +} + +void +LibrariesView::set_background_color (QColor c) +{ + m_background_color = c; + for (std::vector ::const_iterator f = mp_cell_lists.begin (); f != mp_cell_lists.end (); ++f) { + QPalette pl ((*f)->palette ()); + pl.setColor (QPalette::Base, c); + (*f)->setPalette (pl); + } +} + +void +LibrariesView::set_text_color (QColor c) +{ + m_text_color = c; + for (std::vector ::const_iterator f = mp_cell_lists.begin (); f != mp_cell_lists.end (); ++f) { + QPalette pl ((*f)->palette ()); + pl.setColor (QPalette::Text, c); + (*f)->setPalette (pl); + } +} + +void +LibrariesView::update_required () +{ + m_do_full_update_content_dm (); +} + +void +LibrariesView::do_full_update_content () +{ + size_t i = 0; + for (db::LibraryManager::iterator lib = db::LibraryManager::instance ().begin (); lib != db::LibraryManager::instance ().end (); ++lib, ++i) { + if (m_needs_update.size () > i) { + m_needs_update [i] = true; + } + if (m_force_close.size () > i) { + m_force_close [i] = true; + } + } + + do_update_content (); +} + +void +LibrariesView::do_update_content (int lib_index) +{ + // close the search box since we will modify the model + mp_search_frame->hide (); + mp_search_model = 0; + + size_t imin = (lib_index < 0 ? 0 : (size_t) lib_index); + size_t imax = (lib_index < 0 ? std::numeric_limits ::max () : (size_t) lib_index); + + // rebuild all events + detach_from_all_events (); + db::LibraryManager::instance ().changed_event.add (this, &LibrariesView::update_required); + + std::vector libraries; + for (db::LibraryManager::iterator lib = db::LibraryManager::instance ().begin (); lib != db::LibraryManager::instance ().end (); ++lib) { + libraries.push_back (db::LibraryManager::instance ().lib (lib->second)); + libraries.back ()->layout ().hier_changed_event.add (this, &LibrariesView::update_required); + libraries.back ()->retired_state_changed_event.add (this, &LibrariesView::update_required); + } + + for (size_t i = imin; i < libraries.size () && i <= imax; ++i) { + if (i < m_libraries.size () && ! m_libraries[i].get ()) { + tl_assert (i < m_force_close.size ()); + m_force_close [i] = true; + } + if (i >= m_force_close.size ()) { + m_force_close.push_back (true); + } + if (i >= m_needs_update.size ()) { + m_needs_update.push_back (true); + } + if (i >= libraries.size ()) { + m_force_close [i] = true; + m_needs_update [i] = true; + } + } + + size_t n = std::min (m_libraries.size (), libraries.size ()); + for (size_t i = imin; i < n && i <= imax; ++i) { + + if (m_libraries [i].get () != libraries [i]) { + m_needs_update [i] = true; + m_force_close [i] = true; + } + + if (m_needs_update [i]) { + mp_cell_lists [i]->doItemsLayout (); // triggers a redraw + } + + m_libraries [i].reset (libraries [i]); + + } + + if (m_libraries.size () < libraries.size ()) { + for (size_t i = n; i < libraries.size (); ++i) { + m_libraries.push_back (tl::weak_ptr (libraries [i])); + } + } else if (m_libraries.size () > libraries.size ()) { + m_libraries.erase (m_libraries.begin () + libraries.size (), m_libraries.end ()); + } + + bool split_mode = m_split_mode; + // for more than max_cellviews_in_split_mode cellviews, switch to overlay mode + if (int (m_libraries.size ()) > max_cellviews_in_split_mode) { + split_mode = false; + } + + while (mp_cell_lists.size () < m_libraries.size ()) { + + size_t i = mp_cell_lists.size (); + + QPalette pl; + + QFrame *cl_frame = new QFrame (this); + cl_frame->setFrameShape (QFrame::NoFrame); + QVBoxLayout *cl_ly = new QVBoxLayout (cl_frame); + cl_ly->setSpacing (0); + cl_ly->setContentsMargins (0, 0, 0, 0); + + QToolButton *header = new QToolButton (cl_frame); + connect (header, SIGNAL (clicked ()), this, SLOT (header_clicked ())); + header->setText (tl::to_qstring (display_string (int (i)))); + header->setFocusPolicy (Qt::NoFocus); + header->setSizePolicy (QSizePolicy::Preferred, QSizePolicy::Preferred); + header->setCheckable (true); + header->setAutoRaise (true); + header->setAutoFillBackground (true); + header->setVisible (split_mode); + cl_ly->addWidget (header); + + LibraryTreeWidget *cell_list = new LibraryTreeWidget (cl_frame, "tree", mp_view->view_object_widget ()); + cl_ly->addWidget (cell_list); + cell_list->setModel (new CellTreeModel (cell_list, m_libraries [i].get (), CellTreeModel::Flat | CellTreeModel::TopCells | CellTreeModel::BasicCells | CellTreeModel::WithVariants | CellTreeModel::WithIcons, 0)); + cell_list->setUniformRowHeights (true); + + pl = cell_list->palette (); + if (m_text_color.isValid ()) { + pl.setColor (QPalette::Text, m_text_color); + } + if (m_background_color.isValid ()) { + pl.setColor (QPalette::Base, m_background_color); + } + cell_list->setPalette (pl); + + cell_list->header ()->hide (); + cell_list->setSelectionMode (QTreeView::ExtendedSelection); + cell_list->setRootIsDecorated (true); + cell_list->setContextMenuPolicy (Qt::CustomContextMenu); + + connect (cell_list, SIGNAL (customContextMenuRequested (const QPoint &)), this, SLOT (context_menu (const QPoint &))); + connect (cell_list, SIGNAL (cell_clicked (const QModelIndex &)), this, SLOT (clicked (const QModelIndex &))); + connect (cell_list, SIGNAL (cell_double_clicked (const QModelIndex &)), this, SLOT (double_clicked (const QModelIndex &))); + connect (cell_list, SIGNAL (cell_middle_clicked (const QModelIndex &)), this, SLOT (middle_clicked (const QModelIndex &))); + connect (cell_list, SIGNAL (search_triggered (const QString &)), this, SLOT (search_triggered (const QString &))); + + mp_cell_lists.push_back (cell_list); + mp_cell_list_frames.push_back (cl_frame); + mp_cell_list_headers.push_back (header); + + mp_splitter->addWidget (cl_frame); + + } + + while (mp_cell_lists.size () > m_libraries.size ()) { + delete mp_cell_list_frames.back (); + mp_cell_list_frames.pop_back (); + mp_cell_list_headers.pop_back (); + mp_cell_lists.pop_back (); + } + + for (unsigned int i = imin; i < m_libraries.size () && i < (unsigned int) mp_selector->count () && i <= imax; ++i) { + mp_selector->setItemText (i, tl::to_qstring (display_string (i))); + } + while (mp_selector->count () < int (m_libraries.size ())) { + mp_selector->addItem (tl::to_qstring (display_string (mp_selector->count ()))); + } + while (mp_selector->count () > int (m_libraries.size ())) { + mp_selector->removeItem (mp_selector->count () - 1); + } + + if (m_active_index >= int (m_libraries.size ())) { + m_active_index = int (m_libraries.size ()) - 1; + } else if (m_active_index < 0 && ! m_libraries.empty ()) { + m_active_index = 0; + } + mp_selector->setCurrentIndex (m_active_index); + mp_selector->setVisible (mp_cell_lists.size () > 1 && ! split_mode); + + for (unsigned int i = imin; i < m_libraries.size () && i <= imax; ++i) { + + if (m_needs_update [i]) { + + mp_cell_list_headers [i]->setText (tl::to_qstring (display_string (i))); + + // draw the cells in the level of the current cell, + // add an "above" entry if there is a level above. + // highlight the current entry. If the index is + // invalid, just clear the list. + + if (m_force_close [i]) { + + m_force_close [i] = false; + + CellTreeModel *model = dynamic_cast (mp_cell_lists [i]->model ()); + if (model) { + model->configure (m_libraries [i].get (), CellTreeModel::Flat | CellTreeModel::TopCells | CellTreeModel::BasicCells | CellTreeModel::WithVariants | CellTreeModel::WithIcons, 0); + } + + } + + m_needs_update [i] = false; + + } + + mp_cell_list_headers [i]->setVisible (split_mode && m_libraries.size () > 1); + mp_cell_list_headers [i]->setChecked (int (i) == m_active_index); + + mp_cell_list_frames [i]->setVisible (int (i) == m_active_index || split_mode); + + } +} + +void +LibrariesView::select_active_lib_by_name (const std::string &name) +{ + for (std::vector >::const_iterator i = m_libraries.begin (); i != m_libraries.end (); ++i) { + if (i->get () && (*i)->get_name () == name) { + select_active (int (i - m_libraries.begin ())); + break; + } + } +} + +void +LibrariesView::select_active (int lib_index) +{ + if (lib_index != m_active_index) { + mp_selector->setCurrentIndex (lib_index); + selection_changed (lib_index); + } +} + +db::Library * +LibrariesView::active_lib () +{ + if (m_active_index >= 0 && m_active_index < int (m_libraries.size ())) { + return m_libraries [m_active_index].get (); + } + return 0; +} + +void +LibrariesView::selection_changed (int index) +{ + if (index != m_active_index) { + + search_editing_finished (); + + m_active_index = index; + + bool split_mode = m_split_mode; + // for more than max_cellviews_in_split_mode cellviews, switch to overlay mode + if (int (m_libraries.size ()) > max_cellviews_in_split_mode) { + split_mode = false; + } + + int i = 0; + for (std::vector ::const_iterator f = mp_cell_list_frames.begin (); f != mp_cell_list_frames.end (); ++f, ++i) { + (*f)->setVisible (i == index || split_mode); + if (i == index) { + mp_cell_lists [i]->setFocus (); + } + } + + i = 0; + for (std::vector ::const_iterator f = mp_cell_list_headers.begin (); f != mp_cell_list_headers.end (); ++f, ++i) { + (*f)->setChecked (i == index); + } + + emit active_library_changed (index); + + } +} + +std::string +LibrariesView::display_string (int n) const +{ + const db::Library *lib = m_libraries [n].get (); + std::string text = lib->get_name (); + if (! lib->get_description ().empty ()) { + text += " - " + lib->get_description (); + } + return text; +} + +CellTreeItem * +LibrariesView::current_item () const +{ + if (m_active_index < 0 || m_active_index >= int (mp_cell_lists.size ())) { + return 0; + } + if (mp_cell_lists [m_active_index]->currentIndex ().isValid ()) { + return (CellTreeItem *) mp_cell_lists [m_active_index]->currentIndex ().internalPointer (); + } else { + return 0; + } +} + +bool +LibrariesView::has_focus () const +{ + return m_active_index >= 0 && m_active_index < int (mp_cell_lists.size ()) && mp_cell_lists [m_active_index]->hasFocus (); +} + +} // namespace lay diff --git a/src/laybasic/laybasic/layLibrariesView.h b/src/laybasic/laybasic/layLibrariesView.h new file mode 100644 index 000000000..7960d7985 --- /dev/null +++ b/src/laybasic/laybasic/layLibrariesView.h @@ -0,0 +1,280 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 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_layLibrariesView +#define HDR_layLibrariesView + +#include +#include +#include + +#include +#include + +#include "dbLayout.h" +#include "layCanvasPlane.h" +#include "layViewOp.h" +#include "layLayoutView.h" +#include "layCellTreeModel.h" +#include "layWidgets.h" +#include "tlDeferredExecution.h" + +class QModelIndex; +class QComboBox; +class QMenu; +class QSplitter; +class QFrame; +class QToolButton; +class QLineEdit; +class QAction; +class QCheckBox; + +namespace lay +{ + +/** + * @brief A special QTreeView customization + * + * A customized QTreeView that is used to receive middle-mouse-button + * events and processes double clicks by bypassing the standard implementation + * that closes and opens branches. + */ +class LibraryTreeWidget + : public QTreeView +{ +Q_OBJECT + +public: + LibraryTreeWidget (QWidget *parent, const char *name, QWidget *key_event_receiver); + +signals: + void cell_clicked (const QModelIndex &); + void cell_double_clicked (const QModelIndex &); + void cell_middle_clicked (const QModelIndex &); + void search_triggered (const QString &t); + +protected: + virtual void mouseDoubleClickEvent (QMouseEvent *event); + virtual void mousePressEvent (QMouseEvent *event); + virtual void mouseReleaseEvent (QMouseEvent *event); + virtual void startDrag (Qt::DropActions supportedActions); + virtual bool focusNextPrevChild (bool next); + virtual void keyPressEvent (QKeyEvent *event); + virtual bool event (QEvent *event); + + QWidget *mp_key_event_receiver; +}; + +/** + * @brief The hierarchy control panel + * + * The hierarchy control panel allows changing the cell shown, to + * browse the hierarchy and disable/enable cells + * + * The class communicates with a Layout interface for + * retrieval of the cell hierarchy + */ +class LibrariesView + : public QFrame, + public tl::Object +{ +Q_OBJECT + +public: + /** + * @brief Constructor + * + * @param parent The Qt parent widget + * @param name The layer control panel's widget name + */ + LibrariesView (lay::LayoutView *view, QWidget *parent = 0, const char *name = "libraries_view"); + + /** + * @brief Destructor + */ + ~LibrariesView (); + + /** + * @brief Perform the cell control panel's initialisations on the main menu + */ + static void init_menu (lay::AbstractMenu &menu); + + /** + * @brief The sizeHint implementation for Qt layout management + */ + virtual QSize sizeHint () const; + + /** + * @brief Changing of the background color + */ + void set_background_color (QColor c); + + /** + * @brief Changing of the text color + */ + void set_text_color (QColor c); + + /** + * @brief Sets the active library by name + */ + void select_active_lib_by_name (const std::string &name); + + /** + * @brief Select the active cellview + * + * selects the active cellview by index. The index must be + * a valid index within the context of the layout view. + */ + void select_active (int lib_index); + + /** + * @brief Get the active cellview + * + * get the active cellview index. + */ + int active () + { + return m_active_index; + } + + /** + * @brief Gets the active library or 0 if there is no active library + */ + db::Library *active_lib (); + + /** + * @brief Update the contents if necessary + * + * Update the cell trees according to the hierarchy found in + * the layouts. This version includes a hint which cellview has changed. + */ + void do_update_content (int cv_index); + + /** + * @brief Update the contents if necessary + * + * Update the cell trees according to the hierarchy found in + * the layouts. + */ + void do_update_content () + { + do_update_content (-1); + } + + /** + * @brief Event handler + * + * The object subclasses the event handler in order to intercept + * the GTF probe events (Qt::MaxUser). + */ + virtual bool event (QEvent *e); + + /** + * @brief Return true, if the tree view has the focus + */ + bool has_focus () const; + + /** + * @brief Select split mode + * In split mode all cell trees are shown stacked + */ + void set_split_mode (bool sbs); + + /** + * @brief Returns true if side-by-side mode is set + */ + bool split_mode () const + { + return m_split_mode; + } + + /** + * @brief Gets the layout view this panel is attached to + */ + lay::LayoutView *view () + { + return mp_view; + } + +signals: + void active_library_changed (int cellview_index); + +public slots: + void clicked (const QModelIndex &index); + void header_clicked (); + void double_clicked (const QModelIndex &index); + void middle_clicked (const QModelIndex &index); + void selection_changed (int index); + void context_menu (const QPoint &pt); + void search_triggered (const QString &t); + void search_edited (); + void search_editing_finished (); + void search_next (); + void search_prev (); + +private: + db::Layout *mp_layout; + bool m_enable_cb; + lay::LayoutView *mp_view; + std::vector mp_cell_lists; + std::vector mp_cell_list_headers; + std::vector mp_cell_list_frames; + std::vector m_force_close; + std::vector m_needs_update; + int m_active_index; + bool m_split_mode; + QComboBox *mp_selector; + lay::DecoratedLineEdit *mp_search_edit_box; + QAction *mp_case_sensitive; + QAction *mp_use_regular_expressions; + CellTreeModel *mp_search_model; + QFrame *mp_search_frame; + QCheckBox *mp_search_close_cb; + QSplitter *mp_splitter; + QColor m_background_color; + QColor m_text_color; + tl::DeferredMethod m_do_update_content_dm; + tl::DeferredMethod m_do_full_update_content_dm; + std::auto_ptr mp_tree_style; + std::vector > m_libraries; + + // event listener for changes in the cellview and layout + void update_required (); + + // get the current item + CellTreeItem *current_item () const; + + // clears all widgets of the cell lists + void clear_all (); + + // display string of nth cellview + std::string display_string (int n) const; + + // forces a complete update + void do_full_update_content (); +}; + +} // namespace lay + +#endif + diff --git a/src/laybasic/laybasic/layViewObject.cc b/src/laybasic/laybasic/layViewObject.cc index affdb81d2..37162137c 100644 --- a/src/laybasic/laybasic/layViewObject.cc +++ b/src/laybasic/laybasic/layViewObject.cc @@ -70,7 +70,9 @@ CellDragDropData::serialized () const stream << QString::fromUtf8 ("CellDragDropData"); stream << (quintptr) mp_layout; + stream << (quintptr) mp_library; stream << m_cell_index; + stream << m_is_pcell; return data; } @@ -88,7 +90,10 @@ CellDragDropData::deserialize (const QByteArray &ba) quintptr p = 0; stream >> p; mp_layout = reinterpret_cast (p); + stream >> p; + mp_library = reinterpret_cast (p); stream >> m_cell_index; + stream >> m_is_pcell; return true; } else { diff --git a/src/laybasic/laybasic/layViewObject.h b/src/laybasic/laybasic/layViewObject.h index 432eb585d..5b9185412 100644 --- a/src/laybasic/laybasic/layViewObject.h +++ b/src/laybasic/laybasic/layViewObject.h @@ -53,6 +53,12 @@ class QDragLeaveEvent; class QDropEvent; class QMimeData; +namespace db +{ + class Library; + class Layout; +} + namespace lay { class Viewport; @@ -113,7 +119,7 @@ public: * @brief Default ctor */ CellDragDropData () - : mp_layout (0), m_cell_index (0) + : mp_layout (0), mp_library (0), m_cell_index (0), m_is_pcell (false) { // .. nothing yet .. } @@ -124,8 +130,8 @@ public: * @param layout the layout where the cell lives in * @param cell_index The index of the cell */ - CellDragDropData (const db::Layout *layout, db::cell_index_type cell_index) - : mp_layout (layout), m_cell_index (cell_index) + CellDragDropData (const db::Layout *layout, const db::Library *library, db::cell_index_type cell_or_pcell_index, bool is_pcell) + : mp_layout (layout), mp_library (library), m_cell_index (cell_or_pcell_index), m_is_pcell (is_pcell) { // .. nothing yet .. } @@ -138,6 +144,14 @@ public: return mp_layout; } + /** + * @brief Gets the layout object where the cell lives in + */ + const db::Library *library () const + { + return mp_library; + } + /** * @brief Gets the index of the cell */ @@ -146,6 +160,14 @@ public: return m_cell_index; } + /** + * @brief Gets a value indicating whether the cell is a pcell + */ + bool is_pcell () const + { + return m_is_pcell; + } + /** * @brief Serializes itself to an QByteArray */ @@ -160,7 +182,9 @@ public: private: const db::Layout *mp_layout; + const db::Library *mp_library; db::cell_index_type m_cell_index; + bool m_is_pcell; }; /** diff --git a/src/laybasic/laybasic/laybasic.pro b/src/laybasic/laybasic/laybasic.pro index b285677ca..5d59bd3af 100644 --- a/src/laybasic/laybasic/laybasic.pro +++ b/src/laybasic/laybasic/laybasic.pro @@ -175,7 +175,8 @@ SOURCES = \ layNetlistBrowserModel.cc \ layIndexedNetlistModel.cc \ layNetlistCrossReferenceModel.cc \ - layNetlistBrowserTreeModel.cc + layNetlistBrowserTreeModel.cc \ + layLibrariesView.cc HEADERS = \ gtf.h \ @@ -271,7 +272,8 @@ HEADERS = \ layNetlistBrowserModel.h \ layIndexedNetlistModel.h \ layNetlistCrossReferenceModel.h \ - layNetlistBrowserTreeModel.h + layNetlistBrowserTreeModel.h \ + layLibrariesView.h INCLUDEPATH += $$TL_INC $$GSI_INC $$DB_INC $$RDB_INC DEPENDPATH += $$TL_INC $$GSI_INC $$DB_INC $$RDB_INC diff --git a/src/laybasic/laybasic/laybasicConfig.h b/src/laybasic/laybasic/laybasicConfig.h index 4953c3edb..ecdcabb1f 100644 --- a/src/laybasic/laybasic/laybasicConfig.h +++ b/src/laybasic/laybasic/laybasicConfig.h @@ -124,6 +124,9 @@ static const std::string cfg_flat_cell_list ("flat-cell-list"); static const std::string cfg_split_cell_list ("split-cell-list"); static const std::string cfg_cell_list_sorting ("cell-list-sorting"); +static const std::string cfg_split_lib_views ("split-lib-views"); +static const std::string cfg_current_lib_view ("current-lib-view"); + static const std::string cfg_pan_distance ("pan-distance"); static const std::string cfg_paste_display_mode ("paste-display-mode"); diff --git a/src/lib/lib/libBasicArc.cc b/src/lib/lib/libBasicArc.cc index 27112d4d3..0ad950218 100644 --- a/src/lib/lib/libBasicArc.cc +++ b/src/lib/lib/libBasicArc.cc @@ -204,7 +204,13 @@ BasicArc::produce (const db::Layout &layout, const std::vector &la std::string BasicArc::get_display_name (const db::pcell_parameters_type ¶meters) const { - return std::string("ARC(r=") + tl::micron_to_string (parameters [p_actual_radius1].to_double ()) + ".." + tl::micron_to_string (parameters [p_actual_radius2].to_double ()) + ",a=" + tl::to_string (parameters [p_start_angle].to_double (), 6) + ".." + tl::to_string (parameters [p_end_angle].to_double (), 6) + ")"; + return "ARC(l=" + std::string (parameters [p_layer].to_string ()) + + ",r=" + tl::to_string (parameters [p_actual_radius1].to_double ()) + + ".." + tl::to_string (parameters [p_actual_radius2].to_double ()) + + ",a=" + tl::to_string (parameters [p_start_angle].to_double (), 6) + + ".." + tl::to_string (parameters [p_end_angle].to_double (), 6) + + ",n=" + tl::to_string (parameters [p_npoints].to_int ()) + + ")"; } std::vector diff --git a/src/lib/lib/libBasicCircle.cc b/src/lib/lib/libBasicCircle.cc index bcc29411c..049387f1c 100644 --- a/src/lib/lib/libBasicCircle.cc +++ b/src/lib/lib/libBasicCircle.cc @@ -142,7 +142,10 @@ BasicCircle::produce (const db::Layout &layout, const std::vector std::string BasicCircle::get_display_name (const db::pcell_parameters_type ¶meters) const { - return std::string("CIRCLE(r=") + tl::micron_to_string (parameters [p_actual_radius].to_double ()) + ")"; + return "CIRCLE(l=" + std::string (parameters [p_layer].to_string ()) + + ",r=" + tl::to_string (parameters [p_actual_radius].to_double ()) + + ",n=" + tl::to_string (parameters [p_npoints].to_int ()) + + ")"; } std::vector diff --git a/src/lib/lib/libBasicDonut.cc b/src/lib/lib/libBasicDonut.cc index 9b1a53ad2..504534288 100644 --- a/src/lib/lib/libBasicDonut.cc +++ b/src/lib/lib/libBasicDonut.cc @@ -169,7 +169,11 @@ BasicDonut::produce (const db::Layout &layout, const std::vector & std::string BasicDonut::get_display_name (const db::pcell_parameters_type ¶meters) const { - return std::string("DONUT(r=") + tl::micron_to_string (parameters [p_actual_radius1].to_double ()) + ".." + tl::micron_to_string (parameters [p_actual_radius2].to_double ()) + ")"; + return "DONUT(l=" + std::string (parameters [p_layer].to_string ()) + + ",r=" + tl::to_string (parameters [p_actual_radius1].to_double ()) + + ".." + tl::to_string (parameters [p_actual_radius2].to_double ()) + + ",n=" + tl::to_string (parameters [p_npoints].to_int ()) + + ")"; } std::vector diff --git a/src/lib/lib/libBasicEllipse.cc b/src/lib/lib/libBasicEllipse.cc index 1d213452e..d0eb147ef 100644 --- a/src/lib/lib/libBasicEllipse.cc +++ b/src/lib/lib/libBasicEllipse.cc @@ -173,7 +173,11 @@ BasicEllipse::produce (const db::Layout &layout, const std::vector std::string BasicEllipse::get_display_name (const db::pcell_parameters_type ¶meters) const { - return std::string("ELLIPSE(rx=") + tl::micron_to_string (parameters [p_actual_radius_x].to_double ()) + ",ry=" + tl::micron_to_string (parameters [p_actual_radius_y].to_double ()) + ")"; + return "ELLIPSE(l=" + std::string (parameters [p_layer].to_string ()) + + ",rx=" + tl::to_string (parameters [p_actual_radius_x].to_double ()) + + ",ry=" + tl::to_string (parameters [p_actual_radius_y].to_double ()) + + ",n=" + tl::to_string (parameters [p_npoints].to_int ()) + + ")"; } std::vector diff --git a/src/lib/lib/libBasicPie.cc b/src/lib/lib/libBasicPie.cc index bd3bc6d12..46e8b766b 100644 --- a/src/lib/lib/libBasicPie.cc +++ b/src/lib/lib/libBasicPie.cc @@ -186,7 +186,12 @@ BasicPie::produce (const db::Layout &layout, const std::vector &la std::string BasicPie::get_display_name (const db::pcell_parameters_type ¶meters) const { - return std::string("PIE(r=") + tl::micron_to_string (parameters [p_actual_radius].to_double ()) + ",a=" + tl::to_string (parameters [p_start_angle].to_double (), 6) + ".." + tl::to_string (parameters [p_end_angle].to_double (), 6) + ")"; + return "PIE(l=" + std::string (parameters [p_layer].to_string ()) + + ",r=" + tl::to_string (parameters [p_actual_radius].to_double ()) + + ",a=" + tl::to_string (parameters [p_start_angle].to_double (), 6) + + ".." + tl::to_string (parameters [p_end_angle].to_double (), 6) + + ",n=" + tl::to_string (parameters [p_npoints].to_int ()) + + ")"; } std::vector diff --git a/src/lib/lib/libBasicRoundPath.cc b/src/lib/lib/libBasicRoundPath.cc index 20931f27b..11844284d 100644 --- a/src/lib/lib/libBasicRoundPath.cc +++ b/src/lib/lib/libBasicRoundPath.cc @@ -108,7 +108,10 @@ BasicRoundPath::produce (const db::Layout &layout, const std::vector diff --git a/src/lib/lib/libBasicRoundPolygon.cc b/src/lib/lib/libBasicRoundPolygon.cc index 110679a1b..da14fc4ea 100644 --- a/src/lib/lib/libBasicRoundPolygon.cc +++ b/src/lib/lib/libBasicRoundPolygon.cc @@ -112,7 +112,10 @@ BasicRoundPolygon::produce (const db::Layout &layout, const std::vector diff --git a/src/lib/lib/libBasicStrokedPolygon.cc b/src/lib/lib/libBasicStrokedPolygon.cc index fb910acbb..94b834163 100644 --- a/src/lib/lib/libBasicStrokedPolygon.cc +++ b/src/lib/lib/libBasicStrokedPolygon.cc @@ -134,7 +134,12 @@ BasicStrokedPolygon::produce (const db::Layout &layout, const std::vector diff --git a/src/lib/lib/libBasicText.cc b/src/lib/lib/libBasicText.cc index 84d95a761..b20683c7f 100644 --- a/src/lib/lib/libBasicText.cc +++ b/src/lib/lib/libBasicText.cc @@ -177,7 +177,8 @@ BasicText::get_display_name (const db::pcell_parameters_type ¶meters) const if (! parameters.empty ()) { t = parameters [p_text].to_string (); } - return std::string("TEXT('") + t + "')"; + return "TEXT(l=" + std::string (parameters [p_layer].to_string ()) + + ",'" + t + "')"; } std::vector