diff --git a/src/lay/lay/laySaltManagerDialog.cc b/src/lay/lay/laySaltManagerDialog.cc index 9740a9874..37ff6a90d 100644 --- a/src/lay/lay/laySaltManagerDialog.cc +++ b/src/lay/lay/laySaltManagerDialog.cc @@ -26,6 +26,7 @@ #include "laySaltDownloadManager.h" #include "laySalt.h" #include "layVersion.h" +#include "layItemDelegates.h" #include "ui_SaltGrainTemplateSelectionDialog.h" #include "tlString.h" #include "tlExceptions.h" @@ -59,7 +60,7 @@ public: m_salt_templates.add_location (":/salt_templates"); salt_view->setModel (new SaltModel (this, &m_salt_templates)); - salt_view->setItemDelegate (new SaltItemDelegate (this)); + salt_view->setItemDelegate (new lay::HTMLItemDelegate (this)); salt_view->setCurrentIndex (salt_view->model ()->index (0, 0, QModelIndex ())); } @@ -130,7 +131,7 @@ SaltManagerDialog::SaltManagerDialog (QWidget *parent, lay::Salt *salt, const st SaltModel *model = new SaltModel (this, mp_salt); model->set_empty_explanation (tr ("No packages are present on this system")); salt_view->setModel (model); - salt_view->setItemDelegate (new SaltItemDelegate (this)); + salt_view->setItemDelegate (new lay::HTMLItemDelegate (this)); SaltModel *mine_model; @@ -138,13 +139,13 @@ SaltManagerDialog::SaltManagerDialog (QWidget *parent, lay::Salt *salt, const st mine_model = new SaltModel (this, &m_salt_mine, mp_salt, true); mine_model->set_empty_explanation (tr ("All available packages are installed")); salt_mine_view_new->setModel (mine_model); - salt_mine_view_new->setItemDelegate (new SaltItemDelegate (this)); + salt_mine_view_new->setItemDelegate (new lay::HTMLItemDelegate (this)); // This model will show only the grains of mp_salt_mine which are present in mp_salt already. mine_model = new SaltModel (this, &m_salt_mine, mp_salt, false); mine_model->set_empty_explanation (tr ("No packages are installed")); salt_mine_view_update->setModel (mine_model); - salt_mine_view_update->setItemDelegate (new SaltItemDelegate (this)); + salt_mine_view_update->setItemDelegate (new lay::HTMLItemDelegate (this)); mode_tab->setCurrentIndex (0); diff --git a/src/lay/lay/laySaltModel.cc b/src/lay/lay/laySaltModel.cc index c3be49c5d..a5e58c5f8 100644 --- a/src/lay/lay/laySaltModel.cc +++ b/src/lay/lay/laySaltModel.cc @@ -25,84 +25,13 @@ #include #include -#include #include -#include namespace lay { // -------------------------------------------------------------------------------------- -SaltItemDelegate::SaltItemDelegate (QObject *parent) - : QStyledItemDelegate (parent) -{ - // .. nothing yet .. -} - -const int textWidth = 500; - -void -SaltItemDelegate::paint (QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const -{ - QStyleOptionViewItemV4 optionV4 = option; - initStyleOption (&optionV4, index); - // let the text take all the available space (fixes #144) - optionV4.showDecorationSelected = true; - - bool is_enabled = (optionV4.state & QStyle::State_Enabled); - if ((index.flags () & 0x10000) != 0) { - // the item wants to be drawn "disabled" - is_enabled = false; - } - - optionV4.state |= QStyle::State_Enabled; - - QStyle *style = optionV4.widget ? optionV4.widget->style () : QApplication::style (); - - QTextDocument doc; - doc.setHtml (optionV4.text); - - optionV4.text = QString (); - style->drawControl (QStyle::CE_ItemViewItem, &optionV4, painter); - - QAbstractTextDocumentLayout::PaintContext ctx; - - if (optionV4.state & QStyle::State_Selected) { - ctx.palette.setColor (QPalette::Text, optionV4.palette.color (QPalette::Active, QPalette::HighlightedText)); - } else if (! is_enabled) { - ctx.palette.setColor (QPalette::Text, optionV4.palette.color (QPalette::Disabled, QPalette::Text)); - } - - QRect textRect = style->subElementRect (QStyle::SE_ItemViewItemText, &optionV4); - painter->save (); - painter->translate (textRect.topLeft ()); - painter->setClipRect (textRect.translated (-textRect.topLeft ())); - doc.setTextWidth (textWidth); - doc.documentLayout ()->draw (painter, ctx); - painter->restore (); -} - -QSize -SaltItemDelegate::sizeHint (const QStyleOptionViewItem &option, const QModelIndex &index) const -{ - QStyleOptionViewItemV4 optionV4 = option; - initStyleOption (&optionV4, index); - - const QListView *view = dynamic_cast (optionV4.widget); - QSize icon_size (0, 0); - if (view) { - icon_size = view->iconSize (); - } - - QTextDocument doc; - doc.setHtml (optionV4.text); - doc.setTextWidth (textWidth); - return QSize (textWidth + icon_size.width () + 6, std::max (icon_size.height () + 12, int (doc.size ().height ()))); -} - -// -------------------------------------------------------------------------------------- - SaltModel::SaltModel (QObject *parent, lay::Salt *salt, lay::Salt *salt_filtered, bool salt_exclude) : QAbstractItemModel (parent), mp_salt (salt), mp_salt_filtered (salt_filtered), m_salt_exclude (salt_exclude), diff --git a/src/lay/lay/laySaltModel.h b/src/lay/lay/laySaltModel.h index d0714a644..7b8519ad3 100644 --- a/src/lay/lay/laySaltModel.h +++ b/src/lay/lay/laySaltModel.h @@ -196,21 +196,6 @@ public: void create_ordered_list (); }; -// -------------------------------------------------------------------------------------- - -/** - * @brief A delegate displaying the summary of a grain - */ -class SaltItemDelegate - : public QStyledItemDelegate -{ -public: - SaltItemDelegate (QObject *parent); - - void paint (QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; - QSize sizeHint (const QStyleOptionViewItem &option, const QModelIndex &index) const; -}; - } #endif diff --git a/src/laybasic/laybasic/NetlistBrowserPage.ui b/src/laybasic/laybasic/NetlistBrowserPage.ui index bf6f55420..28ba2a25e 100644 --- a/src/laybasic/laybasic/NetlistBrowserPage.ui +++ b/src/laybasic/laybasic/NetlistBrowserPage.ui @@ -63,93 +63,15 @@ 6 - - - - ... - - - - :/down.png:/down.png - - - - - - - ... - - - - :/up.png:/up.png - - - - - - - - 0 - 0 - - - - - 0 - 4 - - - - Qt::ActionsContextMenu - - - QAbstractItemView::ExtendedSelection - - - QAbstractItemView::SelectRows - - - true - - - true - - - true - - - true - - - - - - - Netlist - - - - - - - - 0 - 0 - - - - - 0 - 0 - - + + QFrame::NoFrame QFrame::Raised - + 0 @@ -162,6 +84,28 @@ 0 + + + + ... + + + + :/down.png:/down.png + + + + + + + ... + + + + :/up.png:/up.png + + + @@ -210,6 +154,138 @@ + + + + + 0 + 0 + + + + + 0 + 4 + + + + Qt::ActionsContextMenu + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + true + + + true + + + true + + + false + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + ... + + + + :/back_16.png:/back_16.png + + + true + + + + + + + ... + + + + :/forward_16.png:/forward_16.png + + + true + + + + + + + Netlist + + + + + + @@ -218,9 +294,6 @@ directory_tree - dir_up_pb - dir_down_pb - filter diff --git a/src/laybasic/laybasic/layItemDelegates.cc b/src/laybasic/laybasic/layItemDelegates.cc new file mode 100644 index 000000000..aa2ad73ec --- /dev/null +++ b/src/laybasic/laybasic/layItemDelegates.cc @@ -0,0 +1,193 @@ + +/* + + 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 "layItemDelegates.h" + +#include +#include +#include +#include +#include + +namespace lay +{ + +// -------------------------------------------------------------------------------------- + +HTMLItemDelegate::HTMLItemDelegate (QObject *parent) + : QStyledItemDelegate (parent) +{ + m_icon_margin = 6; + m_icon_spacing = 6; + m_text_margin = 4; + m_text_height = -1; + m_text_width = 500; + m_plain_text = false; + m_anchors_clickable = false; +} + +void +HTMLItemDelegate::set_anchors_clickable (bool a) +{ + m_anchors_clickable = a; +} + +void +HTMLItemDelegate::set_plain_text (bool pt) +{ + m_plain_text = pt; +} + +void +HTMLItemDelegate::set_icon_margin (int m) +{ + m_icon_margin = m; +} + +void +HTMLItemDelegate::set_icon_spacing (int s) +{ + m_icon_spacing = s; +} + +void +HTMLItemDelegate::set_text_margin (int m) +{ + m_text_margin = m; +} + +void +HTMLItemDelegate::set_text_height (int h) +{ + m_text_height = h; +} + +void +HTMLItemDelegate::set_text_width (int w) +{ + m_text_width = w; +} + +void +HTMLItemDelegate::paint (QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QStyleOptionViewItemV4 option_v4 = option; + initStyleOption (&option_v4, index); + // let the text take all the available space (fixes #144) + option_v4.showDecorationSelected = true; + + bool is_enabled = (option_v4.state & QStyle::State_Enabled); + if ((index.flags () & 0x10000) != 0) { + // the item wants to be drawn "disabled" + is_enabled = false; + } + + option_v4.state |= QStyle::State_Enabled; + + QStyle *style = option_v4.widget ? option_v4.widget->style () : QApplication::style (); + + QTextDocument doc; + if (m_plain_text) { + doc.setPlainText (option_v4.text); + } else { + doc.setHtml (option_v4.text); + } + doc.setTextWidth (m_text_width); + doc.setDocumentMargin (m_text_margin); + + option_v4.text = QString (); + style->drawControl (QStyle::CE_ItemViewItem, &option_v4, painter); + + QAbstractTextDocumentLayout::PaintContext ctx; + + if (option_v4.state & QStyle::State_Selected) { + ctx.palette.setColor (QPalette::Text, option_v4.palette.color (QPalette::Active, QPalette::HighlightedText)); + } else if (! is_enabled) { + ctx.palette.setColor (QPalette::Text, option_v4.palette.color (QPalette::Disabled, QPalette::Text)); + } + + QRect text_rect = style->subElementRect (QStyle::SE_ItemViewItemText, &option_v4); + painter->save (); + QPoint tr = text_rect.topLeft (); + painter->translate (tr); + painter->setClipRect (text_rect.translated (-tr)); + doc.documentLayout ()->draw (painter, ctx); + painter->restore (); +} + +QSize +HTMLItemDelegate::sizeHint (const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QStyleOptionViewItemV4 option_v4 = option; + initStyleOption (&option_v4, index); + + const QAbstractItemView *view = dynamic_cast (option_v4.widget); + QSize icon_size (0, 0); + if (view) { + icon_size = view->iconSize (); + } + + QTextDocument doc; + if (m_plain_text) { + doc.setPlainText (option_v4.text); + } else { + doc.setHtml (option_v4.text); + } + doc.setTextWidth (m_text_width); + doc.setDocumentMargin (m_text_margin); + bool has_icon = ! option_v4.icon.isNull (); + int th = m_text_height < 0 ? int (doc.size ().height ()) : m_text_height; + return QSize (m_text_width + (has_icon ? icon_size.width () + m_icon_spacing : 0), std::max (has_icon ? icon_size.height () + 2 * m_icon_margin : 0, th)); +} + +bool +HTMLItemDelegate::editorEvent (QEvent *event, QAbstractItemModel * /*model*/, const QStyleOptionViewItem &option, const QModelIndex &index) +{ + if ((event->type () == QEvent::MouseButtonRelease || event->type () == QEvent::MouseButtonPress) && ! m_plain_text && m_anchors_clickable) { + + QMouseEvent *mouse_event = static_cast (event); + + QStyleOptionViewItemV4 option_v4 = option; + initStyleOption (&option_v4, index); + + QTextDocument doc; + doc.setHtml (option_v4.text); + doc.setTextWidth (m_text_width); + doc.setDocumentMargin (m_text_margin); + + QStyle *style = option_v4.widget ? option_v4.widget->style () : QApplication::style (); + QRect text_rect = style->subElementRect (QStyle::SE_ItemViewItemText, &option_v4); + + QString a = doc.documentLayout ()->anchorAt (mouse_event->pos () - text_rect.topLeft ()); + if (! a.isNull ()) { + if (event->type () == QEvent::MouseButtonRelease) { + emit anchor_clicked (a); + } + return true; + } + + } + + return false; +} + +} diff --git a/src/laybasic/laybasic/layItemDelegates.h b/src/laybasic/laybasic/layItemDelegates.h new file mode 100644 index 000000000..d64363c6f --- /dev/null +++ b/src/laybasic/laybasic/layItemDelegates.h @@ -0,0 +1,103 @@ + +/* + + 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_layItemDelegates +#define HDR_layItemDelegates + +#include + +namespace lay +{ + +// -------------------------------------------------------------------------------------- + +/** + * @brief A delegate displaying the display text as HTML formatted text + */ +class HTMLItemDelegate + : public QStyledItemDelegate +{ +Q_OBJECT + +public: + HTMLItemDelegate (QObject *parent); + + virtual void paint (QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; + virtual QSize sizeHint (const QStyleOptionViewItem &option, const QModelIndex &index) const; + virtual bool editorEvent (QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index); + + void set_anchors_clickable (bool a); + bool anchors_clickable () const + { + return m_anchors_clickable; + } + + void set_icon_margin (int m); + int icon_margin () const + { + return m_icon_margin; + } + + void set_icon_spacing (int s); + int icon_spacing () const + { + return m_icon_spacing; + } + + void set_text_margin (int m); + int text_margin () const + { + return m_text_margin; + } + + void set_text_height (int h); + int text_height () const + { + return m_text_height; + } + + void set_text_width (int w); + int text_width () const + { + return m_text_width; + } + + void set_plain_text (bool pt); + bool plain_text () const + { + return m_plain_text; + } + +signals: + void anchor_clicked (const QString &url); + +private: + int m_icon_margin, m_icon_spacing, m_text_margin; + int m_text_width, m_text_height; + bool m_plain_text; + bool m_anchors_clickable; +}; + + +} + +#endif diff --git a/src/laybasic/laybasic/layNetlistBrowserPage.cc b/src/laybasic/laybasic/layNetlistBrowserPage.cc index bb10723c4..a0a3f618d 100644 --- a/src/laybasic/laybasic/layNetlistBrowserPage.cc +++ b/src/laybasic/laybasic/layNetlistBrowserPage.cc @@ -22,9 +22,12 @@ #include "layNetlistBrowserPage.h" +#include "layItemDelegates.h" #include "dbLayoutToNetlist.h" #include "dbNetlistDeviceClasses.h" +#include + namespace lay { @@ -429,6 +432,45 @@ NetlistBrowserModel::data (const QModelIndex &index, int role) const } } +QString +NetlistBrowserModel::make_link_to (const db::Net *net) const +{ + if (! net) { + return QString (); + } else { + void *id = make_id_circuit_net (circuit_index (net->circuit ()), net_index (net)); + return tl::to_qstring (tl::sprintf ("%s", tl::to_string (reinterpret_cast (id)), net->expanded_name ())); + } +} + +static +std::string device_string (const db::Device *device) +{ + if (! device || ! device->device_class ()) { + return std::string (); + } + + std::string s = device->device_class ()->name (); + bool first = true; + const std::vector &pd = device->device_class ()->parameter_definitions (); + for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { + if (p->is_primary ()) { + if (first) { + s += " ["; + first = false; + } else { + s += ", "; + } + s += p->name (); + s += "="; + double v = device->parameter_value (p->id ()); + s += tl::to_string (v); + } + } + s += "]"; + return s; +} + QString NetlistBrowserModel::text (const QModelIndex &index) const { @@ -436,99 +478,152 @@ NetlistBrowserModel::text (const QModelIndex &index) const if (is_id_circuit (id)) { - db::Circuit *circuit = circuit_from_id (id); - if (circuit) { - return tl::to_qstring (circuit->name ()); + // circuit: header column = name, other columns empty + if (index.column () == 0) { + db::Circuit *circuit = circuit_from_id (id); + if (circuit) { + return tl::to_qstring (circuit->name ()); + } } } else if (is_id_circuit_pin (id) || is_id_circuit_subcircuit_pin (id)) { - db::Pin *pin = pin_from_id (id); - if (pin) { - return tl::to_qstring (pin->expanded_name ()); + // circuit/pin: header column = name, other columns empty + if (index.column () == 0) { + db::Pin *pin = pin_from_id (id); + if (pin) { + return tl::to_qstring (pin->expanded_name ()); + } } } else if (is_id_circuit_pin_net (id)) { + // circuit/pin/net: header column = name, second column link to net db::Circuit *circuit = circuit_from_id (id); db::Pin *pin = pin_from_id (id); if (pin) { db::Net *net = circuit->net_for_pin (pin->id ()); if (net) { - return tl::to_qstring (net->expanded_name ()); + if (index.column () == 0) { + return tl::to_qstring (net->expanded_name ()); + } else { + return make_link_to (net); + } } } } else if (is_id_circuit_device (id)) { + // circuit/device: header column = class + parameters, second column device name db::Device *device = device_from_id (id); - if (device) { - return tl::to_qstring (device->expanded_name ()); + if (device && device->device_class ()) { + + if (index.column () == 0) { + return tl::to_qstring (device_string (device)); + } else { + return tl::to_qstring (device->expanded_name ()); + } + } } else if (is_id_circuit_device_terminal (id)) { + // circuit/device/terminal: header column = terminal name, second column link to net db::Device *device = device_from_id (id); if (device && device->device_class ()) { size_t terminal = circuit_device_terminal_index_from_id (id); if (device->device_class ()->terminal_definitions ().size () > terminal) { - return tl::to_qstring (device->device_class ()->terminal_definitions () [terminal].name ()); + const db::DeviceTerminalDefinition &td = device->device_class ()->terminal_definitions () [terminal]; + if (index.column () == 0) { + return tl::to_qstring (td.name ()); + } else { + return make_link_to (device->net_for_terminal (td.id ())); + } } } } else if (is_id_circuit_subcircuit (id)) { + // circuit/subcircuit: header column = circuit name, second column subcircuit name db::SubCircuit *subcircuit = subcircuit_from_id (id); if (subcircuit) { - return tl::to_qstring (subcircuit->expanded_name ()); + if (index.column () == 0) { + return tl::to_qstring (subcircuit->circuit_ref ()->name ()); + } else { + return tl::to_qstring (subcircuit->expanded_name ()); + } } } else if (is_id_circuit_net (id)) { + // circuit/net: header column = node count, second column net name db::Net *net = net_from_id (id); if (net) { - return tl::to_qstring (net->expanded_name ()); + if (index.column () == 0) { + return tl::to_qstring ("(" + tl::to_string (net->pin_count () + net->terminal_count () + net->subcircuit_pin_count ()) + ")"); + } else { + return tl::to_qstring (net->expanded_name ()); + } } } else if (is_id_circuit_net_pin (id)) { + // circuit/net/pin: header column = pin name, second column empty (for now) const db::NetPinRef *ref = net_pinref_from_id (id); if (ref && ref->pin ()) { - return tl::to_qstring (ref->pin ()->expanded_name ()); + if (index.column () == 0) { + // TODO: make link to pin + return tl::to_qstring (ref->pin ()->expanded_name ()); + } + // TODO: in case of compare use second and third column } } else if (is_id_circuit_net_subcircuit_pin (id)) { + // TODO: enhance const db::NetSubcircuitPinRef *ref = net_subcircuit_pinref_from_id (id); if (ref && ref->pin ()) { + // TODO: make link to circuit return tl::to_qstring (ref->pin ()->expanded_name ()); } } else if (is_id_circuit_net_subcircuit_pin_others (id)) { + // TODO: enhance const db::NetSubcircuitPinRef *ref = net_subcircuit_pinref_from_id (id); size_t other_index = circuit_net_subcircuit_pin_other_index_from_id (id); if (ref && ref->pin () && ref->subcircuit () && ref->subcircuit ()->circuit_ref () && ref->subcircuit ()->circuit_ref ()->pin_by_id (other_index)) { const db::Pin *pin = ref->subcircuit ()->circuit_ref ()->pin_by_id (other_index); + // TODO: make link to pin inside circuit return tl::to_qstring (pin->expanded_name ()); } } else if (is_id_circuit_net_device_terminal (id)) { + // circuit/net/device terminal: header column = terminal and device string, second column = device name const db::NetTerminalRef *ref = net_terminalref_from_id (id); if (ref && ref->terminal_def ()) { - return tl::to_qstring (ref->terminal_def ()->name ()); + if (index.column () == 0) { + return tl::to_qstring (ref->terminal_def ()->name () + " - " + device_string (ref->device ())); + } else { + return tl::to_qstring (ref->device ()->expanded_name ()); + } } } else if (is_id_circuit_net_device_terminal_others (id)) { + // circuit/net/device terminal/more: header column = terminal name, second column = net link const db::NetTerminalRef *ref = net_terminalref_from_id (id); size_t other_index = circuit_net_device_terminal_other_index_from_id (id); if (ref && ref->device_class () && ref->device_class ()->terminal_definitions ().size () > other_index) { const db::DeviceTerminalDefinition &def = ref->device_class ()->terminal_definitions ()[other_index]; - return tl::to_qstring (def.name ()); + if (index.column () == 0) { + return tl::to_qstring (def.name ()); + } else { + return make_link_to (ref->device ()->net_for_terminal (def.id ())); + } } } @@ -762,6 +857,20 @@ NetlistBrowserModel::index (int row, int column, const QModelIndex &parent) cons return createIndex (row, column, new_id); } +QModelIndex +NetlistBrowserModel::index_from_id (void *id, int column) const +{ + if (is_id_circuit_net (id)) { + db::Circuit *circuit = circuit_from_id (id); + return createIndex (int (circuit->pin_count () + circuit_net_index_from_id (id)), column, make_id_circuit_net (circuit_index_from_id (id), circuit_net_index_from_id (id))); + } + + // TODO: more ... + + return QModelIndex (); +} + + QModelIndex NetlistBrowserModel::parent (const QModelIndex &index) const { @@ -979,6 +1088,38 @@ NetlistBrowserModel::subcircuit_from_id (void *id) const return attr_by_object_and_index (circuit, index, circuit->begin_subcircuits (), circuit->end_subcircuits (), m_subcircuit_by_circuit_and_index); } +template +static size_t index_from_attr (const Attr *attr, const Iter &begin, const Iter &end, std::map &cache) +{ + typename std::map::iterator cc = cache.find (attr); + if (cc != cache.end ()) { + return cc->second; + } + + size_t index = 0; + for (Iter i = begin; i != end; ++i, ++index) { + if (i.operator-> () == attr) { + cache.insert (std::make_pair (i.operator-> (), index)); + return index; + } + } + + tl_assert (false); +} + +size_t +NetlistBrowserModel::circuit_index (const db::Circuit *circuit) const +{ + return index_from_attr (circuit, netlist ()->begin_circuits (), netlist ()->end_circuits (), m_circuit_index_by_object); +} + +size_t +NetlistBrowserModel::net_index (const db::Net *net) const +{ + const db::Circuit *circuit = net->circuit (); + return index_from_attr (net, circuit->begin_nets (), circuit->end_nets (), m_net_index_by_object); +} + // ---------------------------------------------------------------------------------- // NetlistBrowserPage implementation @@ -994,7 +1135,8 @@ NetlistBrowserPage::NetlistBrowserPage (QWidget * /*parent*/) m_marker_dither_pattern (-1), mp_view (0), m_cv_index (0), - mp_plugin_root (0) + mp_plugin_root (0), + m_history_ptr (0) { Ui::NetlistBrowserPage::setupUi (this); @@ -1002,8 +1144,26 @@ NetlistBrowserPage::NetlistBrowserPage (QWidget * /*parent*/) m_show_all_action->setCheckable (true); m_show_all_action->setChecked (m_show_all); + lay::HTMLItemDelegate *delegate; + + delegate = new lay::HTMLItemDelegate (this); + delegate->set_text_margin (2); + delegate->set_anchors_clickable (true); + connect (delegate, SIGNAL (anchor_clicked (const QString &)), this, SLOT (anchor_clicked (const QString &))); + directory_tree->setItemDelegateForColumn (1, delegate); + + delegate = new lay::HTMLItemDelegate (this); + delegate->set_text_margin (2); + delegate->set_plain_text (true); + directory_tree->setItemDelegateForColumn (0, delegate); + connect (m_show_all_action, SIGNAL (triggered ()), this, SLOT (show_all_clicked ())); connect (filter, SIGNAL (textEdited (const QString &)), this, SLOT (filter_changed ())); + connect (forward, SIGNAL (clicked ()), this, SLOT (navigate_forward ())); + connect (backward, SIGNAL (clicked ()), this, SLOT (navigate_back ())); + + forward->setEnabled (false); + backward->setEnabled (false); } NetlistBrowserPage::~NetlistBrowserPage () @@ -1057,6 +1217,85 @@ NetlistBrowserPage::set_max_shape_count (size_t max_shape_count) } } +void +NetlistBrowserPage::anchor_clicked (const QString &a) +{ + QUrl url (a); + QString ids = url.queryItemValue (QString::fromUtf8 ("id")); + if (ids.isEmpty ()) { + return; + } + + void *id = reinterpret_cast (ids.toULongLong ()); + navigate_to (id, true); +} + +void +NetlistBrowserPage::navigate_to (void *id, bool fwd) +{ + NetlistBrowserModel *model = dynamic_cast (directory_tree->model ()); + if (! model) { + return; + } + + QModelIndex index = model->index_from_id (id, 0); + if (! index.isValid ()) { + return; + } + + directory_tree->setCurrentIndex (index); + + add_to_history (id, fwd); +} + +void +NetlistBrowserPage::current_index_changed (const QModelIndex &index) +{ + if (index.isValid ()) { + printf("@@@1\n"); fflush(stdout); + add_to_history (index.internalPointer (), true); + } +} + +void +NetlistBrowserPage::add_to_history (void *id, bool fwd) +{ + if (! fwd) { + if (m_history_ptr > 1) { + --m_history_ptr; + m_history [m_history_ptr - 1] = id; + } + } else if (m_history_ptr >= m_history.size ()) { + m_history.push_back (id); + m_history_ptr = m_history.size (); + } else { + if (m_history [m_history_ptr] != id) { + m_history.erase (m_history.begin () + m_history_ptr + 1, m_history.end ()); + } + m_history [m_history_ptr] = id; + ++m_history_ptr; + } + + backward->setEnabled (m_history_ptr > 1); + forward->setEnabled (m_history_ptr < m_history.size ()); +} + +void +NetlistBrowserPage::navigate_back () +{ + if (m_history_ptr > 1) { + navigate_to (m_history [m_history_ptr - 2], false); + } +} + +void +NetlistBrowserPage::navigate_forward () +{ + if (m_history_ptr < m_history.size ()) { + navigate_to (m_history [m_history_ptr]); + } +} + void NetlistBrowserPage::filter_changed () { @@ -1137,6 +1376,7 @@ NetlistBrowserPage::set_l2ndb (db::LayoutToNetlist *database) NetlistBrowserModel *new_model = new NetlistBrowserModel (directory_tree, database); directory_tree->setModel (new_model); + connect (directory_tree->selectionModel (), SIGNAL (currentChanged (const QModelIndex &, const QModelIndex &)), this, SLOT (current_index_changed (const QModelIndex &))); // @@@ connect (directory_tree->selectionModel (), SIGNAL (selectionChanged (const QItemSelection &, const QItemSelection &)), this, SLOT (directory_selection_changed (const QItemSelection &, const QItemSelection &))); directory_tree->header ()->setSortIndicatorShown (true); diff --git a/src/laybasic/laybasic/layNetlistBrowserPage.h b/src/laybasic/laybasic/layNetlistBrowserPage.h index 8baeb3a3c..fd4505531 100644 --- a/src/laybasic/laybasic/layNetlistBrowserPage.h +++ b/src/laybasic/laybasic/layNetlistBrowserPage.h @@ -78,8 +78,9 @@ public: virtual QModelIndex parent (const QModelIndex &index) const; virtual int rowCount (const QModelIndex &parent) const; -private: + QModelIndex index_from_id (void *id, int column) const; +private: void *make_id_circuit (size_t circuit_index) const; void *make_id_circuit_pin (size_t circuit_index, size_t pin_index) const; void *make_id_circuit_pin_net (size_t circuit_index, size_t pin_index, size_t net_index) const; @@ -128,6 +129,9 @@ private: db::SubCircuit *subcircuit_from_id (void *id) const; QString text (const QModelIndex &index) const; QIcon icon (const QModelIndex &index) const; + size_t circuit_index (const db::Circuit *attr) const; + size_t net_index (const db::Net *attr) const; + QString make_link_to (const db::Net *net) const; db::Netlist *netlist () const { @@ -143,6 +147,8 @@ private: mutable std::map > m_device_by_circuit_and_index; mutable std::map > m_pin_by_circuit_and_index; mutable std::map > m_subcircuit_by_circuit_and_index; + mutable std::map m_circuit_index_by_object; + mutable std::map m_net_index_by_object; }; /** @@ -239,6 +245,10 @@ public: private slots: void show_all_clicked (); void filter_changed (); + void anchor_clicked (const QString &url); + void navigate_back (); + void navigate_forward (); + void current_index_changed (const QModelIndex &index); private: bool m_show_all; @@ -256,6 +266,11 @@ private: unsigned int m_cv_index; lay::PluginRoot *mp_plugin_root; tl::weak_ptr mp_database; + std::vector m_history; + size_t m_history_ptr; + + void add_to_history (void *id, bool fwd); + void navigate_to (void *id, bool forward = true); }; } // namespace lay diff --git a/src/laybasic/laybasic/laybasic.pro b/src/laybasic/laybasic/laybasic.pro index 190d7ff74..752ad5ed2 100644 --- a/src/laybasic/laybasic/laybasic.pro +++ b/src/laybasic/laybasic/laybasic.pro @@ -166,7 +166,8 @@ SOURCES = \ layBackgroundAwareTreeStyle.cc \ layNetlistBrowser.cc \ layNetlistBrowserDialog.cc \ - layNetlistBrowserPage.cc + layNetlistBrowserPage.cc \ + layItemDelegates.cc HEADERS = \ gtf.h \ @@ -255,7 +256,8 @@ HEADERS = \ layBackgroundAwareTreeStyle.h \ layNetlistBrowser.h \ layNetlistBrowserDialog.h \ - layNetlistBrowserPage.h + layNetlistBrowserPage.h \ + layItemDelegates.h INCLUDEPATH += $$TL_INC $$GSI_INC $$DB_INC $$RDB_INC DEPENDPATH += $$TL_INC $$GSI_INC $$DB_INC $$RDB_INC