# HG changeset patch
# User Remi Thebault <remi.thebault@gmail.com>
# Date 1605994675 -3600
#      Sat Nov 21 22:37:55 2020 +0100
# Node ID 3ef25dbd9bfec5ac8c10553df38bed41100b231c
# Parent  872e727a8cbba328da0d167ebdd43943e1bf647f
properties table adds a documentation column

implemented with item_hyperlink_delegate,
a generic solution to efficiently add hyperlinks
in item views.

diff -r 872e727a8cbb -r 3ef25dbd9bfe libgui/src/item-hyperlink-delegate.cc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libgui/src/item-hyperlink-delegate.cc	Sat Nov 21 22:37:55 2020 +0100
@@ -0,0 +1,237 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2013-2020 The Octave Project Developers
+//
+// See the file COPYRIGHT.md in the top-level directory of this
+// distribution or <https://octave.org/copyright/>.
+//
+// This file is part of Octave.
+//
+// Octave 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 3 of the License, or
+// (at your option) any later version.
+//
+// Octave 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 Octave; see the file COPYING.  If not, see
+// <https://www.gnu.org/licenses/>.
+//
+////////////////////////////////////////////////////////////////////////
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include "item-hyperlink-delegate.h"
+
+#include <QAbstractItemModel>
+#include <QAbstractItemView>
+#include <QApplication>
+#include <QDesktopServices>
+#include <QFont>
+#include <QMouseEvent>
+#include <QPainter>
+#include <QSet>
+#include <QStyle>
+
+namespace octave
+{
+
+  constexpr int default_alignment = Qt::AlignCenter;
+
+  static const QColor unvisited_color = QColor (Qt::darkBlue);
+  static const QColor visited_color = QColor (Qt::darkMagenta);
+
+  static QSet<QUrl> visited_links;
+
+  static int item_alignment (const QModelIndex &index)
+  {
+    const auto model_alignment = index.data (Qt::TextAlignmentRole);
+    return model_alignment.isValid () ? model_alignment.toInt ()
+                                      : default_alignment;
+  }
+
+  item_hyperlink_delegate::item_hyperlink_delegate (QObject *parent,
+                                                    QAbstractItemView *view)
+      : QObject{ parent }, m_view{ view }, m_font_metrics{ QFont{} }
+  {
+  }
+
+  item_hyperlink_delegate::~item_hyperlink_delegate () = default;
+
+  void item_hyperlink_delegate::mouse_move_event (QMouseEvent *ev)
+  {
+    const auto *m = m_view->model ();
+    if (!m)
+      return;
+
+    if (m_clicked_hyperlink && !m_clicked_hl_rect.contains (ev->pos ()))
+      {
+        // handling mouse drag. Dragging out of hyperlink cancels the click
+        m_clicked_hyperlink = hyperlink{};
+        const auto ind = m_clicked_index;
+        m_clicked_index = QModelIndex{};
+        m_clicked_hl_rect = QRect{};
+
+        if (m_cursor_set) {
+          m_view->setCursor (m_old_cursor);
+          m_cursor_set = false;
+        }
+
+        m_view->update (ind);
+        return;
+      }
+
+    const auto index = m_view->indexAt (ev->pos ());
+    if (!index.isValid ())
+      return;
+
+    const auto hl
+        = qvariant_cast<hyperlink> (m->data (index, Qt::DisplayRole));
+    if (hl.isEmpty ())
+      {
+        if (m_cursor_set)
+          {
+            m_view->setCursor (m_old_cursor);
+            m_cursor_set = false;
+          }
+        return;
+      }
+
+    const auto ir = m_view->visualRect (index);
+    const auto alignment = item_alignment (index);
+    const auto br = QApplication::style ()->itemTextRect (
+        m_font_metrics, ir, alignment, true, hl.title ());
+    const auto within = br.contains (ev->pos ());
+
+    if (within && !m_cursor_set)
+      {
+        m_old_cursor = m_view->cursor ();
+        m_view->setCursor (Qt::PointingHandCursor);
+        m_cursor_set = true;
+      }
+    else if (!within && m_cursor_set)
+      {
+        m_view->setCursor (m_old_cursor);
+        m_cursor_set = false;
+      }
+  }
+
+  void item_hyperlink_delegate::mouse_press_event (QMouseEvent *ev)
+  {
+    const auto *m = m_view->model ();
+    if (!m)
+      return;
+
+    const auto index = m_view->indexAt (ev->pos ());
+    if (!index.isValid ())
+      return;
+
+    const auto hl
+        = qvariant_cast<hyperlink> (m->data (index, Qt::DisplayRole));
+    if (hl.isEmpty ())
+      return;
+
+    const auto ir = m_view->visualRect (index);
+    const auto alignment = item_alignment (index);
+    const auto br = QApplication::style ()->itemTextRect (
+        m_font_metrics, ir, alignment, true, hl.title ());
+
+    if (br.contains (ev->pos ()))
+      {
+        m_click_pos = ev->pos ();
+        m_clicked_hyperlink = hl;
+        m_clicked_index = index;
+        m_clicked_hl_rect = br;
+        m_view->update (index);
+      }
+  }
+
+  void item_hyperlink_delegate::mouse_release_event (QMouseEvent *ev)
+  {
+    Q_UNUSED(ev)
+
+    if (m_clicked_hyperlink)
+      {
+        QDesktopServices::openUrl (m_clicked_hyperlink.url ());
+
+        m_clicked_hyperlink = hyperlink{};
+        const auto ind = m_clicked_index;
+        m_clicked_index = QModelIndex{};
+        m_clicked_hl_rect = QRect{};
+
+        m_view->update (ind);
+      }
+  }
+
+  QSize item_hyperlink_delegate::size_hint (const QStyleOptionViewItem &option,
+                                            const QModelIndex &index)
+  {
+    const auto hl = qvariant_cast<hyperlink> (index.data (Qt::DisplayRole));
+
+    if (hl.isEmpty ())
+      return QSize{};
+
+    if (!m_font_metrics_set)
+      {
+        set_font_metrics (option.fontMetrics);
+      }
+
+    return m_font_metrics.boundingRect (hl.title ()).size ();
+  }
+
+  void item_hyperlink_delegate::paint (QPainter *painter,
+                                       const QStyleOptionViewItem &option,
+                                       const QModelIndex &index)
+  {
+    const auto hl = qvariant_cast<hyperlink> (index.data (Qt::DisplayRole));
+
+    if (hl.isEmpty ())
+      return;
+
+    if (!m_font_metrics_set)
+      {
+        set_font_metrics (option.fontMetrics);
+      }
+
+    QFont font = option.font;
+    font.setUnderline (true);
+
+    QColor textColor = unvisited_color;
+    if (option.state.testFlag (QStyle::State_Selected))
+      {
+        // hard-code the color only if not highlighted
+        // (otherwise we can have blue over blue)
+        textColor = option.palette.color (QPalette::HighlightedText);
+      }
+    else if (visited_links.find (hl.url ()) != visited_links.end ())
+      {
+        textColor = visited_color;
+      }
+
+    const auto alignment = item_alignment (index);
+
+    auto rect = option.rect;
+
+    // animate the click
+    if (m_clicked_hyperlink)
+      {
+        rect.translate (QPoint (1, 2));
+      }
+
+    painter->save ();
+
+    painter->setFont (font);
+    painter->setPen (QPen (textColor, painter->pen ().widthF ()));
+
+    painter->drawText (option.rect, alignment, hl.title ());
+
+    painter->restore ();
+  }
+
+}
diff -r 872e727a8cbb -r 3ef25dbd9bfe libgui/src/item-hyperlink-delegate.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libgui/src/item-hyperlink-delegate.h	Sat Nov 21 22:37:55 2020 +0100
@@ -0,0 +1,147 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2013-2020 The Octave Project Developers
+//
+// See the file COPYRIGHT.md in the top-level directory of this
+// distribution or <https://octave.org/copyright/>.
+//
+// This file is part of Octave.
+//
+// Octave 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 3 of the License, or
+// (at your option) any later version.
+//
+// Octave 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 Octave; see the file COPYING.  If not, see
+// <https://www.gnu.org/licenses/>.
+//
+////////////////////////////////////////////////////////////////////////
+
+#if !defined(octave_item_hyperlink_delegate_h)
+#define octave_item_hyperlink_delegate_h 1
+
+#include <QCursor>
+#include <QFontMetrics>
+#include <QModelIndex>
+#include <QObject>
+#include <QRect>
+#include <QString>
+#include <QStyleOptionViewItem>
+#include <QUrl>
+#include <QVariant>
+#include <Qt>
+
+class QAbstractItemView;
+class QMouseEvent;
+class QObject;
+class QPainter;
+
+namespace octave
+{
+  //! Describes an hyperlink, that is a url with a title
+  class hyperlink
+  {
+  public:
+    hyperlink (const QUrl &url, const QString &title)
+        : m_url{ url }, m_title{ title }
+    {
+    }
+    hyperlink (const QString &url, const QString &title)
+        : m_url{ url }, m_title{ title }
+    {
+    }
+
+    hyperlink () = default;
+    ~hyperlink () = default;
+    hyperlink (const hyperlink &) = default;
+    hyperlink &operator= (const hyperlink &) = default;
+
+    operator bool () const { return !isEmpty (); }
+
+    bool isEmpty () const { return m_url.isEmpty () || m_title.isEmpty (); }
+
+    QUrl url () const { return m_url; }
+    QString title () const { return m_title; }
+
+  private:
+    QUrl m_url;
+    QString m_title;
+  };
+
+  //! This class allows to easily setup hyperlinks in item
+  //! views (such as QTreeView or QTableView).
+  //!
+  //! There is no additional widget instanciated, so it is much more efficient
+  //! than having a QLabel in each cell.
+  //!
+  //! The usage requirements are as follow:
+  //! 1. The view must forward its mouse events to this delegate
+  //!    (and have mouse tracking set)
+  //! 2. For all indices with hyperlink, the model must return an hyperlink
+  //!    object in data() when role is Qt::DisplayRole.
+  //! 3. For all indices with hyperlink, the item delegate must forward
+  //!    sizeHint to this delegate
+  //! 4. For all indices with hyperlink, the item delegate must forward paint
+  //!    to this delegate
+  class item_hyperlink_delegate : public QObject
+  {
+    Q_OBJECT
+
+  public:
+    item_hyperlink_delegate (QObject *parent, QAbstractItemView *view);
+    virtual ~item_hyperlink_delegate ();
+
+    //! The QFontMetrics used by this delegate to paint and compute
+    //! bounding rects
+    QFontMetrics font_metrics () const { return m_font_metrics; }
+
+    //! Set the QFontMetrics used by this delegate.
+    //! Calling this method is entirely optional.
+    //! The delegate will use the style option item passed to paint
+    //! if this method is not called.
+    void set_font_metrics (const QFontMetrics &fm)
+    {
+      m_font_metrics = fm;
+      m_font_metrics_set = true;
+    }
+
+    //! View must call this method to forward mouse move event
+    //! (mouse tracking must be enabled in the view)
+    void mouse_move_event (QMouseEvent *ev);
+    //! View must call this method to forward mouse press event
+    void mouse_press_event (QMouseEvent *ev);
+    //! View must call this method to forward mouse release event
+    void mouse_release_event (QMouseEvent *ev);
+
+    //! View delegate must call this method to estimate size of the hyperlink.
+    QSize size_hint (const QStyleOptionViewItem &option,
+                     const QModelIndex &index);
+
+    //! View delegate must call this method to paint the hyperlink.
+    //! The hyperlink will be painted different color if link was visited.
+    void paint (QPainter *painter, const QStyleOptionViewItem &option,
+                const QModelIndex &index);
+
+  private:
+    QAbstractItemView *m_view = nullptr;
+    QCursor m_old_cursor;
+    bool m_cursor_set = false;
+    QFontMetrics m_font_metrics;
+    bool m_font_metrics_set = false;
+    QPoint m_click_pos;
+    hyperlink m_clicked_hyperlink;
+    QModelIndex m_clicked_index;
+    QRect m_clicked_hl_rect; // to test dragging out easily
+  };
+
+}
+
+Q_DECLARE_METATYPE (octave::hyperlink);
+
+#endif // octave_item_hyperlink_delegate_h
diff -r 872e727a8cbb -r 3ef25dbd9bfe libgui/src/module.mk
--- a/libgui/src/module.mk	Fri Nov 20 21:40:02 2020 +0100
+++ b/libgui/src/module.mk	Sat Nov 21 22:37:55 2020 +0100
@@ -140,6 +140,7 @@ OCTAVE_GUI_SRC_MOC = \
   %reldir%/moc-gui-settings.cc \
   %reldir%/moc-history-dock-widget.cc \
   %reldir%/moc-interpreter-qobject.cc \
