# HG changeset patch
# User Andrew Thornton <art27@cantab.net>
# Date 1471638095 -3600
#      Fri Aug 19 21:21:35 2016 +0100
# Node ID 006fe5e79583c4e14c0884c102ecaf2a21b1a71b
# Parent  046ade63abc408b9051cc35afc7c26ef1ef2bd92
Implement Uitable

* doc/interpreter/genpropdoc.m: Add documentation stubs
* doc/interpreter/gui.txi: Wire in uitable documentation
* doc/interpreter/module.mk: Wire in uitable documentation
* doc/interpreter/plot.txi: Wire in uitable documentation
* libgui/graphics/__init_qt__.cc: Add default foregroundcolor for uitable
* libgui/graphics/Backend.cc: Wire in uitable
* libgui/graphics/Canvas.cc: Wire in uitable
* libgui/graphics/module.mk: Add uitable to the build system
* libgui/graphics/ObjectFactory.cc: Add uitable to the ObjectFactory
* libgui/graphics/Table.cc: The main QT object representing a uitable
* libgui/graphics/Table.h: The main QT object representing a uitable
* libgui/graphics/QtHandlesUtils.cc: Add computeFont for uitable
* libinterp/corefcn/gl-render.cc: Wire in uitable
* libinterp/corefcn/graphics.cc: Add default positions and background color. Wire in uitable, and add properties for uitable.
* libinterp/corefcn/graphics.in.h: Add uitable properties object
* libinterp/corefcn/module.mk: Wire in str2double.h
* libinterp/corefcn/pr-output.cc: Make rational_approx non-static - needed for columnformat in uitable
* libinterp/corefcn/pr-output.h: Expose rational_approx in pr-output.h
* libinterp/corefcn/str2double.cc: Expose str2double1 as str2double_internal - needed to interpret data edits
* libinterp/corefcn/str2double.h: Expose str2double_internal
* libinterp/genprops.awk: rename internal value data as _data to avoid collisions with data in uitable
* scripts/gui/module.mk: Wire in uitable.m
* scripts/gui/uitable.m: uitable script

diff -r 046ade63abc4 -r 006fe5e79583 NEWS
--- a/NEWS	Fri Aug 19 21:05:48 2016 +0200
+++ b/NEWS	Fri Aug 19 21:21:35 2016 +0100
@@ -147,6 +147,7 @@
       reducevolume
       smooth3
       uibuttongroup
+      uitable
 
  ** Deprecated functions.
 
diff -r 046ade63abc4 -r 006fe5e79583 doc/interpreter/genpropdoc.m
--- a/doc/interpreter/genpropdoc.m	Fri Aug 19 21:05:48 2016 +0200
+++ b/doc/interpreter/genpropdoc.m	Fri Aug 19 21:21:35 2016 +0100
@@ -32,7 +32,8 @@
   objnames = {"root", "figure", "axes", "line", ...
               "text", "image", "patch", "surface", "light", ...
               "uimenu", "uibuttongroup", "uicontextmenu", "uipanel", ...
-              "uicontrol", "uitoolbar", "uipushtool", "uitoggletool"};
+              "uicontrol", "uitable", "uitoolbar", "uipushtool", ...
+              "uitoggletool"};
 
   ## Base properties
   base = getstructure ("base");
@@ -1426,6 +1427,41 @@
 
     endswitch
 
+  ## uitable Properties
+  elseif (strcmp (objname, "uitable"))
+      switch (field)
+        ## Overridden shared properties
+
+        ## Specific properties
+        case "backgroundcolor"
+        case "celleditcallback"
+        case "cellselectioncallback"
+        case "columneditable"
+        case "columnformat"
+        case "columnname"
+        case "columnwidth"
+        case "createfcn"
+        case "data"
+        case "deletefcn"
+        case "enable"
+        case "extent"
+        case "fontangle"
+        case "fontname"
+        case "fontsize"
+        case "fontunits"
+        case "fontweight"
+        case "foregroundcolor"
+        case "keypressfcn"
+        case "keyreleasefcn"
+        case "position"
+        case "rearrangeablecolumns"
+        case "rowname"
+        case "rowstriping"
+        case "tooltipstring"
+        case "units"
+
+      endswitch
+
   ## uitoolbar properties
   elseif (strcmp (objname, "uitoolbar"))
     switch (field)
diff -r 046ade63abc4 -r 006fe5e79583 doc/interpreter/gui.txi
--- a/doc/interpreter/gui.txi	Fri Aug 19 21:05:48 2016 +0200
+++ b/doc/interpreter/gui.txi	Fri Aug 19 21:21:35 2016 +0100
@@ -113,6 +113,8 @@
 
 @DOCSTRING(uipanel)
 
+@DOCSTRING(uitable)
+
 @DOCSTRING(uipushtool)
 
 @DOCSTRING(uitoggletool)
diff -r 046ade63abc4 -r 006fe5e79583 doc/interpreter/module.mk
--- a/doc/interpreter/module.mk	Fri Aug 19 21:05:48 2016 +0200
+++ b/doc/interpreter/module.mk	Fri Aug 19 21:21:35 2016 +0100
@@ -15,6 +15,7 @@
   doc/interpreter/plot-uicontextmenuproperties.texi \
   doc/interpreter/plot-uipanelproperties.texi \
   doc/interpreter/plot-uicontrolproperties.texi \
+  doc/interpreter/plot-uitableproperties.texi \
   doc/interpreter/plot-uitoolbarproperties.texi \
   doc/interpreter/plot-uipushtoolproperties.texi \
   doc/interpreter/plot-uitoggletoolproperties.texi
@@ -69,6 +70,9 @@
 doc/interpreter/plot-uicontrolproperties.texi: doc/interpreter/genpropdoc.m
 	$(AM_V_GEN)$(call gen-propdoc-texi,uicontrol)
 
+doc/interpreter/plot-uitableproperties.texi: doc/interpreter/genpropdoc.m
+	$(AM_V_GEN)$(call gen-propdoc-texi,uitable)
+
 doc/interpreter/plot-uitoolbarproperties.texi: doc/interpreter/genpropdoc.m
 	$(AM_V_GEN)$(call gen-propdoc-texi,uitoolbar)
 
diff -r 046ade63abc4 -r 006fe5e79583 doc/interpreter/plot.txi
--- a/doc/interpreter/plot.txi	Fri Aug 19 21:05:48 2016 +0200
+++ b/doc/interpreter/plot.txi	Fri Aug 19 21:21:35 2016 +0100
@@ -1406,6 +1406,7 @@
 * Uicontextmenu Properties::
 * Uipanel Properties::
 * Uicontrol Properties::
+* Uitable Properties::
 * Uitoolbar Properties::
 * Uipushtool Properties::
 * Uitoggletool Properties::
@@ -1544,6 +1545,14 @@
 
 @include plot-uicontrolproperties.texi
 
+@node Uitable Properties
+@subsubsection Uitable Properties
+@cindex uitable properties
+
+The @code{uitable} properties are:
+
+@include plot-uitableproperties.texi
+
 @node Uitoolbar Properties
 @subsubsection Uitoolbar Properties
 @cindex uitoolbar properties
diff -r 046ade63abc4 -r 006fe5e79583 libgui/graphics/Backend.cc
--- a/libgui/graphics/Backend.cc	Fri Aug 19 21:05:48 2016 +0200
+++ b/libgui/graphics/Backend.cc	Fri Aug 19 21:21:35 2016 +0100
@@ -58,6 +58,7 @@
            || go.isa ("uibuttongroup")
            || go.isa ("uimenu")
            || go.isa ("uicontextmenu")
+           || go.isa ("uitable")
            || go.isa ("uitoolbar")
            || go.isa ("uipushtool")
            || go.isa ("uitoggletool"))
@@ -91,6 +92,7 @@
       || go.isa ("uibuttongroup")
       || go.isa ("uimenu")
       || go.isa ("uicontextmenu")
+      || go.isa ("uitable")
       || go.isa ("uitoolbar")
       || go.isa ("uipushtool")
       || go.isa ("uitoggletool"))
@@ -122,6 +124,7 @@
       || pId == uibuttongroup::properties::ID___OBJECT__
       || pId == uimenu::properties::ID___OBJECT__
       || pId == uicontextmenu::properties::ID___OBJECT__
+      || pId == uitable::properties::ID___OBJECT__
       || pId == uitoolbar::properties::ID___OBJECT__
       || pId == uipushtool::properties::ID___OBJECT__
       || pId == uitoggletool::properties::ID___OBJECT__
