diff --git a/src/plugins/tools/net_tracer/lay_plugin/NetTracerConnectivityEditor.ui b/src/plugins/tools/net_tracer/lay_plugin/NetTracerConnectivityEditor.ui
new file mode 100644
index 000000000..62af9ff85
--- /dev/null
+++ b/src/plugins/tools/net_tracer/lay_plugin/NetTracerConnectivityEditor.ui
@@ -0,0 +1,343 @@
+
+
+ NetTracerConnectivityEditor
+
+
+
+ 0
+ 0
+ 572
+ 449
+
+
+
+ Form
+
+
+
+ 6
+
+
+ 9
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Raised
+
+
+
+ 6
+
+
+ 0
+
+
-
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Raised
+
+
+
+ 6
+
+
+ 0
+
+
-
+
+
+ <html>Connectivity (<a href="int:/about/connectivity.xml">See here for details</a>)</html>
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Raised
+
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ ...
+
+
+
+ :/down_16px.png:/down_16px.png
+
+
+
+ -
+
+
+ ...
+
+
+
+ :/del_16px.png:/del_16px.png
+
+
+ Del
+
+
+
+ -
+
+
+ QAbstractItemView::AllEditTriggers
+
+
+ true
+
+
+ QAbstractItemView::SelectRows
+
+
+ 3
+
+
+
+
+
+
+ -
+
+
+ ...
+
+
+
+ :/add_16px.png:/add_16px.png
+
+
+ Return
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 131
+
+
+
+
+ -
+
+
+ ...
+
+
+
+ :/up_16px.png:/up_16px.png
+
+
+
+
+
+
+
+
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Raised
+
+
+
+ 6
+
+
+ 0
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Raised
+
+
+
+ 6
+
+
+ 0
+
+
-
+
+
+ <html>Computed and symbolic layers (<a href="int:/about/symbolic_layers.xml">See here for details</a>)</html>
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Raised
+
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ ...
+
+
+
+ :/down_16px.png:/down_16px.png
+
+
+
+ -
+
+
+ ...
+
+
+
+ :/del_16px.png:/del_16px.png
+
+
+ Del
+
+
+
+ -
+
+
+ QAbstractItemView::AllEditTriggers
+
+
+ true
+
+
+ QAbstractItemView::SelectRows
+
+
+ 2
+
+
+
+
+
+ -
+
+
+ ...
+
+
+
+ :/add_16px.png:/add_16px.png
+
+
+ Return
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 131
+
+
+
+
+ -
+
+
+ ...
+
+
+
+ :/up_16px.png:/up_16px.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerTechComponentEditor.cc b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerTechComponentEditor.cc
new file mode 100644
index 000000000..3d8293d78
--- /dev/null
+++ b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerTechComponentEditor.cc
@@ -0,0 +1,310 @@
+
+/*
+
+ KLayout Layout Viewer
+ Copyright (C) 2006-2022 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 "layNetTracerTechComponentEditor.h"
+#include "layNetTracerConfig.h"
+
+#include "layConfigurationDialog.h"
+#include "laybasicConfig.h"
+#include "layConverters.h"
+#include "layFinder.h"
+#include "layLayoutView.h"
+#include "layTechSetupDialog.h"
+#include "layFileDialog.h"
+#include "layQtTools.h"
+#include "tlExceptions.h"
+#include "tlXMLWriter.h"
+#include "tlUtils.h"
+#include "gsiDecl.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+namespace lay
+{
+
+// @@@ TODO: edit on double-click
+
+// -----------------------------------------------------------------------------------
+// NetTracerTechComponentEditor implementation
+
+NetTracerTechComponentEditor::NetTracerTechComponentEditor (QWidget *parent)
+ : TechnologyComponentEditor (parent)
+{
+ Ui::NetTracerTechComponentEditor::setupUi (this);
+
+ connect (add_pb, SIGNAL (clicked ()), this, SLOT (add_clicked ()));
+ connect (del_pb, SIGNAL (clicked ()), this, SLOT (del_clicked ()));
+ connect (move_up_pb, SIGNAL (clicked ()), this, SLOT (move_up_clicked ()));
+ connect (move_down_pb, SIGNAL (clicked ()), this, SLOT (move_down_clicked ()));
+
+ stack_tree->header ()->setHighlightSections (false);
+ stack_tree->header ()->setStretchLastSection (true);
+
+ connect (stack_tree, SIGNAL (currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), this, SLOT (current_item_changed(QTreeWidgetItem *, QTreeWidgetItem *)));
+ connect (stack_tree, SIGNAL (itemChanged(QTreeWidgetItem *, int)), this, SLOT (item_changed(QTreeWidgetItem *, int)));
+}
+
+void
+NetTracerTechComponentEditor::commit ()
+{
+ db::NetTracerTechnologyComponent *data = dynamic_cast (tech_component ());
+ if (! data) {
+ return;
+ }
+
+ *data = m_data;
+}
+
+void
+NetTracerTechComponentEditor::setup ()
+{
+ db::NetTracerTechnologyComponent *data = dynamic_cast (tech_component ());
+ if (! data) {
+ return;
+ }
+
+ m_data = *data;
+
+ if (m_data.size () == 0) {
+ m_data.push_back (db::NetTracerConnectivity ());
+ }
+
+ update ();
+}
+
+void
+NetTracerTechComponentEditor::item_changed (QTreeWidgetItem *item, int column)
+{
+ int row = stack_tree->indexOfTopLevelItem (item);
+ if (row >= 0 && row < int (m_data.size ())) {
+ if (column == 0) {
+ std::string n = tl::to_string (item->data (column, Qt::EditRole));
+ m_data.begin ()[row].set_name (n);
+ if (n.empty ()) {
+ item->setData (column, Qt::DisplayRole, tr ("(default)"));
+ } else {
+ item->setData (column, Qt::DisplayRole, tl::to_qstring (n));
+ }
+ }
+ if (column == 1) {
+ m_data.begin ()[row].set_description (tl::to_string (item->data (column, Qt::EditRole)));
+ }
+ }
+}
+
+void
+NetTracerTechComponentEditor::current_item_changed (QTreeWidgetItem *current, QTreeWidgetItem *previous)
+{
+ commit_current (previous);
+
+ int row = current ? stack_tree->indexOfTopLevelItem (current) : -1;
+ if (row < 0 || row >= int (m_data.size ())) {
+ connectivity_editor_widget->set_connectivity (db::NetTracerConnectivity ());
+ connectivity_editor_widget->hide ();
+ } else {
+ connectivity_editor_widget->set_connectivity (m_data.begin ()[row]);
+ connectivity_editor_widget->show ();
+ }
+}
+
+void
+NetTracerTechComponentEditor::commit_current ()
+{
+ commit_current (stack_tree->currentItem ());
+}
+
+void
+NetTracerTechComponentEditor::commit_current (QTreeWidgetItem *current)
+{
+ int row = current ? stack_tree->indexOfTopLevelItem (current) : -1;
+ if (row >= 0 && row < int (m_data.size ())) {
+ m_data.begin () [row] = connectivity_editor_widget->get_connectiviy ();
+ }
+}
+
+void
+NetTracerTechComponentEditor::add_clicked ()
+{
+ // removes focus from the tree view - commits the data
+ add_pb->setFocus ();
+ commit_current ();
+
+ int row = stack_tree->currentItem () ? stack_tree->indexOfTopLevelItem (stack_tree->currentItem ()) : -1;
+ if (row < 0) {
+ m_data.push_back (db::NetTracerConnectivity ());
+ row = int (m_data.size () - 1);
+ } else {
+ row += 1;
+ m_data.insert (m_data.begin () + row, db::NetTracerConnectivity ());
+ }
+
+ update ();
+ stack_tree->setCurrentItem (stack_tree->topLevelItem (row));
+}
+
+void
+NetTracerTechComponentEditor::del_clicked ()
+{
+ // removes focus from the tree view - commits the data
+ del_pb->setFocus ();
+ commit_current ();
+
+ std::set selected_rows;
+ QModelIndexList selected_indices = stack_tree->selectionModel ()->selectedIndexes ();
+ for (auto i = selected_indices.begin (); i != selected_indices.end (); ++i) {
+ selected_rows.insert (i->row ());
+ }
+
+ stack_tree->setCurrentIndex (QModelIndex ());
+
+ int offset = 0;
+ for (std::set::const_iterator r = selected_rows.begin (); r != selected_rows.end (); ++r) {
+ m_data.erase (m_data.begin () + (*r - offset));
+ ++offset;
+ }
+
+ update ();
+}
+
+void
+NetTracerTechComponentEditor::move_up_clicked ()
+{
+ // removes focus from the tree view - commits the data
+ move_up_pb->setFocus ();
+ commit_current ();
+
+ std::set selected_rows;
+ QModelIndexList selected_indices = stack_tree->selectionModel ()->selectedIndexes ();
+ for (auto i = selected_indices.begin (); i != selected_indices.end (); ++i) {
+ selected_rows.insert (i->row ());
+ }
+
+ QTreeWidgetItem *current = stack_tree->currentItem ();
+ int n_current = current ? current->data (0, Qt::UserRole).toInt () : -1;
+
+ stack_tree->setCurrentIndex (QModelIndex ());
+
+ int n = 0;
+ for (db::NetTracerTechnologyComponent::iterator l = m_data.begin (); l != m_data.end (); ++l, ++n) {
+ if (selected_rows.find (n + 1) != selected_rows.end () && selected_rows.find (n) == selected_rows.end ()) {
+ std::swap (m_data.begin () [n + 1], m_data.begin () [n]);
+ selected_rows.erase (n + 1);
+ selected_rows.insert (n);
+ if (n_current == n + 1) {
+ n_current = n;
+ }
+ }
+ }
+
+ update ();
+
+ // select the new items
+ for (std::set ::const_iterator s = selected_rows.begin (); s != selected_rows.end (); ++s) {
+ stack_tree->selectionModel ()->select (stack_tree->model ()->index (*s, 0), QItemSelectionModel::Select | QItemSelectionModel::Rows);
+ }
+
+ if (n_current >= 0) {
+ stack_tree->selectionModel ()->select (stack_tree->model ()->index (n_current, 0), QItemSelectionModel::Current | QItemSelectionModel::Rows);
+ }
+}
+
+void
+NetTracerTechComponentEditor::move_down_clicked ()
+{
+ // removes focus from the tree view - commits the data
+ move_down_pb->setFocus ();
+ commit_current ();
+
+ std::set selected_rows;
+ QModelIndexList selected_indices = stack_tree->selectionModel ()->selectedIndexes ();
+ for (auto i = selected_indices.begin (); i != selected_indices.end (); ++i) {
+ selected_rows.insert (i->row ());
+ }
+
+ QTreeWidgetItem *current = stack_tree->currentItem ();
+ int n_current = current ? current->data (0, Qt::UserRole).toInt () : -1;
+
+ stack_tree->setCurrentIndex (QModelIndex ());
+
+ int n = int (m_data.size ());
+ for (db::NetTracerTechnologyComponent::iterator l = m_data.end (); l != m_data.begin (); ) {
+ --l;
+ --n;
+ if (selected_rows.find (n - 1) != selected_rows.end () && selected_rows.find (n) == selected_rows.end ()) {
+ std::swap (m_data.begin () [n - 1], m_data.begin () [n]);
+ selected_rows.erase (n - 1);
+ selected_rows.insert (n);
+ if (n_current == n - 1) {
+ n_current = n;
+ }
+ }
+ }
+
+ update ();
+
+ // select the new items
+ for (std::set ::const_iterator s = selected_rows.begin (); s != selected_rows.end (); ++s) {
+ stack_tree->selectionModel ()->select (stack_tree->model ()->index (*s, 0), QItemSelectionModel::Select | QItemSelectionModel::Rows);
+ }
+
+ if (n_current >= 0) {
+ stack_tree->selectionModel ()->select (stack_tree->model ()->index (n_current, 0), QItemSelectionModel::Current | QItemSelectionModel::Rows);
+ }
+}
+
+void
+NetTracerTechComponentEditor::update ()
+{
+ stack_tree->clear ();
+ stack_tree->clearSelection ();
+
+ int n = 0;
+ for (db::NetTracerTechnologyComponent::iterator l = m_data.begin (); l != m_data.end (); ++l, ++n) {
+
+ QTreeWidgetItem *item = new QTreeWidgetItem (stack_tree);
+ item->setFlags (item->flags () | Qt::ItemIsEditable);
+
+ if (l->name ().empty ()) {
+ item->setData (0, Qt::DisplayRole, QVariant (tr ("(default)")));
+ } else {
+ item->setData (0, Qt::DisplayRole, QVariant (tl::to_qstring (l->name ())));
+ }
+ item->setData (0, Qt::EditRole, QVariant (tl::to_qstring (l->name ())));
+ item->setData (1, Qt::DisplayRole, QVariant (tl::to_qstring (l->description ())));
+
+ item->setData (0, Qt::UserRole, QVariant (n));
+
+ }
+
+ current_item_changed (0, 0);
+}
+
+}
+
diff --git a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerTechComponentEditor.h b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerTechComponentEditor.h
new file mode 100644
index 000000000..db802fe6b
--- /dev/null
+++ b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerTechComponentEditor.h
@@ -0,0 +1,81 @@
+
+/*
+
+ KLayout Layout Viewer
+ Copyright (C) 2006-2022 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_layNetTracerTechComponentEditor
+#define HDR_layNetTracerTechComponentEditor
+
+#include "ui_NetTracerTechComponentEditor.h"
+
+#include "dbNetTracer.h"
+#include "dbNetTracerIO.h"
+#include "dbTechnology.h"
+
+#include "layNetTracerConfig.h"
+#include "layBrowser.h"
+#include "layPlugin.h"
+#include "layViewObject.h"
+#include "layMarker.h"
+#include "layTechnology.h"
+
+#include "tlObject.h"
+
+namespace db
+{
+ class NetTracerTechnologyComponent;
+}
+
+namespace lay
+{
+
+class NetTracerTechComponentEditor
+ : public lay::TechnologyComponentEditor,
+ public Ui::NetTracerTechComponentEditor
+{
+Q_OBJECT
+
+public:
+ NetTracerTechComponentEditor (QWidget *parent);
+
+ void commit ();
+ void setup ();
+
+public slots:
+ void add_clicked ();
+ void del_clicked ();
+ void move_up_clicked ();
+ void move_down_clicked ();
+ void current_item_changed (QTreeWidgetItem *current, QTreeWidgetItem *previous);
+ void item_changed (QTreeWidgetItem *item, int column);
+private:
+ db::NetTracerTechnologyComponent m_data;
+
+ void update ();
+ void commit_current (QTreeWidgetItem *current);
+ void commit_current ();
+};
+
+}
+
+#endif
+