+  %reldir%/moc-item-hyperlink-delegate.cc \
   %reldir%/moc-main-window.cc \
   %reldir%/moc-news-reader.cc \
   %reldir%/moc-octave-qobject.cc \
@@ -214,6 +215,7 @@ noinst_HEADERS += \
   %reldir%/graphics-init.h \
   %reldir%/history-dock-widget.h \
   %reldir%/interpreter-qobject.h \
+  %reldir%/item-hyperlink-delegate.h \
   %reldir%/m-editor/file-editor-interface.h \
   %reldir%/m-editor/file-editor-tab.h \
   %reldir%/m-editor/file-editor.h \
@@ -258,6 +260,7 @@ noinst_HEADERS += \
   %reldir%/gui-settings.cc \
   %reldir%/history-dock-widget.cc \
   %reldir%/interpreter-qobject.cc \
+  %reldir%/item-hyperlink-delegate.cc \
   %reldir%/m-editor/file-editor-tab.cc \
   %reldir%/m-editor/file-editor.cc \
   %reldir%/m-editor/find-dialog.cc \
diff -r 872e727a8cbb -r 3ef25dbd9bfe libgui/src/property-inspector-table.cc
--- a/libgui/src/property-inspector-table.cc	Fri Nov 20 21:40:02 2020 +0100
+++ b/libgui/src/property-inspector-table.cc	Sat Nov 21 22:37:55 2020 +0100
@@ -52,6 +52,7 @@
 #include "octave-qobject.h"
 #include "qt-application.h"
 