diff -r 046ade63abc4 -r 006fe5e79583 libgui/graphics/Canvas.cc
--- a/libgui/graphics/Canvas.cc	Fri Aug 19 21:05:48 2016 +0200
+++ b/libgui/graphics/Canvas.cc	Fri Aug 19 21:21:35 2016 +0100
@@ -387,7 +387,7 @@
       if (childObj.isa ("axes"))
         axesList.append (childObj);
       else if (childObj.isa ("uicontrol") || childObj.isa ("uipanel")
-               || childObj.isa ("uibuttongroup"))
+               || childObj.isa ("uibuttongroup") || childObj.isa ("uitable"))
         {
           Matrix bb = childObj.get_properties ().get_boundingbox (false);
           QRectF r (bb(0), bb(1), bb(2), bb(3));
diff -r 046ade63abc4 -r 006fe5e79583 libgui/graphics/ObjectFactory.cc
--- a/libgui/graphics/ObjectFactory.cc	Fri Aug 19 21:05:48 2016 +0200
+++ b/libgui/graphics/ObjectFactory.cc	Fri Aug 19 21:21:35 2016 +0100
@@ -46,6 +46,7 @@
 #include "PushTool.h"
 #include "RadioButtonControl.h"
 #include "SliderControl.h"
+#include "Table.h"
 #include "TextControl.h"
 #include "ToggleButtonControl.h"
 #include "ToggleTool.h"
@@ -128,6 +129,8 @@
                 obj = Menu::create (go);
               else if (go.isa ("uicontextmenu"))
                 obj = ContextMenu::create (go);
+              else if (go.isa ("uitable"))
+                obj = Table::create (go);
               else if (go.isa ("uitoolbar"))
                 obj = ToolBar::create (go);
               else if (go.isa ("uipushtool"))
diff -r 046ade63abc4 -r 006fe5e79583 libgui/graphics/QtHandlesUtils.cc
--- a/libgui/graphics/QtHandlesUtils.cc	Fri Aug 19 21:05:48 2016 +0200
+++ b/libgui/graphics/QtHandlesUtils.cc	Fri Aug 19 21:21:35 2016 +0100
@@ -139,6 +139,9 @@
 
 template QFont computeFont<uibuttongroup> (const uibuttongroup::properties& props,
                                            int height);
+
+template QFont computeFont<uitable> (const uitable::properties& props,
+                                     int height);
 QColor
 fromRgb (const Matrix& rgb)
 {
diff -r 046ade63abc4 -r 006fe5e79583 libgui/graphics/Table.cc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libgui/graphics/Table.cc	Fri Aug 19 21:21:35 2016 +0100
@@ -0,0 +1,1663 @@
+/*
+
+Copyright (C) 2016 Andrew Thornton
+
+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
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#if defined (HAVE_CONFIG_H)
+#  include "config.h"
+#endif
+
+#include <QEvent>
+#include <QFrame>
+#include <QLabel>
+#include <QMouseEvent>
+#include <QTimer>
+
+#include <QCheckBox>
+#include <QComboBox>
+#include <QHeaderView>
+#include <QLineEdit>
+#include <QModelIndexList>
+#include <QString>
+#include <QStringList>
+#include <QTableWidget>
+#include <QTableWidgetItem>
+
+#include "Container.h"
+#include "ContextMenu.h"
+#include "Table.h"
+#include "QtHandlesUtils.h"
+
+#include "oct-stream.h"
+#include "oct-strstrm.h"
+#include "ov-struct.h"
+#include "pr-output.h"
+#include "str2double.h"
+
+namespace QtHandles
+{
+
+static const int AUTO_WIDTH = 75;
+
+#define AUTO_HEIGHT \
+  tp.get_fontsize () * 2 - 1
+
+static QSize realQSizeForTable(QTableWidget *t)
+{
+  int w = t->verticalHeader ()->width () + 4;
+  for (int i = 0; i < t->columnCount (); i++)
+    w += t->columnWidth (i);
+  int h = t->horizontalHeader ()->height () + 4;
+  for (int i = 0; i < t->rowCount (); i++)
+    h += t->rowHeight (i);
+  return QSize(w, h);
+}
+
+#define FORMATNUMBER(type)                                                    \
+  static QString formatNumber (type d,                                        \
+                               char format = 'f',                             \
+                               int precision = 4)                             \
+  {                                                                           \
+    if (format == 'n')                                                        \
+      {                                                                       \
+        if (d == floor (d))                                                   \
+          return QString::number(d, 'g', precision);                          \
+        else if (d <= pow (10, precision - 1) && d > pow (10, 1 - precision)) \
+          return QString::number(d, 'f', precision);                          \
+        else                                                                  \
+          return QString::number(d, 'e', precision);                          \
+      }                                                                       \
+    else if (format == 'F')                                                   \
+      {                                                                       \
+        int exponent = floor (log10 (d) / 3) * 3;                             \
+        d *=  pow (10, -exponent);                                            \
+        return QString::number(d, 'f', precision) + "e" +                     \
+          (exponent < 0 ? "-" : "+") +                                        \
+          QString("%1").arg(abs (exponent), 3, 10, QChar('0'));               \
+      }                                                                       \
+    else if (format == 'E')                                                   \
+      {                                                                       \
+        int exponent = floor (log10 (d) / 3) * 3;                             \
+        d *=  pow (10, -exponent);                                            \
+        return QString::number(d,                                             \
+                               'f',                                           \
+                               precision - floor (log10 (d)) - 1) +           \
+               "e" + (exponent < 0 ? "-" : "+") +                             \
+               QString("%1").arg(abs (exponent), 3, 10, QChar('0'));          \
+      }                                                                       \
+    else                                                                      \
+      return QString::number(d, format, precision);                           \
+  }
+
+FORMATNUMBER(double)
+FORMATNUMBER(float)
+
+#undef FORMATNUMBER
+
+static QString formatComplex (Complex c, char format = 'f', int precision = 4)
+{
+  return formatNumber(c.real(), format, precision) +
+    " + " + formatNumber(c.imag (), format, precision) + "i";
+}
+
+#define FORMAT_VALUE_EXCEPT_RAT(f,l)                      \
+  if (format == "numeric" || format == "short")           \
+    text = formatNumber(value, 'n', f);                   \
+  else if (format == "short f" || format == "shortf")     \
+    text = formatNumber(value, 'f', f);                   \
+  else if (format == "short e" || format == "shorte")     \
+    text = formatNumber(value, 'e', f);                   \
+  else if (format == "short eng" || format == "shorteng") \
+    text = formatNumber(value, 'F', f);                   \
+  else if (format == "short g" || format == "shortg")     \
+    text = formatNumber(value, 'g', f + 1);               \
+  else if (format == "long")                              \
+    text = formatNumber(value, 'n', l);                   \
+  else if (format == "long f" || format == "longf")       \
+    text = formatNumber(value, 'f', l);                   \
+  else if (format == "long e" || format == "longe")       \
+    text = formatNumber(value, 'e', l);                   \
+  else if (format == "long eng" || format == "longeng")   \
+    text = formatNumber(value, 'E', l);                   \
+  else if (format == "long g" || format == "longg")       \
+    text = formatNumber(value, 'g', l + 1);               \
+  else if (format == "bank")                              \
+    text = QString::number(value, 'f', 2);                \
+  else if (format == "+")                                 \
+    if (value > 0)                                        \
+      text = Utils::fromStdString("+");                   \
+    else if (value < 0)                                   \
+      text = Utils::fromStdString("-");                   \
+    else                                                  \
+      text = Utils::fromStdString("");
+
+#define FORMAT_VALUE(f,l)                                            \
+  FORMAT_VALUE_EXCEPT_RAT(f,l)                                       \
+  else if (format == "rat")                                          \
+    text = Utils::fromStdString(rational_approx(double (value), 0)); \
+  else                                                               \
+    {                                                                \
+      text = formatNumber (value, 'n', f);                           \
+      flag = Qt::AlignLeft ;                                         \
+    }
+
+#define FORMAT_UINT_VALUE()                   \
+  text = QString::number(value);              \
+  if (format == "char"  || format == "popup") \
+    flag = Qt::AlignLeft;                     \
+  else if (format == "+")                     \
+    {                                         \
+      if (value > 0)                          \
+        text = Utils::fromStdString("+");     \
+      else                                    \
+        text = Utils::fromStdString("");      \
+    }
+
+
+#define FORMAT_INT_VALUE()                    \
+  text = QString::number(value);              \
+  if (format == "char" || format == "popup")  \
+    flag = Qt::AlignLeft ;                    \
+  else if (format == "+")                     \
+    {                                         \
+      if (value > 0)                          \
+        text = Utils::fromStdString("+");     \
+      else if (value < 0)                     \
+        text = Utils::fromStdString("-");     \
+      else                                    \
+        text = Utils::fromStdString("");      \
+    }
+
+static std::pair <Qt::AlignmentFlag, QString> qStringValueFor (
+  octave_value val, std::string format = "")
+{
+  Qt::AlignmentFlag flag = Qt::AlignRight;
+  QString text;
+  if (val.is_string ())
+    {
+      text = QtHandles::Utils::fromStdString (val.string_value ());
+      flag = Qt::AlignLeft ;
+    }
+  else if (val.is_complex_type ())
+    {
+      // Matlab has mulitple complex types we only have double.
+      Complex c = val.complex_value ();
+      if (format == "short")
+        text = formatComplex(c, 'f', 4);
+      else if (format == "short e" || format == "shorte")
+        text = formatComplex(c, 'e', 4);
+      else if (format == "short eng" || format == "shorteng")
+        text = formatComplex(c, 'E', 4);
+      else if (format == "short g" || format == "shortg")
+        text = formatComplex(c, 'g', 5);
+      else if (format == "long")
+        text = formatComplex(c, 'f', 15);
+      else if (format == "long e" || format == "longe")
+        text = formatComplex(c, 'e', 15);
+      else if (format == "long eng" || format == "longeng")
+        text = formatComplex(c, 'E', 15);
+      else if (format == "long g" || format == "longg")
+        text = formatComplex(c, 'g', 16);
+      else if (format == "bank")
+        text = QString::number(c.real (), 'f', 2);
+      else if (format == "+")
+        {
+          if (c.real () > 0)
+            text = Utils::fromStdString("+");
+          else if (c.real () < 0)
+            text = Utils::fromStdString("-");
+          else
+            text = Utils::fromStdString("");
+        }
+      else if (format == "rat")
+        text = Utils::fromStdString(rational_approx(c.real (), 0)) + " + " +
+          Utils::fromStdString(rational_approx(c.imag (), 0)) + "i";
+      else if (format == "numeric")
+        text = QString::number (c.real (), 'g', 5) + " + "
+          + QString::number (c.imag (), 'g', 5) + "i";
+      else
+        {
+          text = QString::number (c.real (), 'g', 5) + " + "
+            + QString::number (c.imag (), 'g', 5) + "i";
+          flag = Qt::AlignLeft ;
+        }
+    }
+  else if (val.is_double_type () )
+    {
+      double value = val.double_value ();
+      FORMAT_VALUE(4, 15)
+    }
+  else if (val.is_single_type ())
+    {
+      float value = val.float_value ();
+      FORMAT_VALUE(4, 7)
+    }
+  else if (val.is_int8_type ())
+    {
+      short int value = val.short_value ();
+      FORMAT_INT_VALUE()
+    }
+  else if (val.is_uint8_type ())
+    {
+      unsigned short int value = val.ushort_value ();
+      FORMAT_UINT_VALUE()
+    }
+  else if (val.is_int16_type ())
+    {
+      int value = val.int_value ();
+      FORMAT_INT_VALUE()
+    }
+  else if (val.is_uint16_type ())
+    {
+      unsigned int value = val.uint_value ();
+      FORMAT_UINT_VALUE()
+    }
+  else if (val.is_int32_type ())
+    {
+      long int value = val.long_value ();
+      FORMAT_INT_VALUE()
+    }
+  else if (val.is_uint32_type ())
+    {
+      unsigned long int value = val.ulong_value ();
+      FORMAT_UINT_VALUE()
+    }
+  else if (val.is_int64_type ())
+    {
+      int64_t value = val.int64_value ();
+      FORMAT_INT_VALUE()
+    }
+  else if (val.is_uint64_type ())
+    {
+      uint64_t value = val.uint64_value ();
+      FORMAT_UINT_VALUE()
+    }
+  else if (val.is_bool_type ())
+    {
+      bool b = val.bool_value ();
+      if (format == "char" || format == "popup" || format == "")
+        {
+          text = Utils::fromStdString(b ? "true" : "false");
+          flag = Qt::AlignLeft ;
+        }
+      else if (format == "+")
+        {
+          text = Utils::fromStdString(b ? "+" : "");
+          flag = Qt::AlignLeft ;
+        }
+      else
+        text = Utils::fromStdString(b ? "1" : "0");
+    }
+  else
+    {
+      std::stringstream warn_string;
+      warn_string << "Unknown conversion for datatype " << val.class_name ()
+        << " to " << format << " for value " << val.string_value (true);
+      warning (warn_string.str().c_str());
+
+      text = Utils::fromStdString(val.string_value (true));
+    }
+
+  return std::make_pair(flag, text);
+}
+#undef FORMAT_VALUE
+#undef FORMAT_VALUE_EXCEPT_RAT
+#undef FORMAT_UINT_VALUE
+#undef FORMAT_INT_VALUE
+
+static QTableWidgetItem * itemFor(octave_value val, std::string format = "",
+                                  bool enabled = false)
+{
+  QTableWidgetItem* retval  = new QTableWidgetItem ();
+  std::pair <Qt::AlignmentFlag, QString> flag_and_text = qStringValueFor(val, format);
+  retval->setTextAlignment (flag_and_text.first);
+  retval->setText (flag_and_text.second);
+
+  if (enabled)
+    retval->setFlags (retval->flags ()  | Qt::ItemIsEditable);
+  else
+    retval->setFlags (retval->flags ()  & ~Qt::ItemIsEditable);
+
+  return retval;
+}
+
+
+static octave_value
+attempt_type_conversion (const octave_value& ov, const octave_value& old_value)
+{
+  octave_value retval;
+
+  // Define a macro to help with the conversion of strings to integers
+  // FIXME: these will happily integer overflow in the (u)int64 case
+  // - this probably doesn't matter.
+#define SCANF_AND_CONVERT(name,ctype,format)           \
+  else if (old_value.is_ ## name ## _type ())          \
+    {                                                  \
+      ctype val;                                       \
+      int n;                                           \
+      const char* c_str = ov.string_value ().c_str (); \
+      int error = sscanf (c_str, format, &val, &n);    \
+      if (error != 1 || c_str[n])                      \
+        {                                              \
+          val = 0;                                     \
+        }                                              \
+      retval = octave_value ( octave_ ## name (val));  \
+    }
+
+  if (old_value.is_string ())
+    retval = ov;
+  SCANF_AND_CONVERT(int8, int64_t, "%jd %n")
+  SCANF_AND_CONVERT(uint8, uint64_t, "%ju %n")
+  SCANF_AND_CONVERT(int16, int64_t, "%jd %n")
+  SCANF_AND_CONVERT(uint16, uint64_t, "%ju %n")
+  SCANF_AND_CONVERT(int32, int64_t, "%jd %n")
+  SCANF_AND_CONVERT(uint32, uint64_t, "%ju %n")
+  SCANF_AND_CONVERT(int64, int64_t, "%jd %n")
+  SCANF_AND_CONVERT(uint64, uint64_t, "%ju %n")
+  #undef SCANF_AND_CONVERT
+  else if (old_value.is_numeric_type () && !old_value.is_integer_type ())
+    {
+      // Basically need to do str2double
+      Complex complex = str2double_internal (ov.string_value ());
+      if (old_value.is_single_type ())
+        retval = octave_value (FloatComplex (complex));
+      else
+        retval = octave_value (complex);
+    }
+  else if (old_value.is_bool_type ())
+    {
+      // Right: Matlab basically needs this to be true or false, we should
+      // accept 1 too.
+      if (ov.string_value ()  == "true" || ov.string_value () == "1")
+        retval = octave_value (true);
+      else
+        retval = octave_value (false);
+    }
+  else
+    retval = octave_value (str2double_internal (ov.string_value ()));
+  return retval;
+}
+
+QCheckBox *
+Table::checkBoxForLogical (octave_value val, bool enabled = false)
+{
+  QCheckBox* retval = new QCheckBox (m_tableWidget);
+
+  if ((val.is_bool_type () || val.is_bool_scalar ()) && val.bool_value ())
+    retval->setCheckState (Qt::Checked);
+  else
+    retval->setCheckState (Qt::Unchecked);
+
+  retval->setStyleSheet ("margin-left:50%; margin-right:50%;");
+  retval->setAttribute(Qt::WA_TransparentForMouseEvents, true);
+  retval->setFocusPolicy(Qt::NoFocus);
+  retval->setProperty ("Enabled", QVariant (enabled));
+
+  return retval;
+}
+
+Table*
+Table::create (const graphics_object& go)
+{
+  Object* parent = Object::parentObject (go);
+
+  if (parent)
+    {
+      Container* container = parent->innerContainer ();
+
+      if (container)
+        return new Table (go, new QTableWidget (container));
+    }
+
+    return 0;
+}
+
+Table::Table (const graphics_object& go, QTableWidget* tableWidget)
+  : Object (go, tableWidget), m_tableWidget(tableWidget), m_blockUpdates(false)
+{
+  uitable::properties& tp = properties<uitable> ();
+
+  Matrix bb = tp.get_boundingbox (false);
+  m_tableWidget->setObjectName ("UITable");
+  m_tableWidget->setAutoFillBackground (true);
+  m_tableWidget->setGeometry (octave::math::round (bb(0)), octave::math::round (bb(1)),
+                  octave::math::round (bb(2)), octave::math::round (bb(3)));
+  m_tableWidget->setFont (Utils::computeFont<uitable> (tp)) ;
+  m_tableWidget->setSelectionBehavior (QAbstractItemView::SelectItems);
+  updatePalette ();
+  m_keyPressHandlerDefined = ! tp.get_keypressfcn ().is_empty ();
+  m_keyReleaseHandlerDefined = ! tp.get_keyreleasefcn ().is_empty ();
+  updateData ();
+  updateRowname ();
+  updateColumnname ();
+  updateColumnwidth();
+  updateEnable(); // Also does rearrangeableColumns
+  m_tableWidget->setToolTip (Utils::fromStdString (tp.get_tooltipstring ()));
+  m_tableWidget->setVisible (tp.is_visible ());
+  updateExtent ();
+  m_tableWidget->installEventFilter (this);
+
+
+  connect (m_tableWidget, SIGNAL (itemChanged (QTableWidgetItem*)),
+           SLOT (itemChanged (QTableWidgetItem*)));
+  connect (m_tableWidget, SIGNAL (cellClicked (int, int)),
+           SLOT (cellClicked (int, int)));
+  connect (m_tableWidget, SIGNAL (itemSelectionChanged (void)),
+           SLOT (itemSelectionChanged (void)));
+}
+
+Table::~Table (void)
+{
+}
+
+void
+Table::itemSelectionChanged ()
+{
+  if (!(properties<uitable> ().get_cellselectioncallback ().is_empty ()))
+    {
+      QModelIndexList modelIndexList = m_tableWidget->selectionModel()->selectedIndexes ();
+      int length = modelIndexList.size();
+      Matrix indices = Matrix (length, 2);
+      for (int i = 0; i < length; i++)
+        {
+          indices (i, 0) = modelIndexList.value (i).row () + 1;
+          indices (i, 1) = modelIndexList.value (i).column () + 1;
+        }
+      octave_scalar_map eventData;
+      eventData.setfield ("Indices", indices);
+      octave_value cellSelectionCallbackEventObject = octave_value (new octave_struct (eventData));
+      gh_manager::post_callback(m_handle, "cellselectioncallback", cellSelectionCallbackEventObject);
+    }
+}
+
+void
+Table::cellClicked (int row, int col)
+{
+  QCheckBox* checkBox = qobject_cast<QCheckBox*> (
+    m_tableWidget->cellWidget (row, col));
+  if (checkBox && checkBox->property ("Enabled").toBool())
+    checkBoxClicked(row, col, checkBox);
+}
+
+void
+Table::sendCellEditCallback (int row,
+  int col,
+  octave_value old_value,
+  octave_value new_value,
+  octave_value edit_data,
+  octave_value error)
+{
+
+  if (properties<uitable> ().get_celleditcallback ().is_function_handle ())
+    {
+      Matrix indices = Matrix (1, 2);
+      indices (0, 0) = row + 1;
+      indices (0, 1) = col + 1;
+
+      octave_scalar_map eventData;
+      eventData.setfield ("Indices", indices);
+      eventData.setfield ("PreviousData", old_value);
+      eventData.setfield ("NewData", new_value);
+      eventData.setfield ("EditData", edit_data);
+      eventData.setfield ("Error", error);
+
+      octave_value cellEditCallbackEventObject = octave_value (new octave_struct (eventData));
+
+      gh_manager::post_callback (m_handle, "celleditcallback", cellEditCallbackEventObject);
+    }
+  else if (error.string_value ().length () > 0)
+    warning (error.string_value ().c_str ());
+}
+
+void
+Table::comboBoxCurrentIndexChanged (const QString& value)
+{
+  if (m_blockUpdates)
+    return;
+  m_blockUpdates = true;
+  gh_manager::auto_lock lock;
+  uitable::properties& tp = properties<uitable> ();
+  octave_value data = tp.get_data ();
+  bool ok = false;
+
+  QComboBox* comboBox = qobject_cast<QComboBox*> (sender());
+  int row = comboBox->property ("row").toInt ();
+  int col = comboBox->property ("col").toInt ();
+
+  octave_value edit_data = octave_value(Utils::toStdString(value));
+
+  if (row < data.rows () && col < data.columns ())
+    {
+      if (data.is_cell ())
+        {
+          Cell cell = data.cell_value ();
+          octave_value old_data = cell (row, col);
+          if (cell (row, col).is_string ())
+            {
+              cell (row, col) = edit_data;
+              if (edit_data.string_value () != old_data.string_value ())
+                {
+                  gh_manager::post_set (m_handle, "data", octave_value (cell), false);
+                }
+
+              octave_value error = octave_value("");
+              sendCellEditCallback(row,
+                col,
+                old_data,
+                edit_data,
+                edit_data,
+                error);
+              ok = true;
+            }
+          else
+            {
+              cell (row, col) = attempt_type_conversion(edit_data, old_data);
+
+              // Inform the QTableWidget of our change
+              updateData(row, col, cell (row, col), columnformat (col), columneditable (col));
+
+              gh_manager::post_set (m_handle, "data", octave_value (cell), false);
+
+              octave_value error = octave_value("");
+              sendCellEditCallback(row,
+                col,
+                old_data,
+                cell (row, col),
+                edit_data,
+                error);
+              ok = true;
+            }
+        }
+      else
+        {
+          octave_value old_data = data.is_matrix_type ()
+            ? data.fast_elem_extract(row + col * data.columns())
+            : octave_value ();
+          data.fast_elem_insert (row + col * data.columns(),
+                                 attempt_type_conversion(edit_data, old_data));
+
+          // Inform the QTableWidget of our change
+          updateData(row,
+                     col,
+                     data.fast_elem_extract (row + col * data.columns ()),
+                     columnformat (col),
+                     columneditable (col));
+
+          gh_manager::post_set (m_handle, "data", data, false);
+
+          octave_value error = octave_value("");
+          sendCellEditCallback(row,
+            col,
+            old_data,
+            data.fast_elem_extract(row + col * data.columns()),
+            edit_data,
+            error);
+          ok = true;
+        }
+    }
+  else
+    {
+      // Reset the QTableWidgetItem
+      updateData(row, col, octave_value (""), columnformat (col), columneditable (col));
+
+      octave_value error = octave_value("Table data is not editable at this location.");
+      sendCellEditCallback(row,
+        col,
+        octave_value(),
+        octave_value(),
+        edit_data,
+        error);
+    }
+
+  if (!ok)
+    {
+      comboBox->setCurrentIndex (-1);
+      comboBox->setEditable (true);
+      comboBox->setEditText (comboBox->property ("original_value").toString ());
+      comboBox->lineEdit ()->setReadOnly (true);
+    }
+    m_blockUpdates = false;
+}
+
+void
+Table::checkBoxClicked (int row, int col, QCheckBox* checkBox)
+{
+  if (m_blockUpdates)
+    return;
+  m_blockUpdates = true;
+  gh_manager::auto_lock lock;
+
+  uitable::properties& tp = properties<uitable> ();
+  bool new_value = !checkBox->isChecked ();
+
+  octave_value data = tp.get_data ();
+  if (data.is_bool_type ())
+    {
+      // EASY WE JUST CONVERT
+      boolMatrix matrix = data.bool_matrix_value ();
+      if (row < matrix.rows () && col < matrix.columns ())
+        {
+          bool old_value = matrix (row, col);
+          matrix (row, col) = new_value;
+          checkBox->setChecked(new_value);
+          if (new_value != old_value)
+            {
+              gh_manager::post_set (m_handle, "data", octave_value(matrix), false);
+            }
+
+          sendCellEditCallback(row,
+            col,
+            octave_value(old_value),
+            octave_value(new_value),
+            octave_value(new_value),
+            octave_value(""));
+
+        }
+      else
+        {
+          sendCellEditCallback(row,
+            col,
+            octave_value(),
+            octave_value(),
+            octave_value(new_value),
+            octave_value("Table data is not editable at this location."));
+        }
+    }
+  else if (data.is_cell ())
+    {
+      Cell cell = data.cell_value ();
+      if (row < cell.rows () && col < cell.columns ())
+        {
+            if (cell (row, col).is_bool_type ())
+              {
+                bool old_value = cell (row, col).bool_value ();
+                cell (row, col) = octave_value(new_value);
+                checkBox->setChecked(new_value);
+                if (new_value != old_value)
+                  {
+                    gh_manager::post_set (m_handle, "data", octave_value (cell), false);
+                  }
+
+                sendCellEditCallback(row,
+                  col,
+                  octave_value(old_value),
+                  octave_value(new_value),
+                  octave_value(new_value),
+                  octave_value(""));
+              }
+            else
+              {
+                sendCellEditCallback(row,
+                  col,
+                  cell (row, col),
+                  octave_value(),
+                  octave_value(new_value),
+                  octave_value("Cannot convert logical edit to other type."));
+              }
+        }
+      else
+        {
+          sendCellEditCallback(row,
+            col,
+            cell (row, col),
+            octave_value(),
+            octave_value(new_value),
+            octave_value("Table data is not editable at this location."));
+        }
+    }
+  else if (data.is_matrix_type ())
+    {
+      if (row < data.rows () && col < data.columns ())
+        {
+          sendCellEditCallback(row,
+            col,
+            data.fast_elem_extract (row + col * data.rows()),
+            octave_value(),
+            octave_value(new_value),
+            octave_value("Cannot convert logical edit to other type."));
+        }
+      else
+        {
+          sendCellEditCallback(row,
+            col,
+            data.fast_elem_extract (row + col * data.rows()),
+            octave_value(),
+            octave_value(new_value),
+            octave_value("Table data is not editable at this location."));
+        }
+    }
+    m_blockUpdates = false;
+}
+
+
+void
+Table::itemChanged (QTableWidgetItem* item)
+{
+  if (m_blockUpdates)
+    return;
+  m_blockUpdates = true;
+  gh_manager::auto_lock lock;
+  uitable::properties& tp = properties<uitable> ();
+  octave_value data = tp.get_data ();
+
+  int row = item->row ();
+  int col = item->column ();
+  octave_value edit_data = octave_value (Utils::toStdString(item->text ()));
+  octave_value new_value;
+  octave_value old_value;
+  octave_value new_data;
+
+  if (row < data.rows () && col < data.columns ())
+    {
+      if (data.is_cell ())
+        {
+          old_value = data.cell_value () (row, col);
+        }
+      else if (data.is_matrix_type ())
+        {
+          old_value = data.fast_elem_extract (row + col * data.columns ());
+        }
+
+      // Now we need to coerce the new_value in to the type of the old_value
+      if (old_value.is_string ())
+        new_value = edit_data;
+      else
+        {
+          new_value = attempt_type_conversion(edit_data, old_value);
+          std::pair <Qt::AlignmentFlag, QString> flag_and_text =
+            qStringValueFor(new_value, columnformat (col));
+          item->setTextAlignment (flag_and_text.first);
+          item->setText (flag_and_text.second);
+        }
+
+      if (data.is_cell ())
+        {
+          Cell cell = data.cell_value();
+          cell (row, col) = new_value;
+          new_data = octave_value (cell);
+        }
+      else
+        {
+          data.fast_elem_insert (row + col * data.columns (), new_value);
+          new_data = data;
+        }
+        gh_manager::post_set (m_handle, "data", new_data, false);
+
+        sendCellEditCallback(row,
+          col,
+          octave_value(old_value),
+          octave_value(new_value),
+          octave_value(new_value),
+          octave_value(""));
+    }
+  else
+    {
+      item->setText ("");
+
+      octave_value error = octave_value("Table data is not editable at this location.");
+      sendCellEditCallback(row,
+        col,
+        octave_value(),
+        octave_value(),
+        edit_data,
+        error);
+    }
+
+    m_blockUpdates = false;
+}
+
+void
+Table::update (int pId)
+{
+  uitable::properties& tp = properties<uitable> ();
+
+  switch (pId)
+    {
+    case uitable::properties::ID_BACKGROUNDCOLOR:
+    case uitable::properties::ID_FOREGROUNDCOLOR:
+      updatePalette ();
+      break;
+
+    case uitable::properties::ID_COLUMNNAME:
+      updateColumnname ();
+      updateColumnwidth ();
+      break;
+
+    case uitable::properties::ID_COLUMNWIDTH:
+      updateColumnwidth ();
+      break;
+
+    case uitable::properties::ID_COLUMNEDITABLE:
+    case uitable::properties::ID_COLUMNFORMAT:
+    case uitable::properties::ID_DATA:
+      m_blockUpdates = true;
+      updateData ();
+      updateRowname ();
+      updateColumnname ();
+      updateColumnwidth ();
+      updateEnable();
+      m_blockUpdates = false;
+      break;
+
+    case uitable::properties::ID_ENABLE:
+      updateEnable();
+      break;
+
+    case uitable::properties::ID_KEYPRESSFCN:
+      m_keyPressHandlerDefined = ! tp.get_keypressfcn ().is_empty ();
+      break;
+
+    case uitable::properties::ID_KEYRELEASEFCN:
+      m_keyReleaseHandlerDefined = ! tp.get_keyreleasefcn ().is_empty ();
+      break;
+
+      case uitable::properties::ID_FONTNAME:
+      case uitable::properties::ID_FONTSIZE:
+      case uitable::properties::ID_FONTWEIGHT:
+      case uitable::properties::ID_FONTANGLE:
+        if (m_tableWidget)
+          {
+            m_tableWidget->setFont (Utils::computeFont<uitable> (tp));
+            for (int row = 0; row < m_tableWidget->rowCount (); row++)
+              {
+                m_tableWidget->setRowHeight (row, AUTO_HEIGHT);
+              }
+          }
+        break;
+
+    case uitable::properties::ID_POSITION:
+        {
+          Matrix bb = tp.get_boundingbox (false);
+          m_tableWidget->setGeometry (octave::math::round (bb(0)), octave::math::round (bb(1)),
+                                      octave::math::round (bb(2)), octave::math::round (bb(3)));
+          updateExtent();
+        }
+      break;
+
+    case uitable::properties::ID_REARRANGEABLECOLUMNS:
+      updateRearrangeableColumns ();
+      break;
+
+    case uitable::properties::ID_ROWNAME:
+      updateRowname ();
+      break;
+
+    case uitable::properties::ID_ROWSTRIPING:
+      updatePalette();
+      break;
+
+    case uitable::properties::ID_TOOLTIPSTRING:
+      m_tableWidget->setToolTip (Utils::fromStdString (tp.get_tooltipstring ()));
+      break;
+
+    case base_properties::ID_VISIBLE:
+      m_tableWidget->setVisible (tp.is_visible ());
+      break;
+
+    default:
+      break;
+
+    }
+}
+
+void
+Table::updateColumnname (void)
+{
+  uitable::properties& tp = properties<uitable> ();
+
+  // Reset the Column Count
+  m_tableWidget->setColumnCount (tp.get_data ().columns ());
+
+  octave_value columnname = tp.get_columnname ();
+  QStringList l;
+  bool visible = true;
+
+  if (columnname.is_string () && columnname.string_value (false) == "numbered")
+      for (int i = 0; i < m_tableWidget->columnCount (); i++)
+        l << QString::number(i + 1);
+  else if (columnname.is_string ())
+    {
+      if (m_tableWidget->columnCount () > 0)
+        l << Utils::fromStdString(columnname.string_value ());
+      for (int i = 1; i < m_tableWidget->columnCount (); i++)
+        l << "";
+    }
+  else if (columnname.is_empty ())
+    {
+      for (int i = 0; i < m_tableWidget->columnCount (); i++)
+        l << "";
+
+      visible = false;
+    }
+  else if (columnname.is_cell ())
+    {
+      octave_idx_type n = columnname.numel();
+      Cell cell_value = columnname.cell_value();
+
+      for (octave_idx_type i = 0; i < n; i++)
+        {
+          octave_value v = cell_value (i);
+          if (v.is_string ())
+            l << Utils::fromStdString(v.string_value (true));
+          else if (v.is_matrix_type ())
+            {
+              Matrix data = v.matrix_value ();
+
+              /* Now Matlab does something very strange here:
+               * If data is a row or column matrix, then each datapoint is added
+               * Otherwise nothing is set.
+               */
+              if (data.rows () > 1 && data.cols () > 1)
+                l << "";
+              else
+                for (octave_idx_type j = 0; j < data.numel (); j++)
+                  l << QString::number(data (j));
+            }
+          else if (v.is_numeric_type ())
+            l << QString::number(v.double_value ());
+          else
+            l << QString::number(v.double_value ());
+        }
+    }
+  else if (columnname.is_matrix_type ())
+    {
+      octave_idx_type n = columnname.numel ();
+      Matrix matrix_value = columnname.matrix_value ();
+
+      for (octave_idx_type i = 0; i < n; i++)
+        l << QString::number(matrix_value (i));
+    }
+  else
+    {
+      for (int i = 0; i < m_tableWidget->columnCount (); i++)
+        l << "";
+      visible = false;
+    }
+
+  l.replaceInStrings("|", "\n");
+
+  // Now add the columns as required
+  if (m_tableWidget->columnCount () < l.length())
+    {
+      int oldColumnCount = m_tableWidget->columnCount ();
+      m_tableWidget->setColumnCount (l.length());
+      for (int col = oldColumnCount; col < l.length (); col++)
+        {
+          std::string format = columnformat (col);
+          bool enabled = columneditable (col);
+
+          for (int row = 0; row < m_tableWidget->rowCount (); row++)
+            updateData (row, col, octave_value (""), format, enabled);
+        }
+    }
+
+    m_tableWidget->setHorizontalHeaderLabels (l);
+    m_tableWidget->horizontalHeader ()->setVisible (visible);
+}
+
+void
+Table::updateColumnwidth (void)
+{
+  uitable::properties& tp = properties<uitable> ();
+
+  octave_value columnwidth = tp.get_columnwidth ();
+  if (columnwidth.is_empty () ||
+    (columnwidth.is_string () && columnwidth.string_value (false) == "auto"))
+    for (int i = 0; i < m_tableWidget->columnCount (); i++)
+      m_tableWidget->setColumnWidth (i, AUTO_WIDTH);
+  else if (columnwidth.is_string () && columnwidth.string_value (false) == "preferred")
+    for (int i = 0; i < m_tableWidget->columnCount (); i++)
+      {
+        int column_size = (qobject_cast<QAbstractItemView*> (m_tableWidget))->sizeHintForColumn (i);
+        int header_size = m_tableWidget->horizontalHeader ()->sectionSizeHint (i);
+
+        if (column_size > header_size)
+          header_size = column_size;
+        m_tableWidget->setColumnWidth (i, header_size);
+      }
+  else if (columnwidth.is_cell ())
+    {
+      Cell cell_value = columnwidth.cell_value ();
+      int i = 0;
+      for (; i < m_tableWidget->columnCount () && i < cell_value.numel (); i++)
+        {
+          octave_value v = cell_value (i);
+          if (v.is_string ()  && v.string_value (false) == "auto")
+            m_tableWidget->setColumnWidth (i, AUTO_WIDTH);
+          else if (v.is_string () && v.string_value (false) == "preferred")
+            {
+              int column_size =
+                (qobject_cast<QAbstractItemView*> (m_tableWidget))->sizeHintForColumn (i);
+              int header_size = m_tableWidget->horizontalHeader ()->sectionSizeHint (i);
+
+              if (column_size > header_size)
+                header_size = column_size;
+              m_tableWidget->setColumnWidth (i, header_size);
+            }
+          else
+            {
+              int w = int (v.double_value());
+              m_tableWidget->setColumnWidth (i, w);
+            }
+        }
+      for (;i < m_tableWidget->columnCount (); i++)
+        {
+          int column_size =
+            (qobject_cast<QAbstractItemView*> (m_tableWidget))->sizeHintForColumn (i);
+          int header_size = m_tableWidget->horizontalHeader ()->sectionSizeHint (i);
+
+          if (column_size > header_size)
+            header_size = column_size;
+          m_tableWidget->setColumnWidth (i, header_size);
+        }
+    }
+  else if (columnwidth.is_matrix_type ())
+    {
+      Matrix matrix_value = columnwidth.matrix_value ();
+      int i = 0;
+      for (; i < m_tableWidget->columnCount () && i < matrix_value.numel (); i++)
+        {
+          octave_value v = matrix_value (i);
+          int w = int (v.double_value());
+          m_tableWidget->setColumnWidth (i, w);
+        }
+      for (;i < m_tableWidget->columnCount (); i++)
+        m_tableWidget->setColumnWidth (i, AUTO_WIDTH);
+    }
+}
+
+bool inline
+Table::columneditable (int col)
+{
+  uitable::properties& tp = properties<uitable> ();
+  boolNDArray columneditable = tp.get_columneditable ().bool_array_value ();
+  bool editable = false;
+
+  if (!columneditable.is_empty () && col < columneditable.numel ())
+    editable = columneditable.xelem (col);
+  else if (!columneditable.is_empty () && columneditable.numel () == 1)
+    editable = columneditable.xelem (0);
+
+  return editable;
+}
+
+std::string inline
+Table::columnformat (int col)
+{
+  uitable::properties& tp = properties<uitable> ();
+  std::string format = "";
+  octave_value ov_columnformat = tp.get_columnformat ();
+
+  if (ov_columnformat.is_cell ())
+    {
+      Cell columnformat = ov_columnformat.cell_value ();
+      if (!columnformat.is_empty () && col < columnformat.numel ())
+        {
+          octave_value format_value = columnformat.xelem (col);
+
+          if (!format_value.is_empty () && format_value.is_string())
+            format = format_value.string_value ();
+          else if (!format_value.is_empty () && format_value.is_cell ())
+            format = "popup";
+        }
+    }
+  else if (ov_columnformat.is_string ())
+    {
+      format = ov_columnformat.string_value ();
+    }
+  return format;
+}
+
+void inline
+Table::updateDataColumn (int col)
+{
+  octave_value data = properties<uitable> ().get_data ();
+
+  std::string format = columnformat (col);
+  bool is_editable = columneditable (col);
+
+  for (octave_idx_type row = 0; row < data.rows (); row++)
+    updateData(row,
+      col,
+      data.is_cell ()
+        ? data.cell_value () (row, col)
+        : data.fast_elem_extract (row + col * data.rows ()),
+      format,
+      is_editable);
+}
+
+void inline
+Table::updateData (int row, int col)
+{
+  octave_value data = properties<uitable> ().get_data ();
+  updateData (row,
+    col,
+    data.is_cell ()
+      ? data.cell_value () (row, col)
+      : data.fast_elem_extract (row + col * data.rows ()),
+    columnformat (col),
+    columneditable (col));
+}
+
+void inline
+Table::updateData (int row, int col, octave_value value,
+                   std::string format = "", bool enabled = false)
+{
+  if (format == "logical" || (format == "" && value.is_bool_type ()))
+    {
+      if (m_tableWidget->item (row, col))
+        delete m_tableWidget->item (row, col);
+
+      m_tableWidget->setCellWidget (row, col, checkBoxForLogical(value, enabled));
+      m_tableWidget->cellWidget (row, col)->setProperty ("row", QVariant(row));
+      m_tableWidget->cellWidget (row, col)->setProperty ("col", QVariant(col));
+    }
+  else if (format == "popup" && enabled)
+    {
+      if (m_tableWidget->item (row, col))
+        delete m_tableWidget->item (row, col);
+
+      QString string_value = qStringValueFor(value, format).second;
+      uitable::properties& tp = properties<uitable> ();
+      octave_value format_value = tp.get_columnformat ().cell_value ().xelem (col);
+
+      QComboBox* comboBox = new QComboBox ();
+      comboBox->setProperty("row", QVariant (row));
+      comboBox->setProperty("col", QVariant (col));
+
+      int index = -1;
+      for (int k = 0; k < format_value.numel (); k++)
+        {
+          QString popup_item = Utils::fromStdString(
+            format_value.fast_elem_extract (k).string_value ());
+
+          comboBox->addItem (popup_item);
+
+          if (popup_item == string_value)
+            index = k;
+        }
+      comboBox->setCurrentIndex (index);
+
+      if (index < 0)
+        {
+          comboBox->setEditable (true);
+          comboBox->setEditText (string_value);
+          comboBox->lineEdit ()->setReadOnly (true);
+        }
+
+      comboBox->setProperty ("original_value", QVariant (string_value));
+
+      comboBox->installEventFilter (this);
+      m_tableWidget->setCellWidget(row, col, comboBox);
+      connect(comboBox, SIGNAL(currentIndexChanged(const QString&)),
+              this, SLOT(comboBoxCurrentIndexChanged(const QString&)));
+    }
+  else
+    {
+      if (m_tableWidget->cellWidget (row, col))
+        delete m_tableWidget->cellWidget (row, col);
+      m_tableWidget->setItem (row, col, itemFor(value, format, enabled));
+    }
+}
+
+void
+Table::updateData ()
+{
+  uitable::properties& tp = properties<uitable> ();
+
+  octave_value data = tp.get_data();
+
+  if (data.is_cell () || data.is_matrix_type ())
+    {
+      m_tableWidget->setRowCount (data.rows ());
+      m_tableWidget->setColumnCount (data.columns ());
+
+      for (octave_idx_type col = 0; col < data.columns (); col++)
+        updateDataColumn (col);
+    }
+
+  for (octave_idx_type row = 0; row < m_tableWidget->rowCount (); row++)
+    m_tableWidget->setRowHeight (row, AUTO_HEIGHT);
+}
+
+void
+Table::updateEnable (void)
+{
+  uitable::properties& tp = properties<uitable> ();
+  m_tableWidget->setEnabled (!tp.enable_is ("off"));
+
+  bool enabled = tp.enable_is ("on");
+  bool rearrangeableColumns = tp.is_rearrangeablecolumns ();
+
+  // Set selection mode
+  m_tableWidget->setSelectionMode (enabled
+    ? QAbstractItemView::ExtendedSelection
+    : QAbstractItemView::NoSelection);
+
+  // Set rearrangeablecolumns
+  #if defined (HAVE_QT4)
+    m_tableWidget->horizontalHeader ()->setMovable (enabled && rearrangeableColumns);
+  #elif defined (HAVE_QT5)
+    m_tableWidget->horizontalHeader ()->setSectionsMovable (enabled && rearrangeableColumns);
+  #endif
+  m_tableWidget->horizontalHeader ()->setDragEnabled (enabled && rearrangeableColumns);
+  m_tableWidget->horizontalHeader ()->setDragDropMode (QAbstractItemView::InternalMove);
+
+  // Turn off column editable
+  for (int col = 0; col < m_tableWidget->columnCount (); col++)
+    {
+      bool editable = columneditable (col);
+
+      for (int row = 0; row < m_tableWidget->rowCount (); row++)
+        if (QTableWidgetItem* item = m_tableWidget->item (row, col))
+          {
+            Qt::ItemFlags flags = item->flags ();
+            if (enabled && editable)
+              item->setFlags (flags | Qt::ItemIsEditable);
+            else
+              item->setFlags (flags & ~Qt::ItemIsEditable);
+          }
+        else if (QWidget* widget = m_tableWidget->cellWidget (row, col))
+          {
+            if (qobject_cast<QCheckBox*> (widget))
+              {
+                widget->setProperty ("Enabled", QVariant (enabled & editable));
+              }
+            else
+              {
+                widget->setAttribute(Qt::WA_TransparentForMouseEvents, !(editable & enabled));
+                widget->setFocusPolicy((enabled & editable) ? Qt::StrongFocus : Qt::NoFocus);
+              }
+          }
+    }
+}
+
+void
+Table::updateExtent (void)
+{
+  QSize s = realQSizeForTable (m_tableWidget);
+  Matrix extent = Matrix (1, 4);
+  extent(0, 0) = 0;
+  extent(0, 1) = 0;
+  extent(0, 2) = s.width ();
+  extent(0, 3) = s.height () ;
+  graphics_object go = object ();
+  gh_manager::post_set (go.get_handle (), "extent", extent, false);
+}
+
+void
+Table::updatePalette (void)
+{
+  uitable::properties& tp = properties<uitable> ();
+
+  QPalette p = m_tableWidget->palette ();
+  p.setColor (QPalette::Text,
+              Utils::fromRgb (tp.get_foregroundcolor_rgb ()));
+  p.setColor (QPalette::Base,
+            Utils::fromRgb (tp.get_backgroundcolor_rgb ()));
+  p.setColor (QPalette::AlternateBase,
+            Utils::fromRgb (tp.get_alternatebackgroundcolor_rgb ()));
+  m_tableWidget->setPalette (p);
+  m_tableWidget->setAlternatingRowColors (tp.is_rowstriping ());
+  // FIXME: Handle multiple alternating background colors
+}
+
+void
+Table::updateRowname (void)
+{
+  uitable::properties& tp = properties<uitable> ();
+
+  // Reset the row count
+  m_tableWidget->setRowCount (tp.get_data ().rows ());
+
+  octave_value rowname = tp.get_rowname ();
+  QStringList l;
+  bool visible = true;
+
+  if (rowname.is_string () && rowname.string_value (false) == "numbered")
+    for (int i = 0; i < m_tableWidget->rowCount (); i++)
+      l << QString::number(i + 1);
+  else if (rowname.is_string ())
+    {
+      if (m_tableWidget->rowCount () > 0)
+        l << Utils::fromStdString(rowname.string_value ());
+      for (int i = 1; i < m_tableWidget->rowCount (); i++)
+        l << "";
+    }
+  else if (rowname.is_empty ())
+    {
+      for (int i = 0; i < m_tableWidget->rowCount (); i++)
+        l << "";
+      visible = false;
+    }
+  else if (rowname.is_cell ())
+    {
+      octave_idx_type n = rowname.numel();
+      Cell cell_value = rowname.cell_value();
+
+      for (octave_idx_type i = 0; i < n; i++)
+        {
+          octave_value v = cell_value (i);
+          if (v.is_string ())
+            l << Utils::fromStdString(v.string_value (true));
+          else if (v.is_matrix_type ())
+            {
+              Matrix data = v.matrix_value ();
+
+              /* Now Matlab does something very strange here:
+               * If data is a row or column matrix, then each datapoint is added
+               * Otherwise nothing is set.
+               */
+              if (data.rows () > 1 && data.cols () > 1)
+                l << "";
+              else
+                for (octave_idx_type j = 0; j < data.numel (); j++)
+                  l << QString::number(data (j));
+            }
+          else if (v.is_numeric_type ())
+            l << QString::number(v.double_value (true));
+          else
+            l << QString::number(v.double_value (true));
+        }
+    }
+  else if (rowname.is_matrix_type ())
+    {
+      octave_idx_type n = rowname.numel();
+      Matrix matrix_value = rowname.matrix_value();
+
+      for (octave_idx_type i = 0; i < n; i++)
+        l << QString::number(matrix_value (i));
+    }
+  else
+    {
+      for (int i = 0; i < m_tableWidget->columnCount (); i++)
+        l << "";
+      visible = false;
+    }
+
+  // Add dummy rows as required
+  if (m_tableWidget->rowCount () < l.length())
+    {
+      int oldRowCount = m_tableWidget->rowCount ();
+      m_tableWidget->setRowCount (l.length());
+
+      for (int col = 0; col < m_tableWidget->columnCount (); col++)
+        {
+          std::string format = columnformat (col);
+          bool enabled = columneditable (col);
+
+          for (int row = oldRowCount; row < l.length (); row++)
+            {
+              m_tableWidget->setRowHeight (row, AUTO_HEIGHT);
+
+              updateData (row, col, octave_value (""), format, enabled);
+            }
+        }
+    }
+
+  m_tableWidget->setVerticalHeaderLabels (l);
+  m_tableWidget->verticalHeader ()->setVisible (visible);
+}
+
+void
+Table::updateRearrangeableColumns (void)
+{
+  uitable::properties& tp = properties<uitable> ();
+
+  bool rearrangeableColumns = tp.is_rearrangeablecolumns ();
+  bool enabled = tp.enable_is ("on");
+
+#if defined (HAVE_QT4)
+  m_tableWidget->horizontalHeader ()->setMovable (enabled && rearrangeableColumns);
+#elif defined (HAVE_QT5)
+  m_tableWidget->horizontalHeader ()->setSectionsMovable (enabled && rearrangeableColumns);
+#endif
+  m_tableWidget->horizontalHeader ()->setDragEnabled (enabled && rearrangeableColumns);
+  m_tableWidget->horizontalHeader ()->setDragDropMode (QAbstractItemView::InternalMove);
+}
+
+bool
+Table::eventFilter (QObject* watched, QEvent* xevent)
+{
+  //uitable::properties& tp = properties<uitable> ();
+  if (qobject_cast<QTableWidget*> (watched))
+    {
+      switch (xevent->type ())
+        {
+        case QEvent::Resize:
+          {
+            gh_manager::auto_lock lock;
+            graphics_object go = object ();
+            if (go.valid_object ())
+              {
+                const uitable::properties& tp =
+                  Utils::properties<uitable> (go);
+                if (tp.fontunits_is ("normalized"))
+                  m_tableWidget->setFont (Utils::computeFont<uitable> (tp));
+              }
+          }
+          break;
+
+          case QEvent::MouseButtonPress:
+              {
+                gh_manager::auto_lock lock;
+                QMouseEvent* m = dynamic_cast<QMouseEvent*> (xevent);
+                graphics_object go = object ();
+                const uitable::properties& tp =
+                  Utils::properties<uitable> (go);
+                graphics_object fig = go.get_ancestor ("figure");
+
+                if (m->button () != Qt::LeftButton || !tp.enable_is ("on"))
+                  {
+                    gh_manager::post_set (fig.get_handle (), "selectiontype",
+                                          Utils::figureSelectionType (m), false);
+                    gh_manager::post_set (fig.get_handle (), "currentpoint",
+                                          Utils::figureCurrentPoint (fig, m),
+                                          false);
+                    gh_manager::post_callback (fig.get_handle (),
+                                               "windowbuttondownfcn");
+                    gh_manager::post_callback (m_handle, "buttondownfcn");
+
+                    if (m->button () == Qt::RightButton)
+                      ContextMenu::executeAt (properties (), m->globalPos ());
+                  }
+                else
+                  {
+                    gh_manager::post_set (fig.get_handle (), "selectiontype",
+                                         octave_value ("normal"), false);
+                  }
+              }
+            break;
+
+            case QEvent::KeyPress:
+              {
+                QKeyEvent* k = dynamic_cast<QKeyEvent*> (xevent);
+                if (m_keyPressHandlerDefined)
+                  {
+                    gh_manager::auto_lock lock;
+
+                    octave_scalar_map keyData = Utils::makeKeyEventStruct (k);
+                    graphics_object fig = object ().get_ancestor ("figure");
+
+                    gh_manager::post_set (fig.get_handle (), "currentcharacter",
+                                          keyData.getfield ("Character"), false);
+                    gh_manager::post_callback (m_handle, "keypressfcn", keyData);
+                  }
+                switch (k->key())
+                  {
+                    case Qt::Key_Space:
+                    {
+                      int row = m_tableWidget->currentRow ();
+                      int col = m_tableWidget->currentColumn ();
+                      QCheckBox* checkBox = qobject_cast<QCheckBox*> (
+                        m_tableWidget->cellWidget (row, col));
+                      if (checkBox && checkBox->property ("Enabled").toBool())
+                        checkBoxClicked(row, col, checkBox);
+                    }
+                    break;
+
+                    case Qt::Key_Left:
+                    {
+                      int row = m_tableWidget->currentRow ();
+                      int col = m_tableWidget->currentColumn ();
+                      QComboBox* comboBox = qobject_cast<QComboBox*> (
+                        m_tableWidget->cellWidget (row, col));
+                      if (comboBox)
+                        m_tableWidget->setCurrentCell (row,
+                          (col - 1 < 0) ? 0 : col - 1);
+                    }
+                    break;
+
+                    case Qt::Key_Right:
+                    {
+                      int row = m_tableWidget->currentRow ();
+                      int col = m_tableWidget->currentColumn ();
+                      QComboBox* comboBox = qobject_cast<QComboBox*> (
+                        m_tableWidget->cellWidget (row, col));
+                      if (comboBox)
+                        m_tableWidget->setCurrentCell (row,
+                          (col + 1 < m_tableWidget->columnCount())
+                            ? col + 1 : m_tableWidget->columnCount());
+                    }
+                    break;
+
+                    default:
+                    break;
+                  }
+              }
+            break;
+
+            case QEvent::KeyRelease:
+              {
+                if (m_keyReleaseHandlerDefined)
+                  {
+                    gh_manager::auto_lock lock;
+                    QKeyEvent* k = dynamic_cast<QKeyEvent*> (xevent);
+
+                    octave_scalar_map keyData = Utils::makeKeyEventStruct (k);
+                    graphics_object fig = object ().get_ancestor ("figure");
+
+                    gh_manager::post_set (fig.get_handle (), "currentcharacter",
+                                          keyData.getfield ("Character"), false);
+                    gh_manager::post_callback (m_handle, "keyreleasefcn", keyData);
+                  }
+              }
+            break;
+
+        default:
+          break;
+        }
+    }
+  else if (qobject_cast<QComboBox*> (watched))
+    {
+      switch (xevent->type ())
+        {
+          case QEvent::KeyPress:
+            {
+              QKeyEvent* k = dynamic_cast<QKeyEvent*> (xevent);
+              if (m_keyPressHandlerDefined)
+                {
+                  gh_manager::auto_lock lock;
+
+                  octave_scalar_map keyData = Utils::makeKeyEventStruct (k);
+                  graphics_object fig = object ().get_ancestor ("figure");
+
+                  gh_manager::post_set (fig.get_handle (), "currentcharacter",
+                                        keyData.getfield ("Character"), false);
+                  gh_manager::post_callback (m_handle, "keypressfcn", keyData);
+                }
+              switch (k->key())
+              {
+                case Qt::Key_Left:
+                {
+                  int row = m_tableWidget->currentRow ();
+                  int col = m_tableWidget->currentColumn ();
+                  QComboBox* comboBox_1 = qobject_cast<QComboBox*> (
+                    m_tableWidget->cellWidget (row, col));
+                  if (comboBox_1)
+                    {
+                      m_tableWidget->setCurrentCell (row,
+                        (col - 1 < 0) ? 0 : col - 1);
+                      m_tableWidget->setFocus ();
+                    }
+                }
+                break;
+
+                case Qt::Key_Right:
+                {
+                  int row = m_tableWidget->currentRow ();
+                  int col = m_tableWidget->currentColumn ();
+                  QComboBox* comboBox_1 = qobject_cast<QComboBox*> (
+                    m_tableWidget->cellWidget (row, col));
+                  if (comboBox_1)
+                    {
+                      m_tableWidget->setCurrentCell (row,
+                        (col + 1 < m_tableWidget->columnCount())
+                          ? col + 1 : m_tableWidget->columnCount());
+                      m_tableWidget->setFocus ();
+                    }
+                }
+                break;
+
+                default:
+                break;
+              }
+            }
+
+            case QEvent::MouseButtonPress:
+              {
+                gh_manager::auto_lock lock;
+                QMouseEvent* m = dynamic_cast<QMouseEvent*> (xevent);
+                graphics_object go = object ();
+                const uitable::properties& tp =
+                  Utils::properties<uitable> (go);
+                graphics_object fig = go.get_ancestor ("figure");
+
+                if (m->button () != Qt::LeftButton || !tp.enable_is ("on"))
+                  {
+                    gh_manager::post_set (fig.get_handle (), "selectiontype",
+                                          Utils::figureSelectionType (m), false);
+                    gh_manager::post_set (fig.get_handle (), "currentpoint",
+                                          Utils::figureCurrentPoint (fig, m),
+                                          false);
+                    gh_manager::post_callback (fig.get_handle (),
+                                               "windowbuttondownfcn");
+                    gh_manager::post_callback (m_handle, "buttondownfcn");
+
+                    if (m->button () == Qt::RightButton)
+                      ContextMenu::executeAt (tp, m->globalPos ());
+                  }
+                else
+                  {
+                    gh_manager::post_set (fig.get_handle (), "selectiontype",
+                                         Utils::figureSelectionType (m), false);
+                  }
+              }
+            break;
+
+            default:
+            break;
+        }
+    }
+  return false;
+}
+
+}
+#undef AUTO_HEIGHT
diff -r 046ade63abc4 -r 006fe5e79583 libgui/graphics/Table.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libgui/graphics/Table.h	Fri Aug 19 21:21:35 2016 +0100
@@ -0,0 +1,90 @@
+/*
+
+Copyright (C) 2016 Andrew Thornton
+
+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
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#if ! defined (octave_Table_h)
+#define octave_Table_h 1
+
+#include "Object.h"
+
+class QCheckBox;
+class QTableWidget;
+class QTableWidgetItem;
+
+namespace QtHandles
+{
+
+class Container;
+
+class Table : public Object
+{
+  Q_OBJECT
+
+public:
+  Table (const graphics_object& go, QTableWidget* tableWidget);
+  ~Table (void);
+
+  Container* innerContainer (void) { return m_container; }
+
+  bool eventFilter (QObject* watched, QEvent* event);
+
+  static Table* create (const graphics_object& go);
+
+protected:
+  void update (int pId);
+  //void redraw (void);
+  void updateColumnname (void);
+  void updateColumnwidth (void);
+  void updateData (void);
+  void updateEnable (void);
+  void updateExtent (void);
+  void updatePalette (void);
+  void updateRearrangeableColumns (void);
+  void updateRowname (void);
+
+private slots:
+  void itemChanged (QTableWidgetItem* item);
+  void comboBoxCurrentIndexChanged (const QString& value);
+  void cellClicked (int row, int col);
+  void itemSelectionChanged (void);
+
+private:
+  Container* m_container;
+  QTableWidget* m_tableWidget;
+  bool m_blockUpdates;
+  bool m_keyPressHandlerDefined;
+  bool m_keyReleaseHandlerDefined;
+  QCheckBox * checkBoxForLogical(octave_value cal, bool enabled);
+  void updateData (int row, int col, octave_value value, std::string format, bool enabled);
+  void updateData (int row, int col);
+  void updateDataColumn (int col);
+  std::string columnformat (int column);
+  bool columneditable (int column);
+  void sendCellEditCallback (int row, int col, octave_value old_value, octave_value new_value, octave_value edit_data, octave_value error);
+  void checkBoxClicked (int row, int col, QCheckBox* checkBox);
+
+
+
+};
+
+}; // namespace QtHandles
+
+#endif
diff -r 046ade63abc4 -r 006fe5e79583 libgui/graphics/__init_qt__.cc
--- a/libgui/graphics/__init_qt__.cc	Fri Aug 19 21:05:48 2016 +0200
+++ b/libgui/graphics/__init_qt__.cc	Fri Aug 19 21:21:35 2016 +0100
@@ -74,6 +74,9 @@
           root.set ("defaultuicontrolforegroundcolor",
                     octave_value (Utils::toRgb
                                   (p.color (QPalette::WindowText))));
+          root.set ("defaultuitableforegroundcolor",
+                    octave_value (Utils::toRgb
+                                  (p.color (QPalette::WindowText))));
           root.set ("defaultuipanelbackgroundcolor",
                     octave_value (Utils::toRgb (p.color (QPalette::Window))));
           root.set ("defaultuipanelforegroundcolor",
diff -r 046ade63abc4 -r 006fe5e79583 libgui/graphics/module.mk
--- a/libgui/graphics/module.mk	Fri Aug 19 21:05:48 2016 +0200
+++ b/libgui/graphics/module.mk	Fri Aug 19 21:21:35 2016 +0100
@@ -19,6 +19,7 @@
   libgui/graphics/moc-PushTool.cc \
   libgui/graphics/moc-SliderControl.cc \
   libgui/graphics/moc-TextEdit.cc \
+  libgui/graphics/moc-Table.cc \
   libgui/graphics/moc-ToggleTool.cc \
   libgui/graphics/moc-ToolBar.cc
 
@@ -75,6 +76,7 @@
   libgui/graphics/QtHandlesUtils.h \
   libgui/graphics/RadioButtonControl.h \
   libgui/graphics/SliderControl.h \
+  libgui/graphics/Table.h \
   libgui/graphics/TextControl.h \
   libgui/graphics/TextEdit.h \
   libgui/graphics/ToggleButtonControl.h \
@@ -114,6 +116,7 @@
   libgui/graphics/QtHandlesUtils.cc \
   libgui/graphics/RadioButtonControl.cc \
   libgui/graphics/SliderControl.cc \
+  libgui/graphics/Table.cc \
   libgui/graphics/TextControl.cc \
   libgui/graphics/TextEdit.cc \
   libgui/graphics/ToggleButtonControl.cc \
diff -r 046ade63abc4 -r 006fe5e79583 libinterp/corefcn/gl-render.cc
--- a/libinterp/corefcn/gl-render.cc	Fri Aug 19 21:05:48 2016 +0200
+++ b/libinterp/corefcn/gl-render.cc	Fri Aug 19 21:21:35 2016 +0100
@@ -674,7 +674,8 @@
       draw_image (dynamic_cast<const image::properties&> (props));
     else if (go.isa ("uimenu") || go.isa ("uicontrol")
              || go.isa ("uicontextmenu") || go.isa ("uitoolbar")
-             || go.isa ("uipushtool") || go.isa ("uitoggletool"))
+             || go.isa ("uipushtool") || go.isa ("uitoggletool")
+             || go.isa ("uitable"))
       ; // SKIP
     else if (go.isa ("uipanel"))
       {
diff -r 046ade63abc4 -r 006fe5e79583 libinterp/corefcn/graphics.cc
--- a/libinterp/corefcn/graphics.cc	Fri Aug 19 21:05:48 2016 +0200
+++ b/libinterp/corefcn/graphics.cc	Fri Aug 19 21:21:35 2016 +0100
@@ -542,6 +542,32 @@
   return m;
 }
 
+static Matrix
+default_table_position (void)
+{
+  Matrix retval (1, 4);
+
+  retval(0) = 20;
+  retval(1) = 20;
+  retval(2) = 300;
+  retval(3) = 300;
+
+  return retval;
+}
+
+static Matrix
+default_table_backgroundcolor (void)
+{
+  Matrix retval (2, 3);
+  retval(0, 0) = 1;
+  retval(0, 1) = 1;
+  retval(0, 2) = 1;
+  retval(1, 0) = 0.94;
+  retval(1, 1) = 0.94;
+  retval(1, 2) = 0.94;
+  return retval;
+}
+
 static double
 convert_font_size (double font_size, const caseless_str& from_units,
                    const caseless_str& to_units, double parent_height = 0)
@@ -1041,7 +1067,7 @@
                   pfx = name.substr (0, 7);
 
                   if (pfx.compare ("surface") || pfx.compare ("hggroup")
-                      || pfx.compare ("uipanel"))
+                      || pfx.compare ("uipanel") || pfx.compare ("uitable"))
                     offset = 7;
                   else if (len >= 9)
                     {
@@ -1123,6 +1149,8 @@
     go = new uibuttongroup (h, p);
   else if (type.compare ("uicontextmenu"))
     go = new uicontextmenu (h, p);
+  else if (type.compare ("uitable"))
+    go = new uitable (h, p);
   else if (type.compare ("uitoolbar"))
     go = new uitoolbar (h, p);
   else if (type.compare ("uipushtool"))
@@ -1955,7 +1983,7 @@
                   pfx = name.substr (0, 7);
 
                   if (pfx.compare ("surface") || pfx.compare ("hggroup")
-                      || pfx.compare ("uipanel"))
+                      || pfx.compare ("uipanel") || pfx.compare ("uitable"))
                     offset = 7;
                   else if (len > 9)
                     {
@@ -2028,6 +2056,8 @@
             has_property = uipanel::properties::has_core_property (pname);
           else if (pfx == "uicontextmenu")
             has_property = uicontextmenu::properties::has_core_property (pname);
+          else if (pfx == "uitable")
+            has_property = uitable::properties::has_core_property (pname);
           else if (pfx == "uitoolbar")
             has_property = uitoolbar::properties::has_core_property (pname);
           else if (pfx == "uipushtool")
@@ -2095,7 +2125,7 @@
                   pfx = name.substr (0, 7);
 
                   if (pfx.compare ("surface") || pfx.compare ("hggroup")
-                      || pfx.compare ("uipanel"))
+                      || pfx.compare ("uipanel") || pfx.compare ("uitable"))
                     offset = 7;
                   else if (len > 9)
                     {
@@ -9157,6 +9187,251 @@
 
 // ---------------------------------------------------------------------
 
+Matrix
+uitable::properties::get_boundingbox (bool,
+                                      const Matrix& parent_pix_size) const
+{
+  Matrix pos = get_position ().matrix_value ();
+  Matrix parent_size (parent_pix_size);
+
+  if (parent_size.is_empty ())
+    {
+      graphics_object go = gh_manager::get_object (get_parent ());
+
+      parent_size =
+        go.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
+    }
+
+  pos = convert_position (pos, get_units (), "pixels", parent_size);
+
+  pos(0)--;
+  pos(1)--;
+  pos(1) = parent_size(1) - pos(1) - pos(3);
+  return pos;
+}
+
+void
+uitable::properties::set_columnformat (const octave_value& val)
+{
+  /* Matlab only allows certain values for columnformat - here we will only
+     check the structure of the argument. Values will be checked in Table.cc */
+
+  if (val.is_cellstr ())
+    {
+      if (columnformat.set (val, true))
+        {
+          mark_modified ();
+        }
+    }
+  else if (val.is_cell ())
+    {
+      Cell cell_value = val.cell_value ();
+
+      for (int i = 0; i < cell_value.numel (); i++)
+        {
+          octave_value v = cell_value (i);
+          if (v.is_cell ())
+            {
+              /* We are in a pop-up menu selection
+               * Matlab only allows non-empty strings here */
+              Cell popup = v.cell_value ();
+              for (int j = 0; j < popup.numel (); j++)
+                {
+                  octave_value p = popup (j);
+                  if (!p.is_string () || p.string_value ().length () == 0)
+                    {
+                      error ("set: pop-up menu definitions must be non-empty strings.");
+                    }
+                }
+            }
+          else if (!(v.is_string () || v.is_empty ()))
+            {
+              error ("set: columnformat definintions must be a cellstr of "
+                "either 'char', 'short [e|g|eng]?', 'long [e|g|eng]?', "
+                "'numeric', 'bank', '+', 'rat', 'logical', or a cellstr of non-empty "
+                "pop-up menu definitions.");
+            }
+        }
+
+      if (columnformat.set (val, true))
+        {
+          mark_modified ();
+        }
+    }
+  else if (val.is_empty ())
+    {
+      if (columnformat.set (Cell (), true))
+        {
+          mark_modified ();
+        }
+    }
+  else
+    {
+      error ("set: expecting cell of strings.");
+    }
+}
+
+void
+uitable::properties::set_columnwidth (const octave_value& val)
+{
+  bool error_exists = false;
+  if (val.is_string ()  && val.string_value (false) == "auto")
+    {
+      error_exists = false;
+    }
+  else if (val.is_cell ())
+    {
+      Cell cell_value = val.cell_value ();
+      for (int i = 0; i < cell_value.numel (); i++)
+        {
+          octave_value v = cell_value (i);
+          if (v.is_string ())
+            {
+              if (v.string_value (false) != "auto")
+                error_exists = true;
+            }
+          else if (v.is_cell ())
+            {
+              error_exists = true;
+            }
+          else if (!(v.is_scalar_type ()))
+            {
+              error_exists = true;
+            }
+        }
+    }
+  else
+    {
+      error_exists = true;
+    }
+
+  if (error_exists)
+    {
+      error ("set: expecting either 'auto' or a cell of pixel values or auto.");
+    }
+  else
+    {
+      if (columnwidth.set (val, true))
+        {
+          mark_modified ();
+        }
+    }
+}
+
+void
+uitable::properties::set_units (const octave_value& val)
+{
+  caseless_str old_units = get_units ();
+
+  if (units.set (val, true))
+    {
+      update_units (old_units);
+      mark_modified ();
+    }
+}
+
+void
+uitable::properties::update_units (const caseless_str& old_units)
+{
+  Matrix pos = get_position ().matrix_value ();
+
+  graphics_object parent_go = gh_manager::get_object (get_parent ());
+  Matrix parent_bbox = parent_go.get_properties ().get_boundingbox (true);
+  Matrix parent_size = parent_bbox.extract_n (0, 2, 1, 2);
+
+  pos = convert_position (pos, old_units, get_units (), parent_size);
+  set_position (pos);
+}
+
+void
+uitable::properties::set_fontunits (const octave_value& val)
+{
+  caseless_str old_fontunits = get_fontunits ();
+
+  if (fontunits.set (val, true))
+    {
+      update_fontunits (old_fontunits);
+      mark_modified ();
+    }
+}
+
+void
+uitable::properties::update_fontunits (const caseless_str& old_units)
+{
+  caseless_str new_units = get_fontunits ();
+  double parent_height = get_boundingbox (false).elem (3);
+  double fontsz = get_fontsize ();
+
+  fontsz = convert_font_size (fontsz, old_units, new_units, parent_height);
+
+  set_fontsize (octave_value (fontsz));
+}
+
+double
+uitable::properties::get_fontsize_points (double box_pix_height) const
+{
+  double fontsz = get_fontsize ();
+  double parent_height = box_pix_height;
+
+  if (fontunits_is ("normalized") && parent_height <= 0)
+    parent_height = get_boundingbox (false).elem (3);
+
+  return convert_font_size (fontsz, get_fontunits (), "points", parent_height);
+}
+
+double
+uitable::properties::get_fontsize_pixels (double box_pix_height) const
+{
+  double fontsz = get_fontsize ();
+  double parent_height = box_pix_height;
+
+  if (fontunits_is ("normalized") && parent_height <= 0)
+    parent_height = get_boundingbox (false).elem (3);
+
+  return convert_font_size (fontsz, get_fontunits (), "pixels", parent_height);
+}
+
+Matrix
+uitable::properties::get_backgroundcolor_rgb (void)
+{
+  Matrix bg = backgroundcolor.get ().matrix_value ();
+  return bg.row (0);
+}
+
+Matrix
+uitable::properties::get_alternatebackgroundcolor_rgb (void)
+{
+  int i = 0;
+  Matrix bg = backgroundcolor.get ().matrix_value ();
+  if (bg.rows () > 1)
+    i = 1;
+  return bg.row (i);
+}
+
+Matrix
+uitable::properties::get_extent_matrix (void) const
+{
+  return extent.get ().matrix_value ();
+}
+
+octave_value
+uitable::properties::get_extent (void) const
+{
+  // FIXME: Is it really acceptable to just let the toolkit update the extent?
+  Matrix m = extent.get ().matrix_value ();
+  graphics_object parent_go = gh_manager::get_object (get_parent ());
+  if (parent_go)
+    {
+      Matrix parent_bbox = parent_go.get_properties ().get_boundingbox (true);
+      Matrix parent_size = parent_bbox.extract_n (0, 2, 1, 2);
+
+      return convert_position (m, "pixels", get_units (), parent_size);
+    }
+  return m;
+}
+
+// ---------------------------------------------------------------------
+
 octave_value
 uitoolbar::get_default (const caseless_str& pname) const
 {
@@ -10662,6 +10937,15 @@
   GO_BODY (uicontextmenu);
 }
 
+DEFUN (__go_uitable__, args, ,
+       "-*- texinfo -*-\n\
+@deftypefn {} {} __go_uitable__ (@var{parent})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  GO_BODY (uitable);
+}
+
 DEFUN (__go_uitoolbar__, args, ,
        doc: /* -*- texinfo -*-
 @deftypefn {} {} __go_uitoolbar__ (@var{parent})
@@ -11728,7 +12012,6 @@
 
   return ovl ();
 }
-
 DEFUN (__zoom__, args, ,
        doc: /* -*- texinfo -*-
 @deftypefn  {} {} __zoom__ (@var{axes}, @var{mode}, @var{factor})
diff -r 046ade63abc4 -r 006fe5e79583 libinterp/corefcn/graphics.in.h
--- a/libinterp/corefcn/graphics.in.h	Fri Aug 19 21:05:48 2016 +0200
+++ b/libinterp/corefcn/graphics.in.h	Fri Aug 19 21:21:35 2016 +0100
@@ -5733,6 +5733,105 @@
 
 // ---------------------------------------------------------------------
 
+class OCTINTERP_API uitable : public base_graphics_object
+{
+public:
+  class OCTINTERP_API properties : public base_properties
+  {
+  public:
+    Matrix get_boundingbox (bool internal = false,
+                            const Matrix& parent_pix_size = Matrix ()) const;
+
+    double get_fontsize_points (double box_pix_height = 0) const;
+
+    double get_fontsize_pixels (double box_pix_height = 0) const;
+
+    // See the genprops.awk script for an explanation of the
+    // properties declarations.
+    // Programming note: Keep property list sorted if new ones are added.
+
+    BEGIN_PROPERTIES (uitable)
+      any_property __object__ h , Matrix ()
+      array_property backgroundcolor , default_table_backgroundcolor ()
+      callback_property celleditcallback , Matrix ()
+      callback_property cellselectioncallback , Matrix ()
+      row_vector_property columneditable , Matrix ()
+      any_property columnformat S , Matrix ()
+      any_property columnname , "numbered"
+      any_property columnwidth S , "auto"
+      any_property data u , Matrix ()
+      radio_property enable , "{on}|inactive|off"
+      array_property extent rG , Matrix (1, 4, 0.0)
+      radio_property fontangle u , "{normal}|italic|oblique"
+      string_property fontname u , OCTAVE_DEFAULT_FONTNAME
+      double_property fontsize u , 10
+      radio_property fontunits S , "inches|centimeters|normalized|{points}|pixels"
+      radio_property fontweight u , "light|{normal}|demi|bold"
+      color_property foregroundcolor , color_values (0, 0, 0)
+      callback_property keypressfcn , Matrix ()
+      callback_property keyreleasefcn , Matrix ()
+      array_property position , default_table_position ()
+      bool_property rearrangeablecolumns , "off"
+      any_property rowname , "numbered"
+      bool_property rowstriping , "on"
+      string_property tooltipstring , ""
+      radio_property units S , "normalized|inches|centimeters|points|{pixels}|characters"
+    END_PROPERTIES
+
+    Matrix get_extent_matrix (void) const;
+
+    Matrix get_backgroundcolor_rgb (void);
+
+    Matrix get_alternatebackgroundcolor_rgb (void);
+
+  protected:
+    void init (void)
+    {
+      position.add_constraint (dim_vector (1, 4));
+      extent.add_constraint (dim_vector (1, 4));
+      backgroundcolor.add_constraint (dim_vector (-1, 3));
+      backgroundcolor.add_constraint ("double");
+      columneditable.add_constraint ("logical");
+    }
+
+    void update_units (const caseless_str& old_units);
+    void update_fontunits (const caseless_str& old_units);
+    void update_table_extent (void) { };
+    void update_data (void) { update_table_extent (); }
+    void update_fontname (void) { update_table_extent (); }
+    void update_fontsize (void) { update_table_extent (); }
+    void update_fontangle (void) { update_table_extent (); }
+    void update_fontweight (void) { update_table_extent (); }
+
+  };
+
+private:
+  properties xproperties;
+
+public:
+  uitable (const graphics_handle& mh, const graphics_handle& p)
+    : base_graphics_object (), xproperties (mh, p)
+  { }
+
+  ~uitable (void) { }
+
+  base_properties& get_properties (void) { return xproperties; }
+
+  const base_properties& get_properties (void) const { return xproperties; }
+
+  bool valid_object (void) const { return true; }
+
+  bool has_readonly_property (const caseless_str& pname) const
+  {
+    bool retval = xproperties.has_readonly_property (pname);
+    if (! retval)
+      retval = base_properties::has_readonly_property (pname);
+    return retval;
+  }
+};
+
+// ---------------------------------------------------------------------
+
 class OCTINTERP_API uitoolbar : public base_graphics_object
 {
 public:
diff -r 046ade63abc4 -r 006fe5e79583 libinterp/corefcn/module.mk
--- a/libinterp/corefcn/module.mk	Fri Aug 19 21:05:48 2016 +0200
+++ b/libinterp/corefcn/module.mk	Fri Aug 19 21:21:35 2016 +0100
@@ -87,6 +87,7 @@
   libinterp/corefcn/sparse-xdiv.h \
   libinterp/corefcn/sparse-xpow.h \
   libinterp/corefcn/symtab.h \
+  libinterp/corefcn/str2double.h \
   libinterp/corefcn/sysdep.h \
   libinterp/corefcn/text-renderer.h \
   libinterp/corefcn/toplev.h \
@@ -332,4 +333,3 @@
   libinterp/corefcn/oct-tex-lexer.in.ll \
   libinterp/corefcn/oct-tex-parser.in.yy \
   libinterp/corefcn/oct-tex-symbols.in
-
diff -r 046ade63abc4 -r 006fe5e79583 libinterp/corefcn/pr-output.cc
--- a/libinterp/corefcn/pr-output.cc	Fri Aug 19 21:05:48 2016 +0200
+++ b/libinterp/corefcn/pr-output.cc	Fri Aug 19 21:21:35 2016 +0100
@@ -346,7 +346,7 @@
   return os;
 }
 
-static inline std::string
+std::string
 rational_approx (double val, int len)
 {
   std::string s;
diff -r 046ade63abc4 -r 006fe5e79583 libinterp/corefcn/pr-output.h
--- a/libinterp/corefcn/pr-output.h	Fri Aug 19 21:05:48 2016 +0200
+++ b/libinterp/corefcn/pr-output.h	Fri Aug 19 21:21:35 2016 +0100
@@ -269,4 +269,7 @@
 // TRUE means don't put empty lines in output
 extern bool Vcompact_format;
 
+std::string
+rational_approx (double val, int len);
+
 #endif
diff -r 046ade63abc4 -r 006fe5e79583 libinterp/corefcn/str2double.cc
--- a/libinterp/corefcn/str2double.cc	Fri Aug 19 21:05:48 2016 +0200
+++ b/libinterp/corefcn/str2double.cc	Fri Aug 19 21:21:35 2016 +0100
@@ -259,8 +259,8 @@
 #endif
 }
 
-static Complex
-str2double1 (const std::string& str_arg)
+Complex
+str2double_internal (const std::string& str_arg)
 {
   Complex val (0.0, 0.0);
 
@@ -351,12 +351,12 @@
       if (args(0).rows () == 0 || args(0).columns () == 0)
         retval = Matrix (1, 1, octave::numeric_limits<double>::NaN ());
       else if (args(0).rows () == 1 && args(0).ndims () == 2)
-        retval = str2double1 (args(0).string_value ());
+        retval = str2double_internal (args(0).string_value ());
       else
         {
           const string_vector sv = args(0).string_vector_value ();
 
-          retval = sv.map<Complex> (str2double1);
+          retval = sv.map<Complex> (str2double_internal);
         }
     }
   else if (args(0).is_cell ())
@@ -368,7 +368,7 @@
       for (octave_idx_type i = 0; i < cell.numel (); i++)
         {
           if (cell(i).is_string ())
-            output(i) = str2double1 (cell(i).string_value ());
+            output(i) = str2double_internal (cell(i).string_value ());
         }
       retval = output;
     }
diff -r 046ade63abc4 -r 006fe5e79583 libinterp/corefcn/str2double.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/corefcn/str2double.h	Fri Aug 19 21:21:35 2016 +0100
@@ -0,0 +1,29 @@
+/*
+
+Copyright (C) 2016 Andrew Thornton
+
+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
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#if ! defined (octave_str2double_h)
+#define octave_str2double_h 1
+
+#include "ov.h"
+
+Complex str2double_internal (const std::string& str_arg);
+#endif
diff -r 046ade63abc4 -r 006fe5e79583 libinterp/genprops.awk
--- a/libinterp/genprops.awk	Fri Aug 19 21:05:48 2016 +0200
+++ b/libinterp/genprops.awk	Fri Aug 19 21:21:35 2016 +0100
@@ -228,10 +228,10 @@
 
 function emit_get_callback (i)
 {
-  printf ("  void execute_%s (const octave_value& data = octave_value ()) const", name[i]);
+  printf ("  void execute_%s (const octave_value& _data = octave_value ()) const", name[i]);
 
   if (emit_get[i] == "definition")
-    printf (" { %s.execute (data); }\n", name[i]);
+    printf (" { %s.execute (_data); }\n", name[i]);
   else
     printf (";\n");
 
diff -r 046ade63abc4 -r 006fe5e79583 scripts/gui/module.mk
--- a/scripts/gui/module.mk	Fri Aug 19 21:05:48 2016 +0200
+++ b/scripts/gui/module.mk	Fri Aug 19 21:21:35 2016 +0100
@@ -32,6 +32,7 @@
   scripts/gui/uipushtool.m \
   scripts/gui/uiputfile.m \
   scripts/gui/uiresume.m \
+  scripts/gui/uitable.m \
   scripts/gui/uitoggletool.m \
   scripts/gui/uitoolbar.m \
   scripts/gui/uiwait.m \
diff -r 046ade63abc4 -r 006fe5e79583 scripts/gui/uitable.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/gui/uitable.m	Fri Aug 19 21:21:35 2016 +0100
@@ -0,0 +1,291 @@
+## Copyright (C) 2016 Andrew Thornton
+##
+## 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
+## <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {} {@var{hui} =} uitable (@var{property}, @var{value}, @dots{})
+## @deftypefnx {} {@var{hui} =} uitable (@var{parent}, "@var{property}, @var{value}, @dots{})
+##
+## Create a uitable object and return a handle to it.
+##
+## uitables are used to show tables of data in a figure.
+##
+## If @var{parent} is omitted then a uitable for the current figure is
+## created.  If no figure is available, a new figure is created first.
+##
+## If @var{parent} is given then a uitable relative to @var{parent} is created.
+##
+## Any provided property value pairs will override the default values of the
+## created uitable object.
+##
+## Uitable properties are documented at @ref{Uitable Properties}.
+##
+## Examples:
+##
+## @example
+## @group
+## % create figure and table on it
+## f = figure;
+## m = magic(8);
+## t = uitable(f, 'Data', m, 'ColumnWidth', @{ 40 @});
+## @end group
+##
+## @group
+## % Create a named table
+## f = figure();
+## d = reshape(1:9, 3, []);
+## column_names = @{ 'Col1', 'Col2', 'Col3' @};
+## row_names = @{ 'Row1', 'Row2', 'Row3' @};
+## t = uitable(f, 'Data', d, ...
+##             'ColumnName', column_names, ...
+##             'RowName', row_names);
+##
+## p = get(t, 'Position');
+## e = get(t, 'Extent');
+## p(3:4) = e(3:4);
+## set(t, 'Position', p);
+## @end group
+##
+## @group
+## function uitable_demo ()
+##     f = figure ('Name', 'uitable Demo', 'Menu', 'none', 'Position', [100 100 1000 1000]);
+##
+##     % A basic example
+##     d = @{ 'char', 'A string';
+##         'double', 12.3456789;
+##         'complex', 1+2i;
+##         'bool', true;
+##         'single', single(12.3456789);
+##         'int8', int8(-128);
+##         'uint8', uint8(128);
+##         'int16', int16(-32768);
+##         'uint16', uint16(32768);
+##         'int32', int32(-2147483648);
+##         'uint32', uint32(2147483648);
+##         'int64', int64(-2147483649);
+##         'uint64', uint64(2147843649)@};
+##
+##     popup_options = @{'A', 'B', 'C', 'D', 'E'@};
+##
+##     columnformat_options = @{ '[]', 'char', 'pop-up', 'numeric', 'short', 'short e', ...
+##                     'short eng', 'short g', 'long', 'long e', 'long eng', ...
+##                     'long g', 'bank', '+', 'rat', 'logical'@};
+##     columnformat_values = columnformat_options;
+##     columnformat_values@{1@} = '';
+##     columnformat_values@{3@} = popup_options;
+##
+##     %default_data = cell(size(d, 1), 10);
+##     default_data = repmat(d(:,2), 1, size(columnformat_options, 2));
+##     b_add = uicontrol(f, 'Position', [ 285 950 600 50], ...
+##               'UserData', [size(d,1) 1], ...
+##               'Style', 'pushbutton', ...
+##               'String', 'Set data at selected point to selected datatype');
+##
+##     l_type_table = uicontrol(f, 'Position', [ 0 925 120 25 ], ...
+##         'String', 'Datatype Table:', ...
+##         'Style', 'text');
+##     t_type_table = uitable(f, 'Position', [ 0 860 1000 65 ], ...
+##         'Data', transpose(d(:, 2)), ...
+##         'ColumnName', transpose(d(:, 1)), ...
+##         'RowName', 'Value', ...
+##         'CellSelectionCallback', @@(x, y) ...
+##             set(b_add, 'UserData', y.Indices ));
+##
+##     l_point_table = uicontrol(f, 'Position', [ 60 975 60 25 ], ...
+##         'String', 'Point:', ...
+##         'Style', 'text');
+##     t_point_table = uitable(f, 'Position', [ 120 950 160 42 ], ...
+##                            'RowName', [], ...
+##                            'ColumnName', @{'x', 'y'@}, ...
+##                            'Data', [ 1 1 ], ...
+##                            'ColumnEditable', true);
+##
+##     l_editable_table = uicontrol(f, 'Position', [ 0 815 200 25 ], ...
+##         'Style', 'text', ...
+##         'String', 'Set Data Columns Editable:');
+##     t_editable_table = uitable(f, 'Position', [ 0 750 1000 65 ], ...
+##                                'Data', repmat(false, 1, size(default_data, 2)), ...
+##                                'ColumnEditable', true);
+##
+##     l_format_table = uicontrol(f, 'Position', [ 0 725 200 25 ], ...
+##         'Style', 'text', ...
+##         'String', 'Set Data Column Format:');
+##     t_format_table = uitable(f, 'Position', [ 0 660 1000 65 ], ...
+##         'Data', columnformat_options, ...
+##         'ColumnEditable', true, ...
+##         'ColumnFormat', arrayfun(@@(x) @{columnformat_options@}, 1:size(columnformat_options, 2)));
+##
+##     l_data_table = uicontrol(f, 'Style', 'text', 'String', 'Data:', 'Position', [ 0 635 60 25]);
+##     t_data_table = uitable(f, 'Position', [ 0 0 1000 635 ], ...
+##         'Data', default_data, ...
+##         'ColumnFormat', columnformat_values);
+##
+##     set(t_format_table, 'CellEditCallback', ...
+##         @@(x, y) update_column_format(y.NewData, y.Indices, t_data_table, popup_options));
+##     set(t_point_table, 'CellEditCallback', ...
+##        @@(x, y) validate_point_table(x, y, t_data_table));
+##     set(t_editable_table, 'CellEditCallback', @@(x,y) set(t_data_table, ...
+##             'ColumnEditable', get(t_editable_table, 'Data')));
+##     set(b_add, 'Callback', @@(x, y) ...
+##             update_data(b_add, t_point_table, t_type_table, t_data_table));
+##     set(t_data_table, 'cellselectioncallback', @@(x, y) update_point_table(y.Indices, t_point_table));
+## end
+##
+## function validate_point_table(h, dat, t_data_table)
+##     if (~(dat.NewData > 0 && ...
+##         dat.NewData < size(get(t_data_table, 'Data'), dat.Indices(1, 1)) + 1))
+##
+##         d = get(h, 'Data');
+##         d(dat.Indices) = 1;
+##         set(h, 'Data', d);
+##     end
+## end
+##
+## function update_column_format(format, indices, t_data_table, popup_options)
+##     cf = get(t_data_table, 'ColumnFormat');
+##     if (strcmp(format, '[]'))
+##         format = '';
+##     elseif (strcmp(format, 'pop-up'))
+##         format = popup_options;
+##     end
+##     cf@{indices(1,2)@} = format;
+##     set(t_data_table, 'ColumnFormat', cf);
+## end
+##
+## function update_point_table(indices, t_point_table)
+##     if (size(indices, 1) == 0)
+##         indices = [1 1];
+##     end
+##     set(t_point_table, 'Data', indices(1,:));
+## end  % function
+##
+## function update_data (b_add, t_point_table, t_type_table, t_data_table)
+##     indices = get(b_add, 'UserData');
+##     if (size(indices, 1) == 0)
+##         indices = [1 1];
+##     end
+##     d = get(t_data_table, 'Data');
+##     t_type_table_data = get(t_type_table, 'Data');
+##     p = get(t_point_table, 'Data');
+##     d(p(1,1), p(1,2)) = t_type_table_data(indices(1,2));
+##     set(t_data_table, 'Data', d);
+## end
+## @end group
+## @end example
+##
+## @seealso{figure, uicontrol}
+## @end deftypefn
+
+## Author: Andrew Thornton
+
+function hui = uitable (varargin)
+    [h, args] = __uiobject_split_args__ ("uitable", varargin, ...
+                                         {"figure", "uipanel", "uibuttongroup"});
+    hui = __go_uitable__ (h, args{:});
+
+endfunction
+%!demo
+%!
+%! % create figure and table on it
+%! f = figure;
+%! m = magic(8);
+%! t = uitable(f, 'Data', m, 'ColumnWidth', { 40 });
+%!demo
+%! % create figure and table with editable
+%! f = figure;
+%! m = magic(8);
+%! t = uitable(f, 'Data', m, 'ColumnWidth', { 40 }, 'ColumnEditable', true);
+%!demo
+%! % create figure and table on but change the format to rational_approx
+%! f = figure;
+%! m = magic(8) / 64;
+%! t = uitable(f, 'Data', m, 'ColumnWidth', { 40 }, 'ColumnFormat', arrayfun(@(x) {'rat'}, 1:8));
+%!demo
+%! cf = { 'char', 'char', {'A', 'B', 'C'}, 'numeric', 'short', 'short e', ...
+%!      'short eng', 'short g', 'long', 'long e', 'long eng', 'long g', ...
+%!      'bank', '+', 'rat', 'logical'};
+%! cn = cf;
+%! cn{3} = 'pop-up';
+%!
+%! d = { 'double', 0.0000123456789;
+%! 'double', 0.000123456789;
+%! 'double', 0.00123456789;
+%! 'double', 0.0123456789;
+%! 'double', 0.123456789;
+%! 'double', 12;
+%! 'double', 1.23456789;
+%! 'double', 12.3456789;
+%! 'double', 123.456789;
+%! 'double', 1234.56789;
+%! 'complex', 0.0000123456789 + 12.3456789i;
+%! 'complex', 0.000123456789 + 12.3456789i;
+%! 'complex', 0.00123456789 + 12.3456789i;
+%! 'complex', 0.0123456789 + 12.3456789i;
+%! 'complex', 0.123456789 + 12.3456789i;
+%! 'complex', 1.23456789 + 12.3456789i;
+%! 'complex', 12.3456789 + 12.3456789i;
+%! 'complex', 123.456789 + 12.3456789i;
+%! 'complex', 1234.56789 + 12.3456789i;
+%! 'bool', true;
+%! 'bool', false;
+%! 'single', single(0.0000123456789);
+%! 'single', single(1.23456789);
+%! 'single', single(1234.56789);
+%! 'int8', int8(127);
+%! 'int8', int8(0);
+%! 'int8', int8(-126);
+%! 'int16', int16(32767);
+%! 'int16', int16(0);
+%! 'int16', int16(-32768);
+%! 'int32', int32(2147483647);
+%! 'int32', int32(0);
+%! 'int32', int32(-2147483647);
+%! 'int64', int64(9223372036854775807);
+%! 'int64', int64(0);
+%! 'int64', int64(-9223372036854775807);
+%! 'uint8', uint8(127);
+%! 'uint8', uint8(0);
+%! 'uint8', uint8(255);
+%! 'uint16', uint16(32767);
+%! 'uint16', uint16(0);
+%! 'uint16', uint16(65535);
+%! 'uint32', uint32(2147483647);
+%! 'uint32', uint32(0);
+%! 'uint32', uint32(4294967295);
+%! 'uint64', uint64(9223372036854775807);
+%! 'uint64', uint64(0);
+%! 'uint64', uint64(18446744073709551615);
+%! 'char', 'a string'};
+%!
+%! ws = { 'auto', 140 };
+%! widths = cell(1, size(cf, 2));
+%! widths(1,1) = ws(1,1);
+%! widths(1,2:end) = repmat(ws(:, 2), 1, size(cf, 2) - 1);
+%!
+%! data = cell(size(d, 1), size(cf, 2));
+%!
+%! data(:, 1) = d(:, 1);
+%!
+%! data(:, 2:end) = repmat(d(:, 2), 1, size(cf, 2) - 1);
+%!
+%! t = uitable('Data', data, ...
+%!             'ColumnFormat', cf, ...
+%!             'ColumnWidth', widths, ...
+%!             'Units', 'Normalized', ...
+%!             'Position', [ 0 0 1 1], ...
+%!             'ColumnName', cn, ...
+%!             'ColumnEditable', true);