+#include "item-hyperlink-delegate.h"
 #include "property-inspector-table.h"
 #include "ui-property-inspector-color-editor.h"
 
@@ -63,6 +64,7 @@ namespace octave
     // COL_NAME,
     COL_VALUE,
     COL_TYPE,
+    COL_DOCLINK,
 
     COL_COUNT,
   };
@@ -74,6 +76,59 @@ namespace octave
   constexpr int matrix_edit_row_limit = 4;
   constexpr int matrix_edit_col_limit = 4;
 
+  go_properties_table_view::go_properties_table_view (QWidget *parent)
+      : QTableView (parent)
+  {
+    setMouseTracking (true);
+  }
+
+  go_properties_table_view::~go_properties_table_view () = default;
+
+  void go_properties_table_view::mouseMoveEvent (QMouseEvent *ev)
+  {
+    if (m_hl_dg)
+      {
+        m_hl_dg->mouse_move_event (ev);
+      }
+    QTableView::mouseMoveEvent (ev);
+  }
+
+  void go_properties_table_view::mousePressEvent (QMouseEvent *ev)
+  {
+    if (m_hl_dg)
+      {
+        m_hl_dg->mouse_press_event (ev);
+      }
+    QTableView::mousePressEvent (ev);
+  }
+
+  void go_properties_table_view::mouseReleaseEvent (QMouseEvent *ev)
+  {
+    if (m_hl_dg)
+      {
+        m_hl_dg->mouse_release_event (ev);
+      }
+    QTableView::mouseReleaseEvent (ev);
+  }
+
+  //! turn "figure" or "FIGURE" into "Figure"
+  inline QString titlecased (const QString &input)
+  {
+    if (input.size () <= 1)
+      return input;
+    return input[0].toUpper () + input.right (input.size () - 1).toLower ();
+  }
+
+  inline QUrl go_property_doc_url (const QString &obj_type,
+                                   const QString &prop_name,
+                                   const QString &octave_version = "latest")
+  {
+    return QUrl (
+        QString ("https://octave.org/doc/%1/%2-Properties.html#XREF%3%4")
+            .arg (octave_version, titlecased (obj_type), obj_type.toLower (),
+                  prop_name.toLower ()));
+  }
+
   template <class prop_type>
   inline QString property_string_value (const base_property *prop)
   {
@@ -367,11 +422,22 @@ namespace octave
                 return tr ("Value");
               case COL_TYPE:
                 return tr ("Type");
+              case COL_DOCLINK:
+                return tr ("Doc");
               default:
                 return QVariant{};
               }
           case Qt::TextAlignmentRole:
-            return QVariant (Qt::AlignVCenter | Qt::AlignLeft);
+            switch (section)
+              {
+              case COL_DOCLINK:
+                return QVariant (Qt::AlignCenter);
+              // case COL_NAME:
+              case COL_VALUE:
+              case COL_TYPE:
+              default:
+                return QVariant (Qt::AlignVCenter | Qt::AlignLeft);
+              }
           }
       }
 
@@ -437,11 +503,17 @@ namespace octave
             return QVariant (Qt::AlignVCenter | Qt::AlignLeft);
           case COL_TYPE:
             return QVariant (Qt::AlignVCenter | Qt::AlignLeft);
+          case COL_DOCLINK:
+            return QVariant (Qt::AlignCenter);
           default:
             return QVariant{};
           }
       }
 
+    if (role == Qt::ToolTipRole && index.column () == COL_DOCLINK)
+      return go_property_doc_url (QString::fromStdString (m_go.type ()),
+                                  QString::fromStdString (prop->get_name ()));
+
     if (role != Qt::DisplayRole && role != Qt::EditRole)
       return QVariant{};
 
@@ -453,6 +525,15 @@ namespace octave
         return property_display_data (prop, m_oct_qobj);
       case COL_TYPE:
         return property_type_string (prop);
+      case COL_DOCLINK:
+        {
+          QVariant val;
+          val.setValue (hyperlink (
+              go_property_doc_url (QString::fromStdString (m_go.type ()),
+                                   QString::fromStdString (prop->get_name ())),
+              "?"));
+          return val;
+        }
       default:
         return QVariant{};
       }
@@ -515,8 +596,10 @@ namespace octave
   }
 
   go_properties_item_delegate::go_properties_item_delegate (
-      QObject *p, base_qobject &oct_qobj, QTableView *view)
-      : QStyledItemDelegate (p), m_oct_qobj (oct_qobj), m_view{ view }
+      QObject *p, base_qobject &oct_qobj, QTableView *view,
+      item_hyperlink_delegate *hl_dg)
+      : QStyledItemDelegate (p),
+        m_oct_qobj (oct_qobj), m_view{ view }, m_hl_dg{ hl_dg }
   {
     QObject::connect (this, &go_properties_item_delegate::layoutWillChange,
                       m_view, &QTableView::resizeRowToContents,
@@ -651,6 +734,10 @@ namespace octave
             return;
           }
       }
+    else if (index.column () == COL_DOCLINK)
+      {
+        return m_hl_dg->paint (painter, option, index);
+      }
     return QStyledItemDelegate::paint (painter, option, index);
   }
 
@@ -704,6 +791,11 @@ namespace octave
                 std::max (static_cast<int> (maxH * matrix.rows ()), minRowH));
           }
       }
+    else if (index.column () == COL_DOCLINK)
+      {
+        return m_hl_dg->size_hint (option, index);
+      }
+
     auto sz = QStyledItemDelegate::sizeHint (option, index);
     sz.setHeight (std::max (sz.height (), minRowH));
     return sz;
@@ -1370,7 +1462,7 @@ namespace octave
           }
       }
 
-    const auto sz = ui->rgbIcon->size() - QSize(8, 8);
+    const auto sz = ui->rgbIcon->size () - QSize (8, 8);
     auto pixmap = color_pixmap (color, sz);
     ui->rgbIcon->setPixmap (pixmap);
   }
@@ -1405,7 +1497,7 @@ namespace octave
     auto *edit = ui->svgBox->lineEdit ();
     auto palette = edit->palette ();
     // discriminate #RGB web code (only accept #RRGGBB)
-    if (!col.isValid () || (value.startsWith('#') && value.size() != 7))
+    if (!col.isValid () || (value.startsWith ('#') && value.size () != 7))
       {
         palette.setColor (QPalette::Text, QColor ("red"));
       }
diff -r 872e727a8cbb -r 3ef25dbd9bfe libgui/src/property-inspector-table.h
--- a/libgui/src/property-inspector-table.h	Fri Nov 20 21:40:02 2020 +0100
+++ b/libgui/src/property-inspector-table.h	Sat Nov 21 22:37:55 2020 +0100
@@ -30,6 +30,7 @@
 
 #include <QAbstractTableModel>
 #include <QStyledItemDelegate>
+#include <QTableView>
 #include <QWidget>
 
 #include "Matrix.h"
@@ -47,6 +48,25 @@ class Ui_color_editor_widget;
 namespace octave
 {
   class base_qobject;
+  class item_hyperlink_delegate;
+
+  class go_properties_table_view : public QTableView
+  {
+    Q_OBJECT
+  public:
+    go_properties_table_view (QWidget *parent);
+    virtual ~go_properties_table_view ();
+
+    void set_hl_delegate (item_hyperlink_delegate *hl_dg) { m_hl_dg = hl_dg; }
+
+  protected:
+    void mouseMoveEvent (QMouseEvent *ev) override;
+    void mousePressEvent (QMouseEvent *ev) override;
+    void mouseReleaseEvent (QMouseEvent *ev) override;
+
+  private:
+    item_hyperlink_delegate *m_hl_dg = nullptr;
+  };
 
   class go_properties_table_model : public QAbstractTableModel
   {
@@ -86,7 +106,8 @@ namespace octave
     Q_OBJECT
   public:
     go_properties_item_delegate (QObject *parent, base_qobject &oct_qobj,
-                                 QTableView *view);
+                                 QTableView *view,
+                                 item_hyperlink_delegate *hl_dg);
     ~go_properties_item_delegate () = default;
 
     // painting
@@ -121,6 +142,7 @@ namespace octave
 
     base_qobject &m_oct_qobj;
     QTableView *m_view;
+    item_hyperlink_delegate *m_hl_dg;
     mutable QSize m_former_size;
   };
 
diff -r 872e727a8cbb -r 3ef25dbd9bfe libgui/src/property-inspector.cc
--- a/libgui/src/property-inspector.cc	Fri Nov 20 21:40:02 2020 +0100
+++ b/libgui/src/property-inspector.cc	Sat Nov 21 22:37:55 2020 +0100
@@ -32,6 +32,7 @@
 #include <QTableView>
 #include <QTreeView>
 
+#include "item-hyperlink-delegate.h"
 #include "octave-qobject.h"
 #include "property-inspector-table.h"
 #include "property-inspector-tree.h"
@@ -44,8 +45,9 @@ namespace octave
   property_inspector::property_inspector (QWidget *p, base_qobject &oct_qobj)
       : octave_dock_widget ("PropertyInspector", p, oct_qobj),
         m_splitter (new QSplitter (Qt::Horizontal, this)),
-        m_tree (new QTreeView (this)), m_table (new QTableView (this)),
-        m_tree_model (nullptr), m_table_model (nullptr)
+        m_tree (new QTreeView (this)),
+        m_table (new go_properties_table_view (this)), m_tree_model (nullptr),
+        m_table_model (nullptr)
   {
     set_title (tr ("Property Inspector"));
     setStatusTip (tr ("Inspect properties."));
@@ -60,8 +62,10 @@ namespace octave
 
     setWidget (m_splitter);
 
-    m_table->setItemDelegate (
-        new go_properties_item_delegate (this, m_octave_qobj, m_table));
+    auto *table_hl_dg = new item_hyperlink_delegate (this, m_table);
+    m_table->set_hl_delegate (table_hl_dg);
+    m_table->setItemDelegate (new go_properties_item_delegate (
+        this, m_octave_qobj, m_table, table_hl_dg));
 
     if (oct_qobj.app_context ().interpreter_initialized ())
       {
diff -r 872e727a8cbb -r 3ef25dbd9bfe libgui/src/property-inspector.h
--- a/libgui/src/property-inspector.h	Fri Nov 20 21:40:02 2020 +0100
+++ b/libgui/src/property-inspector.h	Sat Nov 21 22:37:55 2020 +0100
@@ -32,7 +32,6 @@
 
 class QSplitter;
 class QTreeView;
-class QTableView;
 
 namespace octave
 {
@@ -40,6 +39,7 @@ namespace octave
 
   class go_tree_model;
   class go_properties_table_model;
+  class go_properties_table_view;
 
   class property_inspector : public octave_dock_widget
   {
@@ -67,7 +67,7 @@ namespace octave
   private:
     QSplitter *m_splitter;
     QTreeView *m_tree;
-    QTableView *m_table;
+    go_properties_table_view *m_table;
 
     go_tree_model *m_tree_model;
     std::unique_ptr<go_properties_table_model> m_table_model;
# HG changeset patch
# User Remi Thebault <remi.thebault@gmail.com>
# Date 1605994692 -3600
#      Sat Nov 21 22:38:12 2020 +0100
# Node ID 039110445042a93e96987f2e26bd2f8c4467ca64
# Parent  3ef25dbd9bfec5ac8c10553df38bed41100b231c
fix inclusion mistake

diff -r 3ef25dbd9bfe -r 039110445042 libgui/src/property-inspector-tree.h
--- a/libgui/src/property-inspector-tree.h	Sat Nov 21 22:37:55 2020 +0100
+++ b/libgui/src/property-inspector-tree.h	Sat Nov 21 22:38:12 2020 +0100
@@ -28,7 +28,7 @@
 
 #include <memory>
 
-#include <QAbstractTableModel>
+#include <QAbstractItemModel>
 
 #include "graphics-handle.h"
 
