view libinterp/corefcn/graphics.in.h @ 30253:20fd3c03fd74

Add new plot marker styles '|' and '_' (bug #61350) * NEWS: Announce addition. * gl-render.cc (make_marker_list): Add instructions for drawing '|' and '_' markers. * graphics.cc (radio_values::radio_values): Add special case in constructor from string of options to parse '|||' and return value '|'. * graphics.in.h (line, patch, scatter, surface): Add '|' and '_' to marker property for graphics objects which have this property. * __pltopt__.m: Add '|' and '_' to list of possible markers in input validation. * __gnuplot_draw_axes__.m: Add FIXME note about implementing these two markers in gnuplot. There doesn't seem to be an easy way to do this as it is not one of the defined point styles.
author Rik <rik@octave.org>
date Wed, 27 Oct 2021 15:07:52 -0700
parents 14b098a6ba46
children f306fe9bfa0d
line wrap: on
line source

////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2007-2021 The Octave Project Developers
//
// See the file COPYRIGHT.md in the top-level directory of this
// distribution or <https://octave.org/copyright/>.
//
// This file is part of Octave.
//
// Octave is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Octave is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Octave; see the file COPYING.  If not, see
// <https://www.gnu.org/licenses/>.
//
////////////////////////////////////////////////////////////////////////

#if ! defined (octave_graphics_h)
#define octave_graphics_h 1

#include "octave-config.h"

#include <cctype>
#include <cmath>

#include <algorithm>
#include <list>
#include <map>
#include <memory>
#include <set>
#include <sstream>
#include <string>
#include <vector>

#include "caseless-str.h"

#include "errwarn.h"
#include "graphics-handle.h"
#include "graphics-toolkit.h"
#include "oct-map.h"
#include "oct-mutex.h"
#include "oct-refcount.h"
#include "ov.h"
#include "text-renderer.h"

OCTAVE_NAMESPACE_BEGIN

// FIXME: maybe this should be a configure option?
// Matlab defaults to "Helvetica", but that causes problems for many
// gnuplot users.
#if ! defined (OCTAVE_DEFAULT_FONTNAME)
#define OCTAVE_DEFAULT_FONTNAME "*"
#endif

// ---------------------------------------------------------------------

class OCTINTERP_API base_scaler
{
public:
  base_scaler (void) { }

  virtual ~base_scaler (void) = default;

  virtual Matrix scale (const Matrix&) const
  {
    error ("invalid axis scale");
  }

  virtual NDArray scale (const NDArray&) const
  {
    error ("invalid axis scale");
  }

  virtual double scale (double) const
  {
    error ("invalid axis scale");
  }

  virtual double unscale (double) const
  {
    error ("invalid axis scale");
  }

  virtual base_scaler * clone () const
  { return new base_scaler (); }

  virtual bool is_linear (void) const
  { return false; }
};

class lin_scaler : public base_scaler
{
public:
  lin_scaler (void) { }

  Matrix scale (const Matrix& m) const { return m; }

  NDArray scale (const NDArray& m) const { return m; }

  double scale (double d) const { return d; }

  double unscale (double d) const { return d; }

  base_scaler * clone (void) const { return new lin_scaler (); }

  bool is_linear (void) const { return true; }
};

class log_scaler : public base_scaler
{
public:
  log_scaler (void) { }

  Matrix scale (const Matrix& m) const
  {
    Matrix retval (m.rows (), m.cols ());

    do_scale (m.data (), retval.fortran_vec (), m.numel ());

    return retval;
  }

  NDArray scale (const NDArray& m) const
  {
    NDArray retval (m.dims ());

    do_scale (m.data (), retval.fortran_vec (), m.numel ());

    return retval;
  }

  double scale (double d) const
  { return log10 (d); }

  double unscale (double d) const
  { return std::pow (10.0, d); }

  base_scaler * clone (void) const
  { return new log_scaler (); }

private:
  void do_scale (const double *src, double *dest, int n) const
  {
    for (int i = 0; i < n; i++)
      dest[i] = log10 (src[i]);
  }
};

class OCTINTERP_API neg_log_scaler : public base_scaler
{
public:
  neg_log_scaler (void) { }

  Matrix scale (const Matrix& m) const
  {
    Matrix retval (m.rows (), m.cols ());

    do_scale (m.data (), retval.fortran_vec (), m.numel ());

    return retval;
  }

  NDArray scale (const NDArray& m) const
  {
    NDArray retval (m.dims ());

    do_scale (m.data (), retval.fortran_vec (), m.numel ());

    return retval;
  }

  double scale (double d) const
  { return -log10 (-d); }

  double unscale (double d) const
  { return -std::pow (10.0, -d); }

  base_scaler * clone (void) const
  { return new neg_log_scaler (); }

private:
  void do_scale (const double *src, double *dest, int n) const
  {
    for (int i = 0; i < n; i++)
      dest[i] = -log10 (-src[i]);
  }
};

class OCTINTERP_API scaler
{
public:
  scaler (void) : m_rep (new base_scaler ()) { }

  scaler (const scaler& s) : m_rep (s.m_rep->clone ()) { }

  scaler (const std::string& s)
    : m_rep (s == "log"
             ? new log_scaler ()
             : (s == "neglog"
                ? new neg_log_scaler ()
                : (s == "linear"
                   ? new lin_scaler ()
                   : new base_scaler ())))
  { }

  ~scaler (void) { delete m_rep; }

  Matrix scale (const Matrix& m) const
  { return m_rep->scale (m); }

  NDArray scale (const NDArray& m) const
  { return m_rep->scale (m); }

  double scale (double d) const
  { return m_rep->scale (d); }

  double unscale (double d) const
  { return m_rep->unscale (d); }

  bool is_linear (void) const
  { return m_rep->is_linear (); }

  scaler& operator = (const scaler& s)
  {
    if (&s != this)
      {
        if (m_rep)
          {
            delete m_rep;
            m_rep = nullptr;
          }

        m_rep = s.m_rep->clone ();
      }

    return *this;
  }

  scaler& operator = (const std::string& s)
  {
    if (m_rep)
      {
        delete m_rep;
        m_rep = nullptr;
      }

    if (s == "log")
      m_rep = new log_scaler ();
    else if (s == "neglog")
      m_rep = new neg_log_scaler ();
    else if (s == "linear")
      m_rep = new lin_scaler ();
    else
      m_rep = new base_scaler ();

    return *this;
  }

private:
  base_scaler *m_rep;
};

// ---------------------------------------------------------------------

class OCTINTERP_API property;

// FIXME: These values should probably be defined inside a namespace or
// class, but which one is most appropriate?  For now, prefix with
// "GCB_" to avoid conflict with PERSISTENT token ID used in the lexer.
// The lexer token IDs should probably also be fixed...

enum listener_mode { GCB_POSTSET, GCB_PERSISTENT, GCB_PREDELETE };

class OCTINTERP_API base_property
{
public:
  friend class property;

public:
  base_property (void)
    : m_id (-1), m_count (1), m_name (), m_parent (), m_hidden (),
      m_listeners ()
  { }

  base_property (const std::string& s, const graphics_handle& h)
    : m_id (-1), m_count (1), m_name (s), m_parent (h), m_hidden (false),
      m_listeners ()
  { }

  base_property (const base_property& p)
    : m_id (-1), m_count (1), m_name (p.m_name), m_parent (p.m_parent),
      m_hidden (p.m_hidden), m_listeners ()
  { }

  virtual ~base_property (void) = default;

  bool ok (void) const { return m_parent.ok (); }

  std::string get_name (void) const { return m_name; }

  void set_name (const std::string& s) { m_name = s; }

  graphics_handle get_parent (void) const { return m_parent; }

  void set_parent (const graphics_handle& h) { m_parent = h; }

  bool is_hidden (void) const { return m_hidden; }

  void set_hidden (bool flag) { m_hidden = flag; }

  virtual bool is_radio (void) const { return false; }

  int get_id (void) const { return m_id; }

  void set_id (int d) { m_id = d; }

  // Sets property value, notifies graphics toolkit.
  // If do_run is true, runs associated listeners.
  OCTINTERP_API bool set (const octave_value& v, bool do_run = true,
                          bool do_notify_toolkit = true);

  virtual octave_value get (void) const
  {
    error (R"(get: invalid property "%s")", m_name.c_str ());
  }

  virtual std::string values_as_string (void) const
  {
    error (R"(values_as_string: invalid property "%s")", m_name.c_str ());
  }

  virtual Cell values_as_cell (void) const
  {
    error (R"(values_as_cell: invalid property "%s")", m_name.c_str ());
  }

  base_property& operator = (const octave_value& val)
  {
    set (val);
    return *this;
  }

  void add_listener (const octave_value& v, listener_mode mode = GCB_POSTSET)
  {
    octave_value_list& l = m_listeners[mode];
    l.resize (l.length () + 1, v);
  }

  void delete_listener (const octave_value& v = octave_value (),
                        listener_mode mode = GCB_POSTSET)
  {
    octave_value_list& l = m_listeners[mode];

    if (v.is_defined ())
      {
        bool found = false;
        int i;

        for (i = 0; i < l.length (); i++)
          {
            if (v.internal_rep () == l(i).internal_rep ())
              {
                found = true;
                break;
              }
          }
        if (found)
          {
            for (int j = i; j < l.length () - 1; j++)
              l(j) = l(j + 1);

            l.resize (l.length () - 1);
          }
      }
    else
      {
        if (mode == GCB_PERSISTENT)
          l.resize (0);
        else
          {
            octave_value_list lnew (0);
            octave_value_list& lp = m_listeners[GCB_PERSISTENT];
            for (int i = l.length () - 1; i >= 0 ; i--)
              {
                for (int j = 0; j < lp.length (); j++)
                  {
                    if (l(i).internal_rep () == lp(j).internal_rep ())
                      {
                        lnew.resize (lnew.length () + 1, l(i));
                        break;
                      }
                  }
              }
            l = lnew;
          }
      }

  }

  OCTINTERP_API void run_listeners (listener_mode mode = GCB_POSTSET);

  virtual base_property * clone (void) const
  { return new base_property (*this); }

protected:
  virtual bool do_set (const octave_value&)
  {
    error (R"(set: invalid property "%s")", m_name.c_str ());
  }

private:
  typedef std::map<listener_mode, octave_value_list> listener_map;
  typedef std::map<listener_mode, octave_value_list>::iterator
    listener_map_iterator;
  typedef std::map<listener_mode, octave_value_list>::const_iterator
    listener_map_const_iterator;

private:
  int m_id;
  octave::refcount<octave_idx_type> m_count;
  std::string m_name;
  graphics_handle m_parent;
  bool m_hidden;
  listener_map m_listeners;
};

// ---------------------------------------------------------------------

class OCTINTERP_API string_property : public base_property
{
public:
  string_property (const std::string& s, const graphics_handle& h,
                   const std::string& val = "")
    : base_property (s, h), m_str (val) { }

  string_property (const string_property& p)
    : base_property (p), m_str (p.m_str) { }

  octave_value get (void) const
  { return octave_value (m_str); }

  std::string string_value (void) const { return m_str; }

  string_property& operator = (const octave_value& val)
  {
    set (val);
    return *this;
  }

  base_property * clone (void) const { return new string_property (*this); }

protected:
  bool do_set (const octave_value& val)
  {
    if (! val.is_string ())
      error (R"(set: invalid string property value for "%s")",
             get_name ().c_str ());

    std::string new_str = val.string_value ();

    if (new_str != m_str)
      {
        m_str = new_str;
        return true;
      }
    return false;
  }

private:
  std::string m_str;
};

// ---------------------------------------------------------------------

class OCTINTERP_API string_array_property : public base_property
{
public:
  enum desired_enum { string_t, cell_t };

  string_array_property (const std::string& s, const graphics_handle& h,
                         const std::string& val = "", const char& sep = '|',
                         const desired_enum& typ = string_t)
    : base_property (s, h), m_desired_type (typ), m_separator (sep), m_str ()
  {
    std::size_t pos = 0;

    while (true)
      {
        std::size_t new_pos = val.find_first_of (m_separator, pos);

        if (new_pos == std::string::npos)
          {
            m_str.append (val.substr (pos));
            break;
          }
        else
          m_str.append (val.substr (pos, new_pos - pos));

        pos = new_pos + 1;
      }
  }

  string_array_property (const std::string& s, const graphics_handle& h,
                         const Cell& c, const char& sep = '|',
                         const desired_enum& typ = string_t)
    : base_property (s, h), m_desired_type (typ), m_separator (sep), m_str ()
  {
    if (! c.iscellstr ())
      error (R"(set: invalid order property value for "%s")",
             get_name ().c_str ());

    string_vector strings (c.numel ());

    for (octave_idx_type i = 0; i < c.numel (); i++)
      strings[i] = c(i).string_value ();

    m_str = strings;
  }

  string_array_property (const string_array_property& p)
    : base_property (p), m_desired_type (p.m_desired_type),
      m_separator (p.m_separator), m_str (p.m_str) { }

  octave_value get (void) const
  {
    if (m_desired_type == string_t)
      return octave_value (string_value ());
    else
      return octave_value (cell_value ());
  }

  std::string string_value (void) const
  {
    std::string s;

    for (octave_idx_type i = 0; i < m_str.numel (); i++)
      {
        s += m_str[i];
        if (i != m_str.numel () - 1)
          s += m_separator;
      }

    return s;
  }

  Cell cell_value (void) const {return Cell (m_str);}

  string_vector string_vector_value (void) const { return m_str; }

  string_array_property& operator = (const octave_value& val)
  {
    set (val);
    return *this;
  }

  base_property * clone (void) const
  { return new string_array_property (*this); }

protected:
  bool do_set (const octave_value& val)
  {
    if (val.is_string () && val.rows () == 1)
      {
        bool replace = false;
        std::string new_str = val.string_value ();
        string_vector strings;
        std::size_t pos = 0;

        // Split single string on delimiter (usually '|')
        while (pos != std::string::npos)
          {
            std::size_t new_pos = new_str.find_first_of (m_separator, pos);

            if (new_pos == std::string::npos)
              {
                strings.append (new_str.substr (pos));
                break;
              }
            else
              strings.append (new_str.substr (pos, new_pos - pos));

            pos = new_pos + 1;
          }

        if (m_str.numel () == strings.numel ())
          {
            for (octave_idx_type i = 0; i < m_str.numel (); i++)
              if (strings[i] != m_str[i])
                {
                  replace = true;
                  break;
                }
          }
        else
          replace = true;

        m_desired_type = string_t;

        if (replace)
          {
            m_str = strings;
            return true;
          }
      }
    else if (val.is_string ())  // multi-row character matrix
      {
        bool replace = false;
        charMatrix chm = val.char_matrix_value ();
        octave_idx_type nel = chm.rows ();
        string_vector strings (nel);

        if (nel != m_str.numel ())
          replace = true;
        for (octave_idx_type i = 0; i < nel; i++)
          {
            strings[i] = chm.row_as_string (i);
            if (! replace && strings[i] != m_str[i])
              replace = true;
          }

        m_desired_type = string_t;

        if (replace)
          {
            m_str = strings;
            return true;
          }
      }
    else if (val.iscellstr ())
      {
        bool replace = false;
        Cell new_cell = val.cell_value ();

        string_vector strings = new_cell.cellstr_value ();

        octave_idx_type nel = strings.numel ();

        if (nel != m_str.numel ())
          replace = true;
        else
          {
            for (octave_idx_type i = 0; i < nel; i++)
              {
                if (strings[i] != m_str[i])
                  {
                    replace = true;
                    break;
                  }
              }
          }

        m_desired_type = cell_t;

        if (replace)
          {
            m_str = strings;
            return true;
          }
      }
    else
      error (R"(set: invalid string property value for "%s")",
             get_name ().c_str ());

    return false;
  }

private:
  desired_enum m_desired_type;
  char m_separator;
  string_vector m_str;
};

// ---------------------------------------------------------------------

class OCTINTERP_API text_label_property : public base_property
{
public:
  enum type { char_t, cellstr_t };

  text_label_property (const std::string& s, const graphics_handle& h,
                       const std::string& val = "")
    : base_property (s, h), m_value (val), m_stored_type (char_t)
  { }

  text_label_property (const std::string& s, const graphics_handle& h,
                       const NDArray& nda)
    : base_property (s, h), m_stored_type (char_t)
  {
    octave_idx_type nel = nda.numel ();

    m_value.resize (nel);

    for (octave_idx_type i = 0; i < nel; i++)
      {
        std::ostringstream buf;
        buf << nda(i);
        m_value[i] = buf.str ();
      }
  }

  text_label_property (const std::string& s, const graphics_handle& h,
                       const Cell& c)
    : base_property (s, h), m_stored_type (cellstr_t)
  {
    octave_idx_type nel = c.numel ();

    m_value.resize (nel);

    for (octave_idx_type i = 0; i < nel; i++)
      {
        octave_value tmp = c(i);

        if (tmp.is_string ())
          m_value[i] = c(i).string_value ();
        else
          {
            double d = c(i).double_value ();

            std::ostringstream buf;
            buf << d;
            m_value[i] = buf.str ();
          }
      }
  }

  text_label_property (const text_label_property& p)
    : base_property (p), m_value (p.m_value), m_stored_type (p.m_stored_type)
  { }

  bool empty (void) const
  {
    octave_value tmp = get ();
    return tmp.isempty ();
  }

  octave_value get (void) const
  {
    if (m_stored_type == char_t)
      return octave_value (char_value ());
    else
      return octave_value (cell_value ());
  }

  std::string string_value (void) const
  {
    return m_value.empty () ? "" : m_value[0];
  }

  string_vector string_vector_value (void) const { return m_value; }

  charMatrix char_value (void) const { return charMatrix (m_value, ' '); }

  Cell cell_value (void) const {return Cell (m_value); }

  text_label_property& operator = (const octave_value& val)
  {
    set (val);
    return *this;
  }

  base_property * clone (void) const { return new text_label_property (*this); }

protected:

  bool do_set (const octave_value& val)
  {
    if (val.is_string ())
      {
        m_value = val.string_vector_value ();

        m_stored_type = char_t;
      }
    else if (val.iscell ())
      {
        Cell c = val.cell_value ();

        octave_idx_type nel = c.numel ();

        m_value.resize (nel);

        for (octave_idx_type i = 0; i < nel; i++)
          {
            octave_value tmp = c(i);

            if (tmp.is_string ())
              m_value[i] = c(i).string_value ();
            else
              {
                double d = c(i).double_value ();

                std::ostringstream buf;
                buf << d;
                m_value[i] = buf.str ();
              }
          }

        m_stored_type = cellstr_t;
      }
    else
      {
        NDArray nda;

        try
          {
            nda = val.array_value ();
          }
        catch (octave::execution_exception& ee)
          {
            error (ee, R"(set: invalid string property value for "%s")",
                   get_name ().c_str ());
          }

        octave_idx_type nel = nda.numel ();

        m_value.resize (nel);

        for (octave_idx_type i = 0; i < nel; i++)
          {
            std::ostringstream buf;
            buf << nda(i);
            m_value[i] = buf.str ();
          }

        m_stored_type = char_t;
      }

    return true;
  }

private:
  string_vector m_value;
  type m_stored_type;
};

// ---------------------------------------------------------------------

class OCTINTERP_API radio_values
{
public:
  OCTINTERP_API radio_values (const std::string& opt_string = "");

  radio_values (const radio_values& a)
    : m_default_val (a.m_default_val), m_possible_vals (a.m_possible_vals) { }

  radio_values& operator = (const radio_values& a)
  {
    if (&a != this)
      {
        m_default_val = a.m_default_val;
        m_possible_vals = a.m_possible_vals;
      }

    return *this;
  }

  std::string default_value (void) const { return m_default_val; }

  bool validate (const std::string& val, std::string& match)
  {
    bool retval = true;

    if (! contains (val, match))
      error ("invalid value = %s", val.c_str ());

    return retval;
  }

  bool contains (const std::string& val, std::string& match)
  {
    std::size_t k = 0;

    std::size_t len = val.length ();

    std::string first_match;

    for (const auto& possible_val : m_possible_vals)
      {
        if (possible_val.compare (val, len))
          {
            if (len == possible_val.length ())
              {
                // We found a full match (consider the case of val == "replace"
                // with possible values "replace" and "replacechildren").  Any
                // other matches are irrelevant, so set match and return now.
                match = possible_val;
                return true;
              }
            else
              {
                if (k == 0)
                  first_match = possible_val;

                k++;
              }
          }
      }

    if (k == 1)
      {
        match = first_match;
        return true;
      }
    else
      return false;
  }

  OCTINTERP_API std::string values_as_string (void) const;

  OCTINTERP_API Cell values_as_cell (void) const;

  octave_idx_type nelem (void) const { return m_possible_vals.size (); }

private:
  // Might also want to cache
  std::string m_default_val;
  std::set<caseless_str> m_possible_vals;
};

class OCTINTERP_API radio_property : public base_property
{
public:
  radio_property (const std::string& nm, const graphics_handle& h,
                  const radio_values& v = radio_values ())
    : base_property (nm, h),
      m_vals (v), m_current_val (v.default_value ()) { }

  radio_property (const std::string& nm, const graphics_handle& h,
                  const std::string& v)
    : base_property (nm, h),
      m_vals (v), m_current_val (m_vals.default_value ()) { }

  radio_property (const std::string& nm, const graphics_handle& h,
                  const radio_values& v, const std::string& def)
    : base_property (nm, h),
      m_vals (v), m_current_val (def) { }

  radio_property (const radio_property& p)
    : base_property (p), m_vals (p.m_vals), m_current_val (p.m_current_val) { }

  octave_value get (void) const { return octave_value (m_current_val); }

  const std::string& current_value (void) const { return m_current_val; }

  std::string values_as_string (void) const { return m_vals.values_as_string (); }

  Cell values_as_cell (void) const { return m_vals.values_as_cell (); }

  bool is (const caseless_str& v) const
  { return v.compare (m_current_val); }

  bool is_radio (void) const { return true; }

  radio_property& operator = (const octave_value& val)
  {
    set (val);
    return *this;
  }

  base_property * clone (void) const { return new radio_property (*this); }

protected:
  bool do_set (const octave_value& newval)
  {
    if (! newval.is_string ())
      error (R"(set: invalid value for radio property "%s")",
             get_name ().c_str ());

    std::string s = newval.string_value ();

    std::string match;

    if (! m_vals.validate (s, match))
      error (R"(set: invalid value for radio property "%s" (value = %s))",
             get_name ().c_str (), s.c_str ());

    if (match != m_current_val)
      {
        if (s.length () != match.length ())
          warning_with_id ("Octave:abbreviated-property-match",
                           "%s: allowing %s to match %s value %s",
                           "set", s.c_str (), get_name ().c_str (),
                           match.c_str ());
        m_current_val = match;
        return true;
      }
    return false;
  }

private:
  radio_values m_vals;
  std::string m_current_val;
};

// ---------------------------------------------------------------------

class OCTINTERP_API color_values
{
public:
  color_values (double r = 0, double g = 0, double b = 1)
    : m_rgb (1, 3)
  {
    m_rgb(0) = r;
    m_rgb(1) = g;
    m_rgb(2) = b;

    validate ();
  }

  color_values (const std::string& str)
    : m_rgb (1, 3)
  {
    if (! str2rgb (str))
      error ("invalid color specification: %s", str.c_str ());
  }

  color_values (const color_values& c)
    : m_rgb (c.m_rgb)
  { }

  color_values& operator = (const color_values& c)
  {
    if (&c != this)
      m_rgb = c.m_rgb;

    return *this;
  }

  bool operator == (const color_values& c) const
  {
    return (m_rgb(0) == c.m_rgb(0)
            && m_rgb(1) == c.m_rgb(1)
            && m_rgb(2) == c.m_rgb(2));
  }

  bool operator != (const color_values& c) const
  { return ! (*this == c); }

  Matrix rgb (void) const { return m_rgb; }

  operator octave_value (void) const { return m_rgb; }

  void validate (void) const
  {
    for (int i = 0; i < 3; i++)
      {
        if (m_rgb(i) < 0 ||  m_rgb(i) > 1)
          error ("invalid RGB color specification");
      }
  }

private:
  Matrix m_rgb;

  OCTINTERP_API bool str2rgb (const std::string& str);
};

class OCTINTERP_API color_property : public base_property
{
public:
  color_property (const color_values& c, const radio_values& v)
    : base_property ("", graphics_handle ()),
      m_current_type (color_t), m_color_val (c), m_radio_val (v),
      m_current_val (v.default_value ())
  { }

  color_property (const radio_values& v, const color_values& c)
    : base_property ("", graphics_handle ()),
      m_current_type (radio_t), m_color_val (c), m_radio_val (v),
      m_current_val (v.default_value ())
  { }

  color_property (const std::string& nm, const graphics_handle& h,
                  const color_values& c = color_values (),
                  const radio_values& v = radio_values ())
    : base_property (nm, h),
      m_current_type (color_t), m_color_val (c), m_radio_val (v),
      m_current_val (v.default_value ())
  { }

  color_property (const std::string& nm, const graphics_handle& h,
                  const radio_values& v)
    : base_property (nm, h),
      m_current_type (radio_t), m_color_val (color_values ()), m_radio_val (v),
      m_current_val (v.default_value ())
  { }

  color_property (const std::string& nm, const graphics_handle& h,
                  const std::string& v)
    : base_property (nm, h),
      m_current_type (radio_t), m_color_val (color_values ()), m_radio_val (v),
      m_current_val (m_radio_val.default_value ())
  { }

  color_property (const std::string& nm, const graphics_handle& h,
                  const color_property& v)
    : base_property (nm, h),
      m_current_type (v.m_current_type), m_color_val (v.m_color_val),
      m_radio_val (v.m_radio_val), m_current_val (v.m_current_val)
  { }

  color_property (const color_property& p)
    : base_property (p), m_current_type (p.m_current_type),
      m_color_val (p.m_color_val), m_radio_val (p.m_radio_val),
      m_current_val (p.m_current_val) { }

  octave_value get (void) const
  {
    if (m_current_type == color_t)
      return m_color_val.rgb ();

    return m_current_val;
  }

  bool is_rgb (void) const { return (m_current_type == color_t); }

  bool is_radio (void) const { return (m_current_type == radio_t); }

  bool is (const std::string& v) const
  { return (is_radio () && m_current_val == v); }

  Matrix rgb (void) const
  {
    if (m_current_type != color_t)
      error ("color has no RGB value");

    return m_color_val.rgb ();
  }

  const std::string& current_value (void) const
  {
    if (m_current_type != radio_t)
      error ("color has no radio value");

    return m_current_val;
  }

  color_property& operator = (const octave_value& val)
  {
    set (val);
    return *this;
  }

  operator octave_value (void) const { return get (); }

  base_property * clone (void) const { return new color_property (*this); }

  std::string values_as_string (void) const
  { return m_radio_val.values_as_string (); }

  Cell values_as_cell (void) const { return m_radio_val.values_as_cell (); }

protected:
  OCTINTERP_API bool do_set (const octave_value& newval);

private:
  enum current_enum { color_t, radio_t } m_current_type;
  color_values m_color_val;
  radio_values m_radio_val;
  std::string m_current_val;
};

// ---------------------------------------------------------------------

enum finite_type
{
  NO_CHECK,
  FINITE,
  NOT_NAN,
  NOT_INF
};

class OCTINTERP_API double_property : public base_property
{
public:
  double_property (const std::string& nm, const graphics_handle& h,
                   double d = 0)
    : base_property (nm, h),
      m_current_val (d), m_finite_constraint (NO_CHECK),
      m_minval (std::pair<double, bool> (octave_NaN, true)),
      m_maxval (std::pair<double, bool> (octave_NaN, true)) { }

  double_property (const double_property& p)
    : base_property (p), m_current_val (p.m_current_val),
      m_finite_constraint (NO_CHECK),
      m_minval (std::pair<double, bool> (octave_NaN, true)),
      m_maxval (std::pair<double, bool> (octave_NaN, true)) { }

  octave_value get (void) const { return octave_value (m_current_val); }

  double double_value (void) const { return m_current_val; }

  double_property& operator = (const octave_value& val)
  {
    set (val);
    return *this;
  }

  base_property * clone (void) const
  {
    double_property *p = new double_property (*this);

    p->m_finite_constraint = m_finite_constraint;
    p->m_minval = m_minval;
    p->m_maxval = m_maxval;

    return p;
  }

  void add_constraint (const std::string& type, double val, bool inclusive)
  {
    if (type == "min")
      m_minval = std::pair<double, bool> (val, inclusive);
    else if (type == "max")
      m_maxval = std::pair<double, bool> (val, inclusive);
  }

  void add_constraint (const finite_type finite)
  { m_finite_constraint = finite; }

protected:
  bool do_set (const octave_value& v)
  {
    if (! v.is_scalar_type () || ! v.isreal ())
      error (R"(set: invalid value for double property "%s")",
             get_name ().c_str ());

    double new_val = v.double_value ();

    // Check min and max
    if (! octave::math::isnan (m_minval.first))
      {
        if (m_minval.second && m_minval.first > new_val)
          error (R"(set: "%s" must be greater than or equal to %g)",
                 get_name ().c_str (), m_minval.first);
        else if (! m_minval.second && m_minval.first >= new_val)
          error (R"(set: "%s" must be greater than %g)",
                 get_name ().c_str (), m_minval.first);
      }

    if (! octave::math::isnan (m_maxval.first))
      {
        if (m_maxval.second && m_maxval.first < new_val)
          error (R"(set: "%s" must be less than or equal to %g)",
                 get_name ().c_str (), m_maxval.first);
        else if (! m_maxval.second && m_maxval.first <= new_val)
          error (R"(set: "%s" must be less than %g)",
                 get_name ().c_str (), m_maxval.first);
      }

    if (m_finite_constraint == NO_CHECK) { /* do nothing */ }
    else if (m_finite_constraint == FINITE)
      {
        if (! octave::math::isfinite (new_val))
          error (R"(set: "%s" must be finite)", get_name ().c_str ());
      }
    else if (m_finite_constraint == NOT_NAN)
      {
        if (octave::math::isnan (new_val))
          error (R"(set: "%s" must not be nan)", get_name ().c_str ());
      }
    else if (m_finite_constraint == NOT_INF)
      {
        if (octave::math::isinf (new_val))
          error (R"(set: "%s" must not be infinite)", get_name ().c_str ());
      }

    if (new_val != m_current_val)
      {
        m_current_val = new_val;
        return true;
      }

    return false;
  }

private:
  double m_current_val;
  finite_type m_finite_constraint;
  std::pair<double, bool> m_minval, m_maxval;
};

// ---------------------------------------------------------------------

class OCTINTERP_API double_radio_property : public base_property
{
public:
  double_radio_property (double d, const radio_values& v)
    : base_property ("", graphics_handle ()),
      m_current_type (double_t), m_dval (d), m_radio_val (v),
      m_current_val (v.default_value ())
  { }

  double_radio_property (const std::string& nm, const graphics_handle& h,
                         const std::string& v)
    : base_property (nm, h),
      m_current_type (radio_t), m_dval (0), m_radio_val (v),
      m_current_val (m_radio_val.default_value ())
  { }

  double_radio_property (const std::string& nm, const graphics_handle& h,
                         const double_radio_property& v)
    : base_property (nm, h),
      m_current_type (v.m_current_type), m_dval (v.m_dval),
      m_radio_val (v.m_radio_val), m_current_val (v.m_current_val)
  { }

  double_radio_property (const double_radio_property& p)
    : base_property (p), m_current_type (p.m_current_type),
      m_dval (p.m_dval), m_radio_val (p.m_radio_val),
      m_current_val (p.m_current_val) { }

  octave_value get (void) const
  {
    if (m_current_type == double_t)
      return m_dval;

    return m_current_val;
  }

  bool is_double (void) const { return (m_current_type == double_t); }

  bool is_radio (void) const { return (m_current_type == radio_t); }

  bool is (const std::string& v) const
  { return (is_radio () && m_current_val == v); }

  double double_value (void) const
  {
    if (m_current_type != double_t)
      error ("%s: property has no double", get_name ().c_str ());

    return m_dval;
  }

  const std::string& current_value (void) const
  {
    if (m_current_type != radio_t)
      error ("%s: property has no radio value", get_name ().c_str ());

    return m_current_val;
  }

  double_radio_property& operator = (const octave_value& val)
  {
    set (val);
    return *this;
  }

  operator octave_value (void) const { return get (); }

  base_property * clone (void) const
  { return new double_radio_property (*this); }

protected:
  OCTINTERP_API bool do_set (const octave_value& v);

private:
  enum current_enum { double_t, radio_t } m_current_type;
  double m_dval;
  radio_values m_radio_val;
  std::string m_current_val;
};

// ---------------------------------------------------------------------

class OCTINTERP_API array_property : public base_property
{
public:
  array_property (void)
    : base_property ("", graphics_handle ()), m_data (Matrix ()),
      m_min_val (), m_max_val (), m_min_pos (), m_max_neg (),
      m_type_constraints (), m_size_constraints (), m_finite_constraint (NO_CHECK),
      m_minval (std::pair<double, bool> (octave_NaN, true)),
      m_maxval (std::pair<double, bool> (octave_NaN, true))
  {
    get_data_limits ();
  }

  array_property (const std::string& nm, const graphics_handle& h,
                  const octave_value& m)
    : base_property (nm, h), m_data (m.issparse () ? m.full_value () : m),
      m_min_val (), m_max_val (), m_min_pos (), m_max_neg (),
      m_type_constraints (), m_size_constraints (), m_finite_constraint (NO_CHECK),
      m_minval (std::pair<double, bool> (octave_NaN, true)),
      m_maxval (std::pair<double, bool> (octave_NaN, true))
  {
    get_data_limits ();
  }

  // This copy constructor is only intended to be used
  // internally to access min/max values; no need to
  // copy constraints.
  array_property (const array_property& p)
    : base_property (p), m_data (p.m_data),
      m_min_val (p.m_min_val), m_max_val (p.m_max_val), m_min_pos (p.m_min_pos), m_max_neg (p.m_max_neg),
      m_type_constraints (), m_size_constraints (), m_finite_constraint (NO_CHECK),
      m_minval (std::pair<double, bool> (octave_NaN, true)),
      m_maxval (std::pair<double, bool> (octave_NaN, true))
  { }

  octave_value get (void) const { return m_data; }

  void add_constraint (const std::string& type)
  { m_type_constraints.insert (type); }

  void add_constraint (const dim_vector& dims)
  { m_size_constraints.push_back (dims); }

  void add_constraint (const finite_type finite)
  { m_finite_constraint = finite; }

  void add_constraint (const std::string& type, double val, bool inclusive)
  {
    if (type == "min")
      m_minval = std::pair<double, bool> (val, inclusive);
    else if (type == "max")
      m_maxval = std::pair<double, bool> (val, inclusive);
  }

  double min_val (void) const { return m_min_val; }
  double max_val (void) const { return m_max_val; }
  double min_pos (void) const { return m_min_pos; }
  double max_neg (void) const { return m_max_neg; }

  Matrix get_limits (void) const
  {
    Matrix m (1, 4);

    m(0) = min_val ();
    m(1) = max_val ();
    m(2) = min_pos ();
    m(3) = max_neg ();

    return m;
  }

  array_property& operator = (const octave_value& val)
  {
    set (val);
    return *this;
  }

  base_property * clone (void) const
  {
    array_property *p = new array_property (*this);

    p->m_type_constraints = m_type_constraints;
    p->m_size_constraints = m_size_constraints;
    p->m_finite_constraint = m_finite_constraint;
    p->m_minval = m_minval;
    p->m_maxval = m_maxval;

    return p;
  }

protected:
  bool do_set (const octave_value& v)
  {
    octave_value tmp = (v.issparse () ? v.full_value () : v);

    if (! validate (tmp))
      error (R"(invalid value for array property "%s")",
             get_name ().c_str ());

    // FIXME: should we check for actual data change?
    if (! is_equal (tmp))
      {
        m_data = tmp;

        get_data_limits ();

        return true;
      }

    return false;
  }

private:
  OCTINTERP_API bool validate (const octave_value& v);

  OCTINTERP_API bool is_equal (const octave_value& v) const;

  OCTINTERP_API void get_data_limits (void);

protected:
  octave_value m_data;
  double m_min_val;
  double m_max_val;
  double m_min_pos;
  double m_max_neg;
  std::set<std::string> m_type_constraints;
  std::list<dim_vector> m_size_constraints;
  finite_type m_finite_constraint;
  std::pair<double, bool> m_minval, m_maxval;
};

class OCTINTERP_API row_vector_property : public array_property
{
public:
  row_vector_property (const std::string& nm, const graphics_handle& h,
                       const octave_value& m)
    : array_property (nm, h, m)
  {
    add_constraint (dim_vector (-1, 1));
    add_constraint (dim_vector (1, -1));
    add_constraint (dim_vector (0, 0));
  }

  row_vector_property (const row_vector_property& p)
    : array_property (p)
  {
    add_constraint (dim_vector (-1, 1));
    add_constraint (dim_vector (1, -1));
    add_constraint (dim_vector (0, 0));
  }

  void add_constraint (const std::string& type)
  {
    array_property::add_constraint (type);
  }

  void add_constraint (const dim_vector& dims)
  {
    array_property::add_constraint (dims);
  }

  void add_constraint (const finite_type finite)
  {
    array_property::add_constraint (finite);
  }

  void add_constraint (const std::string& type, double val, bool inclusive)
  {
    array_property::add_constraint (type, val, inclusive);
  }

  void add_constraint (octave_idx_type len)
  {
    m_size_constraints.remove (dim_vector (1, -1));
    m_size_constraints.remove (dim_vector (-1, 1));
    m_size_constraints.remove (dim_vector (0, 0));

    add_constraint (dim_vector (1, len));
    add_constraint (dim_vector (len, 1));
  }

  row_vector_property& operator = (const octave_value& val)
  {
    set (val);
    return *this;
  }

  base_property * clone (void) const
  {
    row_vector_property *p = new row_vector_property (*this);

    p->m_type_constraints = m_type_constraints;
    p->m_size_constraints = m_size_constraints;
    p->m_finite_constraint = m_finite_constraint;
    p->m_minval = m_minval;
    p->m_maxval = m_maxval;

    return p;
  }

protected:
  bool do_set (const octave_value& v)
  {
    bool retval = array_property::do_set (v);

    dim_vector dv = m_data.dims ();

    if (dv(0) > 1 && dv(1) == 1)
      {
        int tmp = dv(0);
        dv(0) = dv(1);
        dv(1) = tmp;

        m_data = m_data.reshape (dv);
      }

    return retval;
  }

private:
  OCTINTERP_API bool validate (const octave_value& v);
};

// ---------------------------------------------------------------------

class OCTINTERP_API bool_property : public radio_property
{
public:
  bool_property (const std::string& nm, const graphics_handle& h,
                 bool val)
    : radio_property (nm, h, radio_values (val ? "{on}|off" : "on|{off}"))
  { }

  bool_property (const std::string& nm, const graphics_handle& h,
                 const char *val)
    : radio_property (nm, h, radio_values (std::string (val) == "on" ?
                                           "{on}|off" : "on|{off}"), val)
  { }

  bool_property (const bool_property& p)
    : radio_property (p) { }

  bool is_on (void) const { return is ("on"); }

  bool_property& operator = (const octave_value& val)
  {
    set (val);
    return *this;
  }

  base_property * clone (void) const { return new bool_property (*this); }

protected:
  bool do_set (const octave_value& val)
  {
    if (val.is_bool_scalar ())
      return radio_property::do_set (val.bool_value () ? "on" : "off");
    else
      return radio_property::do_set (val);
  }
};

// ---------------------------------------------------------------------

class OCTINTERP_API handle_property : public base_property
{
public:
  handle_property (const std::string& nm, const graphics_handle& h,
                   const graphics_handle& val = graphics_handle ())
    : base_property (nm, h),
      m_current_val (val) { }

  handle_property (const handle_property& p)
    : base_property (p), m_current_val (p.m_current_val) { }

  octave_value get (void) const { return m_current_val.as_octave_value (); }

  graphics_handle handle_value (void) const { return m_current_val; }

  handle_property& operator = (const octave_value& val)
  {
    set (val);
    return *this;
  }

  handle_property& operator = (const graphics_handle& h)
  {
    set (octave_value (h.value ()));
    return *this;
  }

  void invalidate (void)
  { m_current_val = octave::numeric_limits<double>::NaN (); }

  base_property * clone (void) const { return new handle_property (*this); }

  void add_constraint (const std::string& type)
  { m_type_constraints.insert (type); }

protected:
  OCTINTERP_API bool do_set (const octave_value& v);
  std::set<std::string> m_type_constraints;

private:
  graphics_handle m_current_val;
};

// ---------------------------------------------------------------------

class OCTINTERP_API any_property : public base_property
{
public:
  any_property (const std::string& nm, const graphics_handle& h,
                const octave_value& m = Matrix ())
    : base_property (nm, h), m_data (m) { }

  any_property (const any_property& p)
    : base_property (p), m_data (p.m_data) { }

  octave_value get (void) const { return m_data; }

  any_property& operator = (const octave_value& val)
  {
    set (val);
    return *this;
  }

  base_property * clone (void) const { return new any_property (*this); }

protected:
  bool do_set (const octave_value& v)
  {
    m_data = v;
    return true;
  }

private:
  octave_value m_data;
};

// ---------------------------------------------------------------------

class OCTINTERP_API children_property : public base_property
{
public:
  children_property (void)
    : base_property ("", graphics_handle ()), m_children_list ()
  {
    do_init_children (Matrix ());
  }

  children_property (const std::string& nm, const graphics_handle& h,
                     const Matrix& val)
    : base_property (nm, h), m_children_list ()
  {
    do_init_children (val);
  }

  children_property (const children_property& p)
    : base_property (p), m_children_list ()
  {
    do_init_children (p.m_children_list);
  }

  children_property& operator = (const octave_value& val)
  {
    set (val);
    return *this;
  }

  base_property * clone (void) const { return new children_property (*this); }

  bool remove_child (double val)
  {
    return do_remove_child (val);
  }

  void adopt (double val)
  {
    do_adopt_child (val);
  }

  Matrix get_children (void) const
  {
    return do_get_children (false);
  }

  Matrix get_hidden (void) const
  {
    return do_get_children (true);
  }

  Matrix get_all (void) const
  {
    return do_get_all_children ();
  }

  octave_value get (void) const
  {
    return octave_value (get_children ());
  }

  void delete_children (bool clear = false, bool from_root = false)
  {
    do_delete_children (clear, from_root);
  }

  void renumber (graphics_handle old_gh, graphics_handle new_gh)
  {
    for (auto& hchild : m_children_list)
      {
        if (hchild == old_gh)
          {
            hchild = new_gh.value ();
            return;
          }
      }

    error ("children_list::renumber: child not found!");
  }

private:
  typedef std::list<double>::iterator children_list_iterator;
  typedef std::list<double>::const_iterator const_children_list_iterator;
  std::list<double> m_children_list;

protected:
  bool do_set (const octave_value& val)
  {
    Matrix new_kids;

    try
      {
        new_kids = val.matrix_value ();
      }
    catch (octave::execution_exception& ee)
      {
        error (ee, "set: children must be an array of graphics handles");
      }

    octave_idx_type nel = new_kids.numel ();

    const Matrix new_kids_column = new_kids.reshape (dim_vector (nel, 1));

    bool is_ok = true;
    bool add_hidden = true;

    const Matrix visible_kids = do_get_children (false);

    if (visible_kids.numel () == new_kids.numel ())
      {
        Matrix t1 = visible_kids.sort ();
        Matrix t2 = new_kids_column.sort ();
        Matrix t3 = get_hidden ().sort ();

        if (t1 != t2)
          is_ok = false;

        if (t1 == t3)
          add_hidden = false;
      }
    else
      is_ok = false;

    if (! is_ok)
      error ("set: new children must be a permutation of existing children");

    Matrix tmp = new_kids_column;

    if (add_hidden)
      tmp.stack (get_hidden ());

    m_children_list.clear ();

    // Don't use do_init_children here, as that reverses the
    // order of the list, and we don't want to do that if setting
    // the child list directly.
    for (octave_idx_type i = 0; i < tmp.numel (); i++)
      m_children_list.push_back (tmp.xelem (i));

    return is_ok;
  }

private:
  void do_init_children (const Matrix& val)
  {
    m_children_list.clear ();
    for (octave_idx_type i = 0; i < val.numel (); i++)
      m_children_list.push_front (val.xelem (i));
  }

  void do_init_children (const std::list<double>& val)
  {
    m_children_list.clear ();
    m_children_list = val;
  }

  OCTINTERP_API Matrix do_get_children (bool return_hidden) const;

  Matrix do_get_all_children (void) const
  {
    Matrix retval (m_children_list.size (), 1);
    octave_idx_type i = 0;

    for (const auto& hchild : m_children_list)
      retval(i++) = hchild;

    return retval;
  }

  bool do_remove_child (double child)
  {
    for (auto it = m_children_list.begin (); it != m_children_list.end (); it++)
      {
        if (*it == child)
          {
            m_children_list.erase (it);
            return true;
          }
      }
    return false;
  }

  void do_adopt_child (double val)
  {
    m_children_list.push_front (val);
  }

  void do_delete_children (bool clear, bool from_root);
};

// ---------------------------------------------------------------------

class OCTINTERP_API callback_property : public base_property
{
public:
  callback_property (const std::string& nm, const graphics_handle& h,
                     const octave_value& m)
    : base_property (nm, h), m_callback (m) { }

  callback_property (const callback_property& p)
    : base_property (p), m_callback (p.m_callback) { }

  octave_value get (void) const { return m_callback; }

  OCTINTERP_API void execute (const octave_value& data = octave_value ()) const;

  bool is_defined (void) const
  {
    return (m_callback.is_defined () && ! m_callback.isempty ());
  }

  callback_property& operator = (const octave_value& val)
  {
    set (val);
    return *this;
  }

  base_property * clone (void) const { return new callback_property (*this); }

protected:
  bool do_set (const octave_value& v)
  {
    if (! validate (v))
      error (R"(invalid value for callback property "%s")",
             get_name ().c_str ());

    m_callback = v;
    return true;
    return false;
  }

private:
  OCTINTERP_API bool validate (const octave_value& v) const;

private:
  octave_value m_callback;
};

// ---------------------------------------------------------------------

class OCTINTERP_API property
{
public:
  property (void) : m_rep (new base_property ("", graphics_handle ()))
  { }

  property (base_property *bp, bool persist = false) : m_rep (bp)
  { if (persist) m_rep->m_count++; }

  property (const property& p) : m_rep (p.m_rep)
  {
    m_rep->m_count++;
  }

  ~property (void)
  {
    if (--m_rep->m_count == 0)
      delete m_rep;
  }

  bool ok (void) const
  { return m_rep->ok (); }

  std::string get_name (void) const
  { return m_rep->get_name (); }

  void set_name (const std::string& name)
  { m_rep->set_name (name); }

  graphics_handle get_parent (void) const
  { return m_rep->get_parent (); }

  void set_parent (const graphics_handle& h)
  { m_rep->set_parent (h); }

  bool is_hidden (void) const
  { return m_rep->is_hidden (); }

  void set_hidden (bool flag)
  { m_rep->set_hidden (flag); }

  bool is_radio (void) const
  { return m_rep->is_radio (); }

  int get_id (void) const
  { return m_rep->get_id (); }

  void set_id (int d)
  { m_rep->set_id (d); }

  octave_value get (void) const
  { return m_rep->get (); }

  bool set (const octave_value& val, bool do_run = true,
            bool do_notify_toolkit = true)
  { return m_rep->set (val, do_run, do_notify_toolkit); }

  std::string values_as_string (void) const
  { return m_rep->values_as_string (); }

  Cell values_as_cell (void) const
  { return m_rep->values_as_cell (); }

  property& operator = (const octave_value& val)
  {
    *m_rep = val;
    return *this;
  }

  property& operator = (const property& p)
  {
    if (m_rep && --m_rep->m_count == 0)
      delete m_rep;

    m_rep = p.m_rep;
    m_rep->m_count++;

    return *this;
  }

  void add_listener (const octave_value& v, listener_mode mode = GCB_POSTSET)
  { m_rep->add_listener (v, mode); }

  void delete_listener (const octave_value& v = octave_value (),
                        listener_mode mode = GCB_POSTSET)
  { m_rep->delete_listener (v, mode); }

  void run_listeners (listener_mode mode = GCB_POSTSET)
  { m_rep->run_listeners (mode); }

  static OCTINTERP_API property
  create (const std::string& name, const graphics_handle& parent,
          const caseless_str& type, const octave_value_list& args);

  property clone (void) const
  { return property (m_rep->clone ()); }

#if 0
  const string_property& as_string_property (void) const
  { return *(dynamic_cast<string_property *> (m_rep)); }

  const radio_property& as_radio_property (void) const
  { return *(dynamic_cast<radio_property *> (m_rep)); }

  const color_property& as_color_property (void) const
  { return *(dynamic_cast<color_property *> (m_rep)); }

  const double_property& as_double_property (void) const
  { return *(dynamic_cast<double_property *> (m_rep)); }

  const bool_property& as_bool_property (void) const
  { return *(dynamic_cast<bool_property *> (m_rep)); }

  const handle_property& as_handle_property (void) const
  { return *(dynamic_cast<handle_property *> (m_rep)); }
#endif

private:
  base_property *m_rep;
};

// ---------------------------------------------------------------------

typedef std::pair<std::string, octave_value> pval_pair;

class OCTINTERP_API pval_vector : public std::vector<pval_pair>
{
public:
  const_iterator find (const std::string pname) const
  {
    const_iterator it;

    for (it = (*this).begin (); it != (*this).end (); it++)
      if (pname == (*it).first)
        return it;

    return (*this).end ();
  }

  iterator find (const std::string pname)
  {
    iterator it;

    for (it = (*this).begin (); it != (*this).end (); it++)
      if (pname == (*it).first)
        return it;

    return (*this).end ();
  }

  octave_value lookup (const std::string pname) const
  {
    octave_value retval;

    const_iterator it = find (pname);

    if (it != (*this).end ())
      retval = (*it).second;

    return retval;
  }

  octave_value& operator [] (const std::string pname)
  {
    iterator it = find (pname);

    if (it == (*this).end ())
      {
        push_back (pval_pair (pname, octave_value ()));
        return (*this).back ().second;
      }

    return (*it).second;
  }

  void erase (const std::string pname)
  {
    iterator it = find (pname);
    if (it != (*this).end ())
      erase (it);
  }

  void erase (iterator it)
  {
    std::vector<pval_pair>::erase (it);
  }

};

class OCTINTERP_API property_list
{
public:
  typedef pval_vector pval_map_type;
  typedef std::map<std::string, pval_map_type> plist_map_type;

  typedef pval_map_type::iterator pval_map_iterator;
  typedef pval_map_type::const_iterator pval_map_const_iterator;

  typedef plist_map_type::iterator plist_map_iterator;
  typedef plist_map_type::const_iterator plist_map_const_iterator;

  property_list (const plist_map_type& m = plist_map_type ())
    : m_plist_map (m) { }

  ~property_list (void) = default;

  OCTINTERP_API void set (const caseless_str& name, const octave_value& val);

  OCTINTERP_API octave_value lookup (const caseless_str& name) const;

  plist_map_iterator begin (void) { return m_plist_map.begin (); }
  plist_map_const_iterator begin (void) const { return m_plist_map.begin (); }

  plist_map_iterator end (void) { return m_plist_map.end (); }
  plist_map_const_iterator end (void) const { return m_plist_map.end (); }

  plist_map_iterator find (const std::string& go_name)
  {
    return m_plist_map.find (go_name);
  }

  plist_map_const_iterator find (const std::string& go_name) const
  {
    return m_plist_map.find (go_name);
  }

  OCTINTERP_API octave_scalar_map
  as_struct (const std::string& prefix_arg) const;

private:
  plist_map_type m_plist_map;
};

// ---------------------------------------------------------------------

class base_graphics_object;
class graphics_object;

class OCTINTERP_API base_properties
{
public:
  base_properties (const std::string& ty = "unknown",
                   const graphics_handle& mh = graphics_handle (),
                   const graphics_handle& p = graphics_handle ());

  virtual ~base_properties (void) = default;

  virtual std::string graphics_object_name (void) const { return "unknown"; }

  OCTINTERP_API void mark_modified (void);

  OCTINTERP_API void override_defaults (base_graphics_object& obj);

  virtual void init_integerhandle (const octave_value&)
  {
    panic_impossible ();
  }

  // Look through DEFAULTS for properties with given CLASS_NAME, and
  // apply them to the current object with set (virtual method).

  OCTINTERP_API void
  set_from_list (base_graphics_object& obj, property_list& defaults);

  void insert_property (const std::string& name, property p)
  {
    p.set_name (name);
    p.set_parent (m___myhandle__);
    m_all_props[name] = p;
  }

  virtual void set (const caseless_str&, const octave_value&);

  virtual octave_value get (const caseless_str& pname) const;

  virtual octave_value get (const std::string& pname) const
  {
    return get (caseless_str (pname));
  }

  virtual octave_value get (const char *pname) const
  {
    return get (caseless_str (pname));
  }

  virtual octave_value get (bool all = false) const;

  // FIXME: It seems like this function should be const, but that is
  // currently not possible with the way that properties are stored as
  // specific types in the graphics_object classes.
  virtual property get_property (const caseless_str& pname);

  virtual bool has_property (const caseless_str&) const
  {
    panic_impossible ();
    return false;
  }

  bool is_modified (void) const { return is___modified__ (); }

  virtual void remove_child (const graphics_handle& h, bool = false)
  {
    if (m_children.remove_child (h.value ()))
      {
        m_children.run_listeners ();
        mark_modified ();
      }
  }

  virtual void adopt (const graphics_handle& h)
  {
    m_children.adopt (h.value ());
    m_children.run_listeners ();
    mark_modified ();
  }

  virtual octave::graphics_toolkit get_toolkit (void) const;

  virtual Matrix
  get_boundingbox (bool /* finternal */ = false,
                   const Matrix& /* parent_pix_size */ = Matrix ()) const
  { return Matrix (1, 4, 0.0); }

  virtual void update_boundingbox (void);

  virtual void update_autopos (const std::string& elem_type);

  virtual void add_listener (const caseless_str&, const octave_value&,
                             listener_mode = GCB_POSTSET);

  virtual void delete_listener (const caseless_str&, const octave_value&,
                                listener_mode = GCB_POSTSET);

  void set_beingdeleted (const octave_value& val)
  {
    m_beingdeleted.set (val, true, false);
    update_beingdeleted ();
  }

  void set_tag (const octave_value& val) { m_tag = val; }

  OCTINTERP_API void set_parent (const octave_value& val);

  Matrix get_children (void) const
  {
    return m_children.get_children ();
  }

  Matrix get_all_children (void) const
  {
    return m_children.get_all ();
  }

  Matrix get_hidden_children (void) const
  {
    return m_children.get_hidden ();
  }

  OCTINTERP_API void
  get_children_of_type (const caseless_str& type, bool get_invisible,
                        bool traverse,
                        std::list<graphics_object> &children_list) const;

  void set_modified (const octave_value& val) { set___modified__ (val); }

  void set___modified__ (const octave_value& val) { m___modified__ = val; }

  // Redirect calls to "uicontextmenu" to "contextmenu".

  graphics_handle get_uicontextmenu (void) const
  {
    return get_contextmenu ();
  }

  void set_uicontextmenu (const octave_value& val)
  {
    set_contextmenu (val);
  }

  void reparent (const graphics_handle& new_parent) { m_parent = new_parent; }

  // Update data limits for AXIS_TYPE (xdata, ydata, etc.) in the parent
  // axes object.

  virtual void update_axis_limits (const std::string& axis_type) const;

  virtual void update_axis_limits (const std::string& axis_type,
                                   const graphics_handle& h) const;

  virtual void update_contextmenu (void) const;

  virtual void delete_children (bool clear = false, bool from_root = false)
  {
    m_children.delete_children (clear, from_root);
  }

  void renumber_child (graphics_handle old_gh, graphics_handle new_gh)
  {
    m_children.renumber (old_gh, new_gh);
  }

  void renumber_parent (graphics_handle new_gh)
  {
    m_parent = new_gh;
  }

  static OCTINTERP_API property_list::pval_map_type factory_defaults (void);

  // FIXME: These functions should be generated automatically by the
  //        genprops.awk script.
  //
  // EMIT_BASE_PROPERTIES_GET_FUNCTIONS

  virtual octave_value get_alim (void) const { return octave_value (); }
  virtual octave_value get_clim (void) const { return octave_value (); }
  virtual octave_value get_xlim (void) const { return octave_value (); }
  virtual octave_value get_ylim (void) const { return octave_value (); }
  virtual octave_value get_zlim (void) const { return octave_value (); }

  virtual bool is_aliminclude (void) const { return false; }
  virtual bool is_climinclude (void) const { return false; }
  virtual bool is_xliminclude (void) const { return false; }
  virtual bool is_yliminclude (void) const { return false; }
  virtual bool is_zliminclude (void) const { return false; }

  OCTINTERP_API bool is_handle_visible (void) const;

  OCTINTERP_API std::set<std::string> dynamic_property_names (void) const;

  OCTINTERP_API bool has_dynamic_property (const std::string& pname) const;

protected:
  std::set<std::string> m_dynamic_properties;

  OCTINTERP_API void
  set_dynamic (const caseless_str& pname, const octave_value& val);

  OCTINTERP_API octave_value get_dynamic (const caseless_str& pname) const;

  OCTINTERP_API octave_value get_dynamic (bool all = false) const;

  OCTINTERP_API property get_property_dynamic (const caseless_str& pname) const;

  BEGIN_BASE_PROPERTIES
    // properties common to all objects
    bool_property beingdeleted s , "off"
    radio_property busyaction , "{queue}|cancel"
    callback_property buttondownfcn , Matrix ()
    children_property children gf , Matrix ()
    bool_property clipping , "on"
    handle_property contextmenu u , graphics_handle ()
    callback_property createfcn , Matrix ()
    callback_property deletefcn , Matrix ()
    radio_property handlevisibility u , "{on}|callback|off"
    bool_property hittest , "on"
    bool_property interruptible , "on"
    handle_property parent fs , p
    radio_property pickableparts , "{visible}|all|none"
    bool_property selected , "off"
    bool_property selectionhighlight , "on"
    string_property tag s , ""
    string_property type frs , ty
    handle_property uicontextmenu gsh , graphics_handle ()
    any_property userdata , Matrix ()
    bool_property visible u , "on"

    // Octave-specific properties
    any_property __appdata__ h , Matrix ()
    bool_property __modified__ hs , "on"
    graphics_handle __myhandle__ fhrs , mh
  END_PROPERTIES

  virtual void update_beingdeleted (void) { };

  virtual void update_handlevisibility (void);

  virtual void update_visible (void) { };

protected:
  struct cmp_caseless_str
  {
  public:
    bool operator () (const caseless_str& a, const caseless_str& b) const
    {
      std::string a1 = a;
      std::transform (a1.begin (), a1.end (), a1.begin (), tolower);
      std::string b1 = b;
      std::transform (b1.begin (), b1.end (), b1.begin (), tolower);

      return a1 < b1;
    }
  };

  std::map<caseless_str, property, cmp_caseless_str> m_all_props;

protected:

  virtual void init (void)
  {
    m_contextmenu.add_constraint ("uicontextmenu");
  }
};

class OCTINTERP_API base_graphics_object
{
public:
  friend class graphics_object;

  base_graphics_object (void) : m_toolkit_flag (false) { }

  // No copying!

  base_graphics_object (const base_graphics_object&) = delete;

  base_graphics_object& operator = (const base_graphics_object&) = delete;

  virtual ~base_graphics_object (void) = default;

  virtual void mark_modified (void)
  {
    if (! valid_object ())
      error ("base_graphics_object::mark_modified: invalid graphics object");

    get_properties ().mark_modified ();
  }

  virtual void override_defaults (base_graphics_object& obj)
  {
    if (! valid_object ())
      error ("base_graphics_object::override_defaults: invalid graphics object");
    get_properties ().override_defaults (obj);
  }

  void build_user_defaults_map (property_list::pval_map_type& def,
                                const std::string go_name) const;

  virtual void set_from_list (property_list& plist)
  {
    if (! valid_object ())
      error ("base_graphics_object::set_from_list: invalid graphics object");

    get_properties ().set_from_list (*this, plist);
  }

  virtual void set (const caseless_str& pname, const octave_value& pval)
  {
    if (! valid_object ())
      error ("base_graphics_object::set: invalid graphics object");

    get_properties ().set (pname, pval);
  }

  virtual void set_defaults (const std::string&)
  {
    error ("base_graphics_object::set_defaults: invalid graphics object");
  }

  // The following version of the get method is not declared virtual
  // because no derived class overrides it.

  octave_value get (bool all = false) const
  {
    if (! valid_object ())
      error ("base_graphics_object::get: invalid graphics object");

    return get_properties ().get (all);
  }

  virtual octave_value get (const caseless_str& pname) const
  {
    if (! valid_object ())
      error ("base_graphics_object::get: invalid graphics object");

    return get_properties ().get (pname);
  }

  virtual octave_value get_default (const caseless_str&) const;

  virtual octave_value get_factory_default (const caseless_str&) const;

  virtual octave_value get_defaults (void) const
  {
    error ("base_graphics_object::get_defaults: invalid graphics object");
  }

  virtual property_list get_defaults_list (void) const
  {
    if (! valid_object ())
      error ("base_graphics_object::get_defaults_list: invalid graphics object");

    return property_list ();
  }

  virtual octave_value get_factory_defaults (void) const
  {
    error ("base_graphics_object::get_factory_defaults: invalid graphics object");
  }

  virtual property_list get_factory_defaults_list (void) const
  {
    error ("base_graphics_object::get_factory_defaults_list: invalid graphics object");
  }

  virtual bool has_readonly_property (const caseless_str& pname) const
  {
    return base_properties::has_readonly_property (pname);
  }

  // FIXME: It seems like this function should be const, but that is
  // currently not possible.
  virtual std::string values_as_string (void);

  // FIXME: It seems like this function should be const, but that is
  // currently not possible.
  virtual std::string value_as_string (const std::string& prop);

  // FIXME: It seems like this function should be const, but that is
  // currently not possible.
  virtual octave_scalar_map values_as_struct (void);

  virtual graphics_handle get_parent (void) const
  {
    if (! valid_object ())
      error ("base_graphics_object::get_parent: invalid graphics object");

    return get_properties ().get_parent ();
  }

  graphics_handle get_handle (void) const
  {
    if (! valid_object ())
      error ("base_graphics_object::get_handle: invalid graphics object");

    return get_properties ().get___myhandle__ ();
  }

  virtual void remove_child (const graphics_handle& h, bool from_root = false)
  {
    if (! valid_object ())
      error ("base_graphics_object::remove_child: invalid graphics object");

    get_properties ().remove_child (h, from_root);
  }

  virtual void adopt (const graphics_handle& h)
  {
    if (! valid_object ())
      error ("base_graphics_object::adopt: invalid graphics object");

    get_properties ().adopt (h);
  }

  virtual void reparent (const graphics_handle& np)
  {
    if (! valid_object ())
      error ("base_graphics_object::reparent: invalid graphics object");

    get_properties ().reparent (np);
  }

  virtual void defaults (void) const
  {
    if (! valid_object ())
      error ("base_graphics_object::default: invalid graphics object");

    std::string msg = (type () + "::defaults");
    err_not_implemented (msg.c_str ());
  }

  virtual base_properties& get_properties (void)
  {
    static base_properties properties;
    warning ("base_graphics_object::get_properties: invalid graphics object");
    return properties;
  }

  virtual const base_properties& get_properties (void) const
  {
    static base_properties properties;
    warning ("base_graphics_object::get_properties: invalid graphics object");
    return properties;
  }

  virtual void update_axis_limits (const std::string& axis_type);

  virtual void update_axis_limits (const std::string& axis_type,
                                   const graphics_handle& h);

  virtual bool valid_object (void) const { return false; }

  bool valid_toolkit_object (void) const { return m_toolkit_flag; }

  virtual std::string type (void) const
  {
    return (valid_object () ? get_properties ().graphics_object_name ()
                            : "unknown");
  }

  bool isa (const std::string& go_name) const
  {
    return type () == go_name;
  }

  virtual octave::graphics_toolkit get_toolkit (void) const
  {
    if (! valid_object ())
      error ("base_graphics_object::get_toolkit: invalid graphics object");

    return get_properties ().get_toolkit ();
  }

  virtual void add_property_listener (const std::string& nm,
                                      const octave_value& v,
                                      listener_mode mode = GCB_POSTSET)
  {
    if (valid_object ())
      get_properties ().add_listener (nm, v, mode);
  }

  virtual void delete_property_listener (const std::string& nm,
                                         const octave_value& v,
                                         listener_mode mode = GCB_POSTSET)
  {
    if (valid_object ())
      get_properties ().delete_listener (nm, v, mode);
  }

  virtual void remove_all_listeners (void);

  virtual void reset_default_properties (void);

protected:
  virtual void initialize (const graphics_object& go)
  {
    if (! m_toolkit_flag)
      m_toolkit_flag = get_toolkit ().initialize (go);
  }

  virtual void finalize (const graphics_object& go)
  {
    if (m_toolkit_flag)
      {
        get_toolkit ().finalize (go);
        m_toolkit_flag = false;
      }
  }

  virtual void update (const graphics_object& go, int id)
  {
    if (m_toolkit_flag)
      get_toolkit ().update (go, id);
  }

protected:

  // A flag telling whether this object is a valid object
  // in the backend context.
  bool m_toolkit_flag;
};

class OCTINTERP_API graphics_object
{
public:

  graphics_object (void) : m_rep (new base_graphics_object ()) { }

  graphics_object (base_graphics_object *new_rep) : m_rep (new_rep) { }

  graphics_object (const graphics_object&) = default;

  graphics_object& operator = (const graphics_object&) = default;

  ~graphics_object (void) = default;

  void mark_modified (void) { m_rep->mark_modified (); }

  void override_defaults (base_graphics_object& obj)
  {
    m_rep->override_defaults (obj);
  }

  void override_defaults (void)
  {
    m_rep->override_defaults (*m_rep);
  }

  void build_user_defaults_map (property_list::pval_map_type& def,
                                const std::string go_name) const
  {
    m_rep->build_user_defaults_map (def, go_name);
  }

  void set_from_list (property_list& plist) { m_rep->set_from_list (plist); }

  void set (const caseless_str& name, const octave_value& val)
  {
    m_rep->set (name, val);
  }

  OCTINTERP_API void set (const octave_value_list& args);

  OCTINTERP_API void set (const Array<std::string>& names, const Cell& values,
                          octave_idx_type row);

  OCTINTERP_API void set (const octave_map& m);

  OCTINTERP_API void set_value_or_default (const caseless_str& name,
                                           const octave_value& val);

  void set_defaults (const std::string& mode) { m_rep->set_defaults (mode); }

  octave_value get (bool all = false) const { return m_rep->get (all); }

  octave_value get (const caseless_str& name) const
  {
    return name.compare ("default")
           ? get_defaults ()
           : (name.compare ("factory")
              ? get_factory_defaults () : m_rep->get (name));
  }

  octave_value get (const std::string& name) const
  {
    return get (caseless_str (name));
  }

  octave_value get (const char *name) const
  {
    return get (caseless_str (name));
  }

  octave_value get_default (const caseless_str& name) const
  {
    return m_rep->get_default (name);
  }

  octave_value get_factory_default (const caseless_str& name) const
  {
    return m_rep->get_factory_default (name);
  }

  octave_value get_defaults (void) const { return m_rep->get_defaults (); }

  property_list get_defaults_list (void) const
  {
    return m_rep->get_defaults_list ();
  }

  octave_value get_factory_defaults (void) const
  {
    return m_rep->get_factory_defaults ();
  }

  property_list get_factory_defaults_list (void) const
  {
    return m_rep->get_factory_defaults_list ();
  }

  bool has_readonly_property (const caseless_str& pname) const
  {
    return m_rep->has_readonly_property (pname);
  }

  // FIXME: It seems like this function should be const, but that is
  // currently not possible.
  std::string values_as_string (void) { return m_rep->values_as_string (); }

  // FIXME: It seems like this function should be const, but that is
  // currently not possible.
  std::string value_as_string (const std::string& prop)
  {
    return m_rep->value_as_string (prop);
  }

  // FIXME: It seems like this function should be const, but that is
  // currently not possible.
  octave_map values_as_struct (void) { return m_rep->values_as_struct (); }

  graphics_handle get_parent (void) const { return m_rep->get_parent (); }

  graphics_handle get_handle (void) const { return m_rep->get_handle (); }

  OCTINTERP_API graphics_object get_ancestor (const std::string& type) const;

  void remove_child (const graphics_handle& h) { m_rep->remove_child (h); }

  void adopt (const graphics_handle& h) { m_rep->adopt (h); }

  void reparent (const graphics_handle& h) { m_rep->reparent (h); }

  void defaults (void) const { m_rep->defaults (); }

  bool isa (const std::string& go_name) const { return m_rep->isa (go_name); }

  base_properties& get_properties (void) { return m_rep->get_properties (); }

  const base_properties& get_properties (void) const
  {
    return m_rep->get_properties ();
  }

  void update_axis_limits (const std::string& axis_type)
  {
    m_rep->update_axis_limits (axis_type);
  }

  void update_axis_limits (const std::string& axis_type,
                           const graphics_handle& h)
  {
    m_rep->update_axis_limits (axis_type, h);
  }

  bool valid_object (void) const { return m_rep->valid_object (); }

  std::string type (void) const { return m_rep->type (); }

  operator bool (void) const { return m_rep->valid_object (); }

  // FIXME: These functions should be generated automatically by the
  //        genprops.awk script.
  //
  // EMIT_GRAPHICS_OBJECT_GET_FUNCTIONS

  octave_value get_alim (void) const
  { return get_properties ().get_alim (); }

  octave_value get_clim (void) const
  { return get_properties ().get_clim (); }

  octave_value get_xlim (void) const
  { return get_properties ().get_xlim (); }

  octave_value get_ylim (void) const
  { return get_properties ().get_ylim (); }

  octave_value get_zlim (void) const
  { return get_properties ().get_zlim (); }

  bool is_aliminclude (void) const
  { return get_properties ().is_aliminclude (); }

  bool is_climinclude (void) const
  { return get_properties ().is_climinclude (); }

  bool is_xliminclude (void) const
  { return get_properties ().is_xliminclude (); }

  bool is_yliminclude (void) const
  { return get_properties ().is_yliminclude (); }

  bool is_zliminclude (void) const
  { return get_properties ().is_zliminclude (); }

  bool is_handle_visible (void) const
  { return get_properties ().is_handle_visible (); }

  octave::graphics_toolkit get_toolkit (void) const
  { return m_rep->get_toolkit (); }

  void add_property_listener (const std::string& nm, const octave_value& v,
                              listener_mode mode = GCB_POSTSET)
  { m_rep->add_property_listener (nm, v, mode); }

  void delete_property_listener (const std::string& nm, const octave_value& v,
                                 listener_mode mode = GCB_POSTSET)
  { m_rep->delete_property_listener (nm, v, mode); }

  void remove_all_listeners (void) { m_rep->remove_all_listeners (); }

  void initialize (void) { m_rep->initialize (*this); }

  void finalize (void) { m_rep->finalize (*this); }

  void update (int id) { m_rep->update (*this, id); }

  void reset_default_properties (void)
  { m_rep->reset_default_properties (); }

private:

  std::shared_ptr<base_graphics_object> m_rep;
};

// ---------------------------------------------------------------------

class OCTINTERP_API root_figure : public base_graphics_object
{
public:

  // The gh_manager constructor creates the single instance of
  // the root_figure object.

  friend class gh_manager;

  class OCTINTERP_API properties : public base_properties
  {
  public:
    OCTINTERP_API void
    remove_child (const graphics_handle& h, bool from_root = false);

    OCTINTERP_API Matrix
    get_boundingbox (bool internal = false,
                     const Matrix& parent_pix_size = Matrix ()) const;

    // See the genprops.awk script for an explanation of the
    // properties declarations.

    // FIXME: Properties that still don't have callbacks are:
    // monitorpositions, pointerlocation, pointerwindow.
    // Note that these properties are not yet used by Octave, so setting
    // them will have no effect.

    // FIXME: The commandwindowsize property has been deprecated in Matlab
    //        and is now available through matlab.desktop.comandwindow.size.
    //        Until Octave has something similar, keep this property in root.

    // Programming note: Keep property list sorted if new ones are added.

    BEGIN_PROPERTIES (root_figure, root)
      handle_property callbackobject Sr , graphics_handle ()
      array_property commandwindowsize r , Matrix (1, 2, 0)
      handle_property currentfigure S , graphics_handle ()
      string_property fixedwidthfontname , "Courier"
      array_property monitorpositions r , default_screensize ()
      array_property pointerlocation , Matrix (1, 2, 0)
      double_property pointerwindow r , 0.0
      double_property screendepth r , default_screendepth ()
      double_property screenpixelsperinch r , default_screenpixelsperinch ()
      array_property screensize r , default_screensize ()
      bool_property showhiddenhandles , "off"
      radio_property units U , "{pixels}|inches|centimeters|points|normalized|characters"
      // Hide base properties which don't make sense for root object
      //radio_property beingdeleted h , "{off}|on"
    END_PROPERTIES
  };

private:

  properties m_properties;

protected:

  root_figure (void)
    : m_properties (0, graphics_handle ()), m_default_properties (),
      m_factory_properties (init_factory_properties ())
  { }

public:

  ~root_figure (void) = default;

  root_figure (const root_figure&) = delete;

  root_figure& operator = (const root_figure&) = delete;

  void mark_modified (void) { }

  void override_defaults (base_graphics_object& obj)
  {
    // Now override with our defaults.  If the default_properties
    // list includes the properties for all defaults (line,
    // surface, etc.) then we don't have to know the type of OBJ
    // here, we just call its set function and let it decide which
    // properties from the list to use.
    obj.set_from_list (m_default_properties);
  }

  void set (const caseless_str& name, const octave_value& value)
  {
    if (name.compare ("default", 7))
      // strip "default", pass rest to function that will
      // parse the remainder and add the element to the
      // default_properties map.
      m_default_properties.set (name.substr (7), value);
    else
      m_properties.set (name, value);
  }

  octave_value get (const caseless_str& name) const
  {
    octave_value retval;

    if (name.compare ("default", 7))
      return get_default (name.substr (7));
    else if (name.compare ("factory", 7))
      return get_factory_default (name.substr (7));
    else
      retval = m_properties.get (name);

    return retval;
  }

  octave_value get_default (const caseless_str& name) const
  {
    octave_value retval = m_default_properties.lookup (name);

    if (retval.is_undefined ())
      {
        // no default property found, use factory default
        retval = m_factory_properties.lookup (name);

        if (retval.is_undefined ())
          error ("get: invalid default property '%s'", name.c_str ());
      }

    return retval;
  }

  octave_value get_factory_default (const caseless_str& name) const
  {
    octave_value retval = m_factory_properties.lookup (name);

    if (retval.is_undefined ())
      error ("get: invalid factory default property '%s'", name.c_str ());

    return retval;
  }

  octave_value get_defaults (void) const
  {
    return m_default_properties.as_struct ("default");
  }

  property_list get_defaults_list (void) const
  {
    return m_default_properties;
  }

  octave_value get_factory_defaults (void) const
  {
    return m_factory_properties.as_struct ("factory");
  }

  property_list get_factory_defaults_list (void) const
  {
    return m_factory_properties;
  }

  base_properties& get_properties (void) { return m_properties; }

  const base_properties& get_properties (void) const { return m_properties; }

  bool valid_object (void) const { return true; }

  OCTINTERP_API void reset_default_properties (void);

  bool has_readonly_property (const caseless_str& pname) const
  {
    bool retval = m_properties.has_readonly_property (pname);
    if (! retval)
      retval = base_properties::has_readonly_property (pname);
    return retval;
  }

private:

  property_list m_default_properties;

  property_list m_factory_properties;

  static OCTINTERP_API property_list::plist_map_type
  init_factory_properties (void);
};

// ---------------------------------------------------------------------

class OCTINTERP_API figure : public base_graphics_object
{
public:

  class OCTINTERP_API properties : public base_properties
  {
  public:
    void init_integerhandle (const octave_value& val)
    {
      m_integerhandle = val;
    }

    OCTINTERP_API void
    remove_child (const graphics_handle& h, bool from_root = false);

    OCTINTERP_API void set_visible (const octave_value& val);

    OCTINTERP_API octave::graphics_toolkit get_toolkit (void) const;

    OCTINTERP_API void set_toolkit (const octave::graphics_toolkit& b);

    OCTINTERP_API void set___graphics_toolkit__ (const octave_value& val);

    OCTINTERP_API void adopt (const graphics_handle& h);

    OCTINTERP_API void set_position (const octave_value& val,
                                     bool do_notify_toolkit = true);

    OCTINTERP_API void set_outerposition (const octave_value& val,
                                          bool do_notify_toolkit = true);

    OCTINTERP_API Matrix bbox2position (const Matrix& bbox) const;

    OCTINTERP_API Matrix
    get_boundingbox (bool internal = false,
                     const Matrix& parent_pix_size = Matrix ()) const;

    OCTINTERP_API void
    set_boundingbox (const Matrix& bb, bool internal = false,
                     bool do_notify_toolkit = true);

    OCTINTERP_API Matrix map_from_boundingbox (double x, double y) const;

    OCTINTERP_API Matrix map_to_boundingbox (double x, double y) const;

    OCTINTERP_API void update_units (const caseless_str& old_units);

    OCTINTERP_API void update_paperunits (const caseless_str& old_paperunits);

    OCTINTERP_API std::string get_title (void) 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 (figure)
      array_property alphamap , Matrix (64, 1, 1)
      callback_property buttondownfcn , Matrix ()
      callback_property closerequestfcn , "closereq"
      color_property color , color_property (color_values (1, 1, 1), radio_values ("none"))
      array_property colormap , viridis_colormap ()
      handle_property currentaxes S , graphics_handle ()
      string_property currentcharacter r , ""
      handle_property currentobject r , graphics_handle ()
      array_property currentpoint r , Matrix (2, 1, 0)
      bool_property dockcontrols , "off"
      string_property filename , ""
      bool_property graphicssmoothing , "on"
      bool_property integerhandle S , "on"
      bool_property inverthardcopy , "on"
      callback_property keypressfcn , Matrix ()
      callback_property keyreleasefcn , Matrix ()
      radio_property menubar , "{figure}|none"
      string_property name , ""
      array_property number rG , Matrix ()
      radio_property nextplot , "{add}|new|replace|replacechildren"
      bool_property numbertitle , "on"
      array_property outerposition s , Matrix (1, 4, -1.0)
      radio_property paperorientation U , "{portrait}|landscape"
      array_property paperposition m , default_figure_paperposition ()
      // FIXME: Matlab default is "auto", but this messes up hgsave BIST test.
      radio_property paperpositionmode au , "{auto}|manual"
      array_property papersize U , default_figure_papersize ()
      radio_property papertype SU , "{usletter}|uslegal|a0|a1|a2|a3|a4|a5|b0|b1|b2|b3|b4|b5|arch-a|arch-b|arch-c|arch-d|arch-e|a|b|c|d|e|tabloid|<custom>"
      radio_property paperunits Su , "{inches}|centimeters|normalized|points"
      radio_property pointer , "crosshair|{arrow}|ibeam|watch|topl|topr|botl|botr|left|top|right|bottom|circle|cross|fleur|custom|hand"
      array_property pointershapecdata , Matrix (16, 16, 1)
      array_property pointershapehotspot , Matrix (1, 2, 1)
      array_property position s , default_figure_position ()
      radio_property renderer m , "{opengl}|painters"
      radio_property renderermode , "{auto}|manual"
      bool_property resize , "on"
      // FIXME: "resizefcn" is no longer recommended by Matlab,
      //        and has been replaced with "sizechangedfcn"
      //        Eventually this will need to be hidden, and then removed.
      callback_property resizefcn , Matrix ()
      radio_property selectiontype , "{normal}|extend|alt|open"
      callback_property sizechangedfcn , Matrix ()
      radio_property toolbar , "{auto}|figure|none"
      radio_property units Su , "{pixels}|normalized|inches|centimeters|points|characters"
      callback_property windowbuttondownfcn , Matrix ()
      callback_property windowbuttonmotionfcn , Matrix ()
      callback_property windowbuttonupfcn , Matrix ()
      callback_property windowkeypressfcn , Matrix ()
      callback_property windowkeyreleasefcn , Matrix ()
      callback_property windowscrollwheelfcn , Matrix ()
      radio_property windowstyle , "{normal}|modal|docked"

      // Overridden base property
      // Property is not implemented for figures.
      // Hide it and set it to a default value that works.
      radio_property pickableparts h , "{visible}"

      // Octave-specific properties
      mutable string_property __gl_extensions__ hr , ""
      mutable string_property __gl_renderer__ hr , ""
      mutable string_property __gl_vendor__ hr , ""
      mutable string_property __gl_version__ hr , ""
      bool_property __gl_window__ h , "off"
      string_property __graphics_toolkit__ hs , default_graphics_toolkit ()
      any_property __guidata__ h , Matrix ()
      radio_property __mouse_mode__ hS , "{none}|pan|rotate|select|text|zoom"
      bool_property __printing__ h , "off"
      any_property __pan_mode__ h , Matrix ()
      any_property __plot_stream__ h , Matrix ()
      any_property __rotate_mode__ h , Matrix ()
      any_property __zoom_mode__ h , Matrix ()
      double_property __device_pixel_ratio__ hU , 1.0

    END_PROPERTIES

  protected:
    void init (void)
    {
      m_alphamap.add_constraint (dim_vector (-1, 1));
      m_alphamap.add_constraint (dim_vector (1, -1));
      m_colormap.add_constraint (dim_vector (-1, 3));
      m_colormap.add_constraint (dim_vector (0, 0));
      m_outerposition.add_constraint (dim_vector (1, 4));
      m_outerposition.add_constraint (FINITE);
      m_paperposition.add_constraint (dim_vector (1, 4));
      m_paperposition.add_constraint (FINITE);
      m_papersize.add_constraint (dim_vector (1, 2));
      m_papersize.add_constraint (FINITE);
      m_pointershapecdata.add_constraint (dim_vector (16, 16));
      m_pointershapecdata.add_constraint (dim_vector (32, 32));
      m_pointershapehotspot.add_constraint (dim_vector (1, 2));
      m_position.add_constraint (dim_vector (1, 4));
      m_position.add_constraint (FINITE);

      init_toolkit ();
    }

  private:
    OCTINTERP_API Matrix get_auto_paperposition (void);

    void update_paperpositionmode (void)
    {
      if (m_paperpositionmode.is ("auto"))
        m_paperposition.set (get_auto_paperposition ());
    }

    OCTINTERP_API void update_handlevisibility (void);

    OCTINTERP_API void init_toolkit (void);

    octave::graphics_toolkit m_toolkit;
  };

private:
  properties m_properties;

public:
  figure (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), m_properties (mh, p), m_default_properties ()
  { }

  ~figure (void) = default;

  void override_defaults (base_graphics_object& obj)
  {
    // Allow parent (root object) to override first (properties knows how
    // to find the parent object).
    m_properties.override_defaults (obj);

    // Now override with our defaults.  If the default_properties
    // list includes the properties for all defaults (line,
    // surface, etc.) then we don't have to know the type of OBJ
    // here, we just call its set function and let it decide which
    // properties from the list to use.
    obj.set_from_list (m_default_properties);
  }

  void set (const caseless_str& name, const octave_value& value)
  {
    if (name.compare ("default", 7))
      // strip "default", pass rest to function that will
      // parse the remainder and add the element to the
      // default_properties map.
      m_default_properties.set (name.substr (7), value);
    else
      m_properties.set (name, value);
  }

  octave_value get (const caseless_str& name) const
  {
    octave_value retval;

    if (name.compare ("default", 7))
      retval = get_default (name.substr (7));
    else
      retval = m_properties.get (name);

    return retval;
  }

  OCTINTERP_API octave_value get_default (const caseless_str& name) const;

  octave_value get_defaults (void) const
  {
    return m_default_properties.as_struct ("default");
  }

  property_list get_defaults_list (void) const
  {
    return m_default_properties;
  }

  base_properties& get_properties (void) { return m_properties; }

  const base_properties& get_properties (void) const { return m_properties; }

  bool valid_object (void) const { return true; }

  OCTINTERP_API void reset_default_properties (void);

  bool has_readonly_property (const caseless_str& pname) const
  {
    bool retval = m_properties.has_readonly_property (pname);
    if (! retval)
      retval = base_properties::has_readonly_property (pname);
    return retval;
  }

private:
  property_list m_default_properties;
};

// ---------------------------------------------------------------------

class OCTINTERP_API graphics_xform
{
public:

  graphics_xform (void)
    : m_xform (xform_eye ()), m_xform_inv (xform_eye ()),
      m_sx ("linear"), m_sy ("linear"), m_sz ("linear"),  m_zlim (1, 2, 0.0)
  {
    m_zlim(1) = 1.0;
  }

  graphics_xform (const Matrix& xm, const Matrix& xim,
                  const scaler& x, const scaler& y, const scaler& z,
                  const Matrix& zl)
    : m_xform (xm), m_xform_inv (xim), m_sx (x), m_sy (y), m_sz (z), m_zlim (zl) { }

  graphics_xform (const graphics_xform& g)
    : m_xform (g.m_xform), m_xform_inv (g.m_xform_inv), m_sx (g.m_sx),
      m_sy (g.m_sy), m_sz (g.m_sz), m_zlim (g.m_zlim) { }

  ~graphics_xform (void) = default;

  graphics_xform& operator = (const graphics_xform& g)
  {
    m_xform = g.m_xform;
    m_xform_inv = g.m_xform_inv;
    m_sx = g.m_sx;
    m_sy = g.m_sy;
    m_sz = g.m_sz;
    m_zlim = g.m_zlim;

    return *this;
  }

  static OCTINTERP_API ColumnVector xform_vector (double x, double y, double z);

  static OCTINTERP_API Matrix xform_eye (void);

  OCTINTERP_API ColumnVector
  transform (double x, double y, double z, bool use_scale = true) const;

  OCTINTERP_API ColumnVector
  untransform (double x, double y, double z, bool use_scale = true) const;

  ColumnVector untransform (double x, double y, bool use_scale = true) const
  { return untransform (x, y, (m_zlim(0)+m_zlim(1))/2, use_scale); }

  Matrix xscale (const Matrix& m) const { return m_sx.scale (m); }
  Matrix yscale (const Matrix& m) const { return m_sy.scale (m); }
  Matrix zscale (const Matrix& m) const { return m_sz.scale (m); }

  Matrix scale (const Matrix& m) const
  {
    bool has_z = (m.columns () > 2);

    if (m_sx.is_linear () && m_sy.is_linear ()
        && (! has_z || m_sz.is_linear ()))
      return m;

    Matrix retval (m.dims ());

    int r = m.rows ();

    for (int i = 0; i < r; i++)
      {
        retval(i,0) = m_sx.scale (m(i,0));
        retval(i,1) = m_sy.scale (m(i,1));
        if (has_z)
          retval(i,2) = m_sz.scale (m(i,2));
      }

    return retval;
  }

private:
  Matrix m_xform;
  Matrix m_xform_inv;
  scaler m_sx, m_sy, m_sz;
  Matrix m_zlim;
};

enum
{
  AXE_ANY_DIR   = 0,
  AXE_DEPTH_DIR = 1,
  AXE_HORZ_DIR  = 2,
  AXE_VERT_DIR  = 3
};

class OCTINTERP_API axes : public base_graphics_object
{
public:

  class OCTINTERP_API properties : public base_properties
  {
  public:

    OCTINTERP_API void
    set_defaults (base_graphics_object& obj, const std::string& mode);

    OCTINTERP_API void
    remove_child (const graphics_handle& h, bool from_root = false);

    OCTINTERP_API void adopt (const graphics_handle& h);

    const scaler& get_x_scaler (void) const { return m_sx; }
    const scaler& get_y_scaler (void) const { return m_sy; }
    const scaler& get_z_scaler (void) const { return m_sz; }

    OCTINTERP_API Matrix
    get_boundingbox (bool internal = false,
                     const Matrix& parent_pix_size = Matrix ()) const;
    OCTINTERP_API Matrix
    get_extent (bool with_text = false,
                bool only_text_height=false) const;

    OCTINTERP_API double
    get___fontsize_points__ (double box_pix_height = 0) const;

    void update_boundingbox (void)
    {
      if (units_is ("normalized"))
        {
          sync_positions ();
          base_properties::update_boundingbox ();
        }
    }

    OCTINTERP_API void update_camera (void);
    OCTINTERP_API void update_axes_layout (void);
    OCTINTERP_API void update_aspectratios (void);
    void update_transform (void)
    {
      update_aspectratios ();
      update_camera ();
      update_axes_layout ();
    }

    OCTINTERP_API void sync_positions (void);

    // Redirect calls to "activepositionproperty" to "positionconstraint".

    std::string get_activepositionproperty (void) const
    {
      std::string cur_val;

      if (m_positionconstraint.is ("innerposition"))
        cur_val = "position";
      else
        cur_val = "outerposition";

      return cur_val;
    }

    void set_activepositionproperty (const octave_value& val)
    {
      // call set method to validate the input
      m_activepositionproperty.set (val);

      if (val.char_matrix_value ().row_as_string (0) == "position")
        set_positionconstraint ("innerposition");
      else
        set_positionconstraint (val);
    }

    // Redirect calls to "innerposition" to "position".

    octave_value get_innerposition (void) const
    {
      return get_position ();
    }

    void set_innerposition (const octave_value& val)
    {
      set_position (val);
    }

    OCTINTERP_API void update_autopos (const std::string& elem_type);
    OCTINTERP_API void update_xlabel_position (void);
    OCTINTERP_API void update_ylabel_position (void);
    OCTINTERP_API void update_zlabel_position (void);
    OCTINTERP_API void update_title_position (void);

    graphics_xform get_transform (void) const
    { return graphics_xform (m_x_render, m_x_render_inv, m_sx, m_sy, m_sz, m_x_zlim); }

    Matrix get_transform_matrix (void) const { return m_x_render; }
    Matrix get_inverse_transform_matrix (void) const { return m_x_render_inv; }
    Matrix get_opengl_matrix_1 (void) const { return m_x_gl_mat1; }
    Matrix get_opengl_matrix_2 (void) const { return m_x_gl_mat2; }
    Matrix get_transform_zlim (void) const { return m_x_zlim; }

    int get_xstate (void) const { return m_xstate; }
    int get_ystate (void) const { return m_ystate; }
    int get_zstate (void) const { return m_zstate; }
    double get_xPlane (void) const { return m_xPlane; }
    double get_xPlaneN (void) const { return m_xPlaneN; }
    double get_yPlane (void) const { return m_yPlane; }
    double get_yPlaneN (void) const { return m_yPlaneN; }
    double get_zPlane (void) const { return m_zPlane; }
    double get_zPlaneN (void) const { return m_zPlaneN; }
    double get_xpTick (void) const { return m_xpTick; }
    double get_xpTickN (void) const { return m_xpTickN; }
    double get_ypTick (void) const { return m_ypTick; }
    double get_ypTickN (void) const { return m_ypTickN; }
    double get_zpTick (void) const { return m_zpTick; }
    double get_zpTickN (void) const { return m_zpTickN; }
    double get_x_min (void) const { return std::min (m_xPlane, m_xPlaneN); }
    double get_x_max (void) const { return std::max (m_xPlane, m_xPlaneN); }
    double get_y_min (void) const { return std::min (m_yPlane, m_yPlaneN); }
    double get_y_max (void) const { return std::max (m_yPlane, m_yPlaneN); }
    double get_z_min (void) const { return std::min (m_zPlane, m_zPlaneN); }
    double get_z_max (void) const { return std::max (m_zPlane, m_zPlaneN); }
    double get_fx (void) const { return m_fx; }
    double get_fy (void) const { return m_fy; }
    double get_fz (void) const { return m_fz; }
    double get_xticklen (void) const { return m_xticklen; }
    double get_yticklen (void) const { return m_yticklen; }
    double get_zticklen (void) const { return m_zticklen; }
    double get_xtickoffset (void) const { return m_xtickoffset; }
    double get_ytickoffset (void) const { return m_ytickoffset; }
    double get_ztickoffset (void) const { return m_ztickoffset; }
    bool get_x2Dtop (void) const { return m_x2Dtop; }
    bool get_y2Dright (void) const { return m_y2Dright; }
    bool get_layer2Dtop (void) const { return m_layer2Dtop; }
    bool get_is2D (bool include_kids = false) const
    { return (include_kids ? (m_is2D && ! m_has3Dkids) : m_is2D); }
    void set_has3Dkids (bool val) { m_has3Dkids = val; }
    bool get_xySym (void) const { return m_xySym; }
    bool get_xyzSym (void) const { return m_xyzSym; }
    bool get_zSign (void) const { return m_zSign; }
    bool get_nearhoriz (void) const { return m_nearhoriz; }

    ColumnVector pixel2coord (double px, double py) const
    { return get_transform ().untransform (px, py, (m_x_zlim(0)+m_x_zlim(1))/2); }

    ColumnVector coord2pixel (double x, double y, double z) const
    { return get_transform ().transform (x, y, z); }

    OCTINTERP_API void
    zoom_about_point (const std::string& mode, double x, double y,
                      double factor, bool push_to_zoom_stack = true);
    OCTINTERP_API void
    zoom (const std::string& mode, double factor,
          bool push_to_zoom_stack = true);
    OCTINTERP_API void
    zoom (const std::string& mode, const Matrix& xl, const Matrix& yl,
          bool push_to_zoom_stack = true);

    OCTINTERP_API void
    translate_view (const std::string& mode,
                    double x0, double x1, double y0, double y1,
                    bool push_to_zoom_stack = true);

    OCTINTERP_API void
    pan (const std::string& mode, double factor,
         bool push_to_zoom_stack = true);

    OCTINTERP_API void
    rotate3d (double x0, double x1, double y0, double y1,
              bool push_to_zoom_stack = true);

    OCTINTERP_API void
    rotate_view (double delta_az, double delta_el,
                 bool push_to_zoom_stack = true);

    OCTINTERP_API void unzoom (void);
    OCTINTERP_API void update_handlevisibility (void);
    OCTINTERP_API void push_zoom_stack (void);
    OCTINTERP_API void clear_zoom_stack (bool do_unzoom = true);

    OCTINTERP_API void update_units (const caseless_str& old_units);

    OCTINTERP_API void update_font (std::string prop = "");

    OCTINTERP_API void update_fontunits (const caseless_str& old_fontunits);

    void increase_num_lights (void) { m_num_lights++; }
    void decrease_num_lights (void) { m_num_lights--; }
    unsigned int get_num_lights (void) const { return m_num_lights; }

  private:

    scaler m_sx = scaler ();
    scaler m_sy = scaler ();
    scaler m_sz = scaler ();

    Matrix m_x_render = Matrix ();
    Matrix m_x_render_inv = Matrix ();
    Matrix m_x_gl_mat1 = Matrix ();
    Matrix m_x_gl_mat2 = Matrix ();
    Matrix m_x_zlim = Matrix ();

    std::list<octave_value> m_zoom_stack = std::list<octave_value> ();

    // Axes layout data
    int m_xstate = 0;
    int m_ystate = 0;
    int m_zstate = 0;

    double m_xPlane = 0.0;
    double m_yPlane = 0.0;
    double m_zPlane = 0.0;

    double m_xPlaneN = 0.0;
    double m_yPlaneN = 0.0;
    double m_zPlaneN = 0.0;

    double m_xpTick = 0.0;
    double m_ypTick = 0.0;
    double m_zpTick = 0.0;

    double m_xpTickN = 0.0;
    double m_ypTickN = 0.0;
    double m_zpTickN = 0.0;

    double m_fx = 0.0;
    double m_fy = 0.0;
    double m_fz = 0.0;

    double m_xticklen = 0.0;
    double m_yticklen = 0.0;
    double m_zticklen = 0.0;

    double m_xtickoffset = 0.0;
    double m_ytickoffset = 0.0;
    double m_ztickoffset = 0.0;

    bool m_x2Dtop = false;
    bool m_y2Dright = false;
    bool m_layer2Dtop = false;
    bool m_is2D = false;
    bool m_has3Dkids = false;
    bool m_xySym = false;
    bool m_xyzSym = false;
    bool m_zSign = false;
    bool m_nearhoriz = false;

    unsigned int m_num_lights = 0;

    // Text renderer, used for calculation of text (tick labels) size
    octave::text_renderer m_txt_renderer;

    OCTINTERP_API void
    set_text_child (handle_property& h, const std::string& who,
                    const octave_value& v);

    OCTINTERP_API void
    delete_text_child (handle_property& h, bool from_root = false);

    // 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 (axes)
      radio_property activepositionproperty gsh , "{outerposition}|position"
      row_vector_property alim m , default_lim ()
      radio_property alimmode , "{auto}|manual"
      // FIXME: not yet implemented
      array_property alphamap , Matrix ()
      radio_property alphascale , "{linear}|log"
      color_property ambientlightcolor , color_values (1, 1, 1)
      bool_property box u , "off"
      radio_property boxstyle , "{back}|full"
      row_vector_property cameraposition mu , Matrix (1, 3, 0.0)
      radio_property camerapositionmode u , "{auto}|manual"
      row_vector_property cameratarget mu , Matrix (1, 3, 0.0)
      radio_property cameratargetmode u , "{auto}|manual"
      row_vector_property cameraupvector mu , Matrix (1, 3, 0.0)
      radio_property cameraupvectormode u , "{auto}|manual"
      double_property cameraviewangle mu , 6.6086
      radio_property cameraviewanglemode u , "{auto}|manual"
      row_vector_property clim m , default_lim ()
      radio_property climmode al , "{auto}|manual"
      radio_property clippingstyle , "{3dbox}|rectangle"
      color_property color , color_property (color_values (1, 1, 1), radio_values ("none"))
      array_property colormap sg , Matrix ()
      array_property colororder , default_colororder ()
      double_property colororderindex , 1.0
      radio_property colorscale , "{linear}|log"
      array_property currentpoint , Matrix (2, 3, 0.0)
      row_vector_property dataaspectratio mu , Matrix (1, 3, 1.0)
      radio_property dataaspectratiomode u , "{auto}|manual"
      radio_property fontangle u , "{normal}|italic"
      string_property fontname u , OCTAVE_DEFAULT_FONTNAME
      double_property fontsize mu , 10
      // FIXME: not yet implemented
      radio_property fontsizemode , "{auto}|manual"
      bool_property fontsmoothing u , "on"
      radio_property fontunits SU , "{points}|inches|centimeters|normalized|pixels"
      radio_property fontweight u , "{normal}|bold"
      double_property gridalpha m , 0.15
      radio_property gridalphamode , "{auto}|manual"
      color_property gridcolor m , color_property (color_values (0.15, 0.15, 0.15), radio_values ("none"))
      radio_property gridcolormode , "{auto}|manual"
      radio_property gridlinestyle , "{-}|--|:|-.|none"
      array_property innerposition sg , default_axes_position ()
      // FIXME: Should be an array of "interaction objects". Make it read-only for now.
      any_property interactions r , Matrix ()
      double_property labelfontsizemultiplier u , 1.1
      radio_property layer u , "{bottom}|top"
      // FIXME: Should be a "layoutoptions" object. Make it read-only for now.
      handle_property layout r , graphics_handle ()
      // FIXME: Should be a "legend" object. Make it read-only for now.
      handle_property legend r , graphics_handle ()
      // FIXME: should be kind of string array.
      any_property linestyleorder S , "-"
      double_property linestyleorderindex , 1.0
      double_property linewidth , 0.5
      double_property minorgridalpha m , 0.25
      radio_property minorgridalphamode , "{auto}|manual"
      color_property minorgridcolor m , color_property (color_values (0.1, 0.1, 0.1), radio_values ("none"))
      radio_property minorgridcolormode , "{auto}|manual"
      radio_property minorgridlinestyle , "{:}|-|--|-.|none"
      radio_property nextplot , "{replace}|add|replacechildren"
      double_property nextseriesindex r , 1.0
      array_property outerposition u , default_axes_outerposition ()
      row_vector_property plotboxaspectratio mu , Matrix (1, 3, 1.0)
      radio_property plotboxaspectratiomode u , "{auto}|manual"
      array_property position u , default_axes_position ()
      radio_property positionconstraint , "{outerposition}|innerposition"
      radio_property projection , "{orthographic}|perspective"
      radio_property sortmethod , "{depth}|childorder"
      radio_property tickdir mu , "{in}|out"
      radio_property tickdirmode u , "{auto}|manual"
      // FIXME: Added recently to Matlab, should replace interpreter property.
      radio_property ticklabelinterpreter u , "{tex}|latex|none"
      array_property ticklength u , default_axes_ticklength ()
      array_property tightinset r , Matrix (1, 4, 0.0)
      handle_property title SOf , make_graphics_handle ("text", m___myhandle__, false, false, false)
      double_property titlefontsizemultiplier u , 1.1
      radio_property titlefontweight u , "{bold}|normal"
      // FIXME: Should be a "axestoolbar" object. Make it read-only for now.
      handle_property toolbar r , graphics_handle ()
      radio_property units SU , "{normalized}|inches|centimeters|points|pixels|characters"
      array_property view u , default_axes_view ()
      // FIXME: Should be a "ruler" object. Make it read-only for now.
      handle_property xaxis r , graphics_handle ()
      radio_property xaxislocation u , "{bottom}|top|origin"
      color_property xcolor mu , color_property (color_values (0.15, 0.15, 0.15), radio_values ("none"))
      radio_property xcolormode , "{auto}|manual"
      radio_property xdir u , "{normal}|reverse"
      bool_property xgrid , "off"
      handle_property xlabel SOf , make_graphics_handle ("text", m___myhandle__, false, false, false)
      row_vector_property xlim mu , default_lim ()
      radio_property xlimmode al , "{auto}|manual"
      bool_property xminorgrid , "off"
      bool_property xminortick , "off"
      radio_property xscale alu , "{linear}|log"
      row_vector_property xtick mu , default_axes_tick ()
      // FIXME: should be kind of string array.
      any_property xticklabel S , ""
      radio_property xticklabelmode u , "{auto}|manual"
      double_property xticklabelrotation , 0.0
      radio_property xtickmode u , "{auto}|manual"
      // FIXME: Should be a "ruler" object. Make it read-only for now.
      handle_property yaxis r , graphics_handle ()
      radio_property yaxislocation u , "{left}|right|origin"
      color_property ycolor mu , color_property (color_values (0.15, 0.15, 0.15), radio_values ("none"))
      radio_property ycolormode , "{auto}|manual"
      radio_property ydir u , "{normal}|reverse"
      bool_property ygrid , "off"
      handle_property ylabel SOf , make_graphics_handle ("text", m___myhandle__, false, false, false)
      row_vector_property ylim mu , default_lim ()
      radio_property ylimmode al , "{auto}|manual"
      bool_property yminorgrid , "off"
      bool_property yminortick , "off"
      radio_property yscale alu , "{linear}|log"
      row_vector_property ytick mu , default_axes_tick ()
      any_property yticklabel S , ""
      radio_property yticklabelmode u , "{auto}|manual"
      double_property yticklabelrotation , 0.0
      radio_property ytickmode u , "{auto}|manual"
      // FIXME: Should be a "ruler" object. Make it read-only for now.
      handle_property zaxis r , graphics_handle ()
      color_property zcolor mu , color_property (color_values (0.15, 0.15, 0.15), radio_values ("none"))
      radio_property zcolormode , "{auto}|manual"
      radio_property zdir u , "{normal}|reverse"
      bool_property zgrid , "off"
      handle_property zlabel SOf , make_graphics_handle ("text", m___myhandle__, false, false, false)
      row_vector_property zlim mu , default_lim ()
      radio_property zlimmode al , "{auto}|manual"
      bool_property zminorgrid , "off"
      bool_property zminortick , "off"
      radio_property zscale alu , "{linear}|log"
      row_vector_property ztick mu , default_axes_tick ()
      any_property zticklabel S , ""
      radio_property zticklabelmode u , "{auto}|manual"
      double_property zticklabelrotation , 0.0
      radio_property ztickmode u , "{auto}|manual"

      // Octave-specific properties
      array_property __colormap__ hu , Matrix ()
      double_property mousewheelzoom , 0.5

      // hidden properties for alignment of subplots
      radio_property __autopos_tag__ h , "{none}|subplot"
      // hidden properties for inset
      array_property looseinset hu , Matrix (1, 4, 0.0)
      // hidden properties for minor ticks
      row_vector_property xminortickvalues h , Matrix ()
      row_vector_property yminortickvalues h , Matrix ()
      row_vector_property zminortickvalues h , Matrix ()
      // hidden property for text rendering
      double_property __fontsize_points__ hgr , 0
   END_PROPERTIES

  protected:
    OCTINTERP_API void init (void);

  private:

    std::string
    get_scale (const std::string& scale, const Matrix& lims)
    {
      std::string retval = scale;

      if (scale == "log" && lims.numel () > 1 && lims(0) < 0 && lims(1) < 0)
        retval = "neglog";

      return retval;
    }

    void update_xscale (void)
    {
      m_sx = get_scale (get_xscale (), m_xlim.get ().matrix_value ());
    }

    void update_yscale (void)
    {
      m_sy = get_scale (get_yscale (), m_ylim.get ().matrix_value ());
    }

    void update_zscale (void)
    {
      m_sz = get_scale (get_zscale (), m_zlim.get ().matrix_value ());
    }

    OCTINTERP_API void
    update_label_color (handle_property label, color_property col);
    void update_xcolor (void)
    { update_label_color (m_xlabel, m_xcolor); }

    void update_ycolor (void)
    { update_label_color (m_ylabel, m_ycolor); }

    void update_zcolor (void)
    { update_label_color (m_zlabel, m_zcolor); }

    void update_view (void) { sync_positions (); }

    void update_cameraposition (void) { update_transform (); }
    void update_cameratarget (void) { update_transform (); }
    void update_cameraupvector (void) { update_transform (); }
    void update_cameraviewangle (void) { update_transform (); }

    void update_camerapositionmode (void)
    {
      if (camerapositionmode_is ("auto"))
        update_cameraposition ();
    }
    void update_cameratargetmode (void)
    {
      if (cameratargetmode_is ("auto"))
        update_cameratarget ();
    }
    void update_cameraupvectormode (void)
    {
      if (cameraupvectormode_is ("auto"))
        update_cameraupvector ();
    }
    void update_cameraviewanglemode (void)
    {
      if (cameraviewanglemode_is ("auto"))
        update_cameraviewangle ();
    }

    void update_dataaspectratio (void) { sync_positions (); }
    void update_dataaspectratiomode (void) { sync_positions (); }
    void update_plotboxaspectratio (void) { sync_positions (); }
    void update_plotboxaspectratiomode (void) { sync_positions (); }

    void update_layer (void) { update_axes_layout (); }
    void update_box (void)
    {
      if (m_xticklabelmode.is ("auto"))
        calc_ticklabels (m_xtick, m_xticklabel, m_xscale.is ("log"),
                         xaxislocation_is ("origin"),
                         m_yscale.is ("log") ? 2 :
                           (yaxislocation_is ("origin") ? 0 :
                             (yaxislocation_is ("left") ? -1 : 1)),
                         m_xlim);
      if (m_yticklabelmode.is ("auto"))
        calc_ticklabels (m_ytick, m_yticklabel, m_yscale.is ("log"),
                         yaxislocation_is ("origin"),
                         m_xscale.is ("log") ? 2 :
                           (xaxislocation_is ("origin") ? 0 :
                             (xaxislocation_is ("bottom") ? -1 : 1)),
                         m_ylim);
    }
    void update_yaxislocation (void)
    {
      sync_positions ();
      update_axes_layout ();
      if (m_xticklabelmode.is ("auto"))
        calc_ticklabels (m_xtick, m_xticklabel, m_xscale.is ("log"),
                         xaxislocation_is ("origin"),
                         m_yscale.is ("log") ? 2 :
                           (yaxislocation_is ("origin") ? 0 :
                             (yaxislocation_is ("left") ? -1 : 1)),
                         m_xlim);
      if (m_yticklabelmode.is ("auto"))
        calc_ticklabels (m_ytick, m_yticklabel, m_yscale.is ("log"),
                         yaxislocation_is ("origin"),
                         m_xscale.is ("log") ? 2 :
                           (xaxislocation_is ("origin") ? 0 :
                             (xaxislocation_is ("bottom") ? -1 : 1)),
                         m_ylim);
      update_ylabel_position ();
    }
    void update_xaxislocation (void)
    {
      sync_positions ();
      update_axes_layout ();
      if (m_xticklabelmode.is ("auto"))
        calc_ticklabels (m_xtick, m_xticklabel, m_xscale.is ("log"),
                         xaxislocation_is ("origin"),
                         m_yscale.is ("log") ? 2 :
                           (yaxislocation_is ("origin") ? 0 :
                             (yaxislocation_is ("left") ? -1 : 1)),
                         m_xlim);
      if (m_yticklabelmode.is ("auto"))
        calc_ticklabels (m_ytick, m_yticklabel, m_yscale.is ("log"),
                         yaxislocation_is ("origin"),
                         m_xscale.is ("log") ? 2 :
                           (xaxislocation_is ("origin") ? 0 :
                             (xaxislocation_is ("bottom") ? -1 : 1)),
                         m_ylim);
      update_xlabel_position ();
    }

    void update_xdir (void) { update_camera (); update_axes_layout (); }
    void update_ydir (void) { update_camera (); update_axes_layout (); }
    void update_zdir (void) { update_camera (); update_axes_layout (); }

    void update_ticklength (void);
    void update_tickdir (void) { update_ticklength (); }
    void update_tickdirmode (void) { update_ticklength (); }

    void update_ticklabelinterpreter (void)
    {
      update_xtick (false);
      update_ytick (false);
      update_ztick (true);
    }

    void update_xtick (bool sync_pos = true)
    {
      calc_ticks_and_lims (m_xlim, m_xtick, m_xminortickvalues,
                           m_xlimmode.is ("auto"), m_xtickmode.is ("auto"),
                           m_xscale.is ("log"));
      if (m_xticklabelmode.is ("auto"))
        calc_ticklabels (m_xtick, m_xticklabel, m_xscale.is ("log"),
                         xaxislocation_is ("origin"),
                         m_yscale.is ("log") ? 2 :
                           (yaxislocation_is ("origin") ? 0 :
                             (yaxislocation_is ("left") ? -1 : 1)),
                         m_xlim);

      if (sync_pos)
        sync_positions ();
    }
    void update_ytick (bool sync_pos = true)
    {
      calc_ticks_and_lims (m_ylim, m_ytick, m_yminortickvalues,
                           m_ylimmode.is ("auto"), m_ytickmode.is ("auto"),
                           m_yscale.is ("log"));
      if (m_yticklabelmode.is ("auto"))
        calc_ticklabels (m_ytick, m_yticklabel, m_yscale.is ("log"),
                         yaxislocation_is ("origin"),
                         m_xscale.is ("log") ? 2 :
                           (xaxislocation_is ("origin") ? 0 :
                             (xaxislocation_is ("bottom") ? -1 : 1)),
                         m_ylim);

      if (sync_pos)
        sync_positions ();
    }
    void update_ztick (bool sync_pos = true)
    {
      calc_ticks_and_lims (m_zlim, m_ztick, m_zminortickvalues,
                           m_zlimmode.is ("auto"), m_ztickmode.is ("auto"),
                           m_zscale.is ("log"));
      if (m_zticklabelmode.is ("auto"))
        calc_ticklabels (m_ztick, m_zticklabel, m_zscale.is ("log"), false,
                         2, m_zlim);

      if (sync_pos)
        sync_positions ();
    }

    void update_xtickmode (void)
    {
      if (m_xtickmode.is ("auto"))
        update_xtick ();
    }
    void update_ytickmode (void)
    {
      if (m_ytickmode.is ("auto"))
        update_ytick ();
    }
    void update_ztickmode (void)
    {
      if (m_ztickmode.is ("auto"))
        update_ztick ();
    }

    void update_xticklabelmode (void)
    {
      if (m_xticklabelmode.is ("auto"))
        calc_ticklabels (m_xtick, m_xticklabel, m_xscale.is ("log"),
                         xaxislocation_is ("origin"),
                         m_yscale.is ("log") ? 2 :
                           (yaxislocation_is ("origin") ? 0 :
                             (yaxislocation_is ("left") ? -1 : 1)),
                         m_xlim);
    }
    void update_yticklabelmode (void)
    {
      if (m_yticklabelmode.is ("auto"))
        calc_ticklabels (m_ytick, m_yticklabel, m_yscale.is ("log"),
                         yaxislocation_is ("origin"),
                         m_xscale.is ("log") ? 2 :
                           (xaxislocation_is ("origin") ? 0 :
                             (xaxislocation_is ("bottom") ? -1 : 1)),
                         m_ylim);
    }
    void update_zticklabelmode (void)
    {
      if (m_zticklabelmode.is ("auto"))
        calc_ticklabels (m_ztick, m_zticklabel, m_zscale.is ("log"), false, 2, m_zlim);
    }

    void update_fontname (void)
    {
      update_font ("fontname");
      sync_positions ();
    }
    void update_fontsize (void)
    {
      update_font ("fontsize");
      sync_positions ();
    }
    void update_fontsmoothing (void)
    {
      update_font ("fontsmoothing");
    }
    void update_fontangle (void)
    {
      update_font ("fontangle");
      sync_positions ();
    }
    void update_fontweight (void)
    {
      update_font ("fontweight");
      sync_positions ();
    }

    void update_titlefontsizemultiplier (void)
    {
      // update_font handles title and axis labels
      update_font ("fontsize");
      sync_positions ();
    }

    void update_labelfontsizemultiplier (void)
    {
      update_font ("fontsize");
      sync_positions ();
    }

    void update_titlefontweight (void)
    {
      // update_font handles title and axis labels
      update_font ("fontweight");
      sync_positions ();
    }

    OCTINTERP_API void update_outerposition (void);
    OCTINTERP_API void update_position (void);
    OCTINTERP_API void update_looseinset (void);

    OCTINTERP_API double calc_tick_sep (double minval, double maxval);
    OCTINTERP_API void
    calc_ticks_and_lims (array_property& lims, array_property& ticks,
                         array_property& mticks, bool limmode_is_auto,
                         bool tickmode_is_auto, bool is_logscale);
    OCTINTERP_API void
    calc_ticklabels (const array_property& ticks, any_property& labels,
                     bool is_logscale, const bool is_origin,
                     const int other_axislocation,
                     const array_property& axis_lims);
    OCTINTERP_API Matrix
    get_ticklabel_extents (const Matrix& ticks,
                           const string_vector& ticklabels,
                           const Matrix& limits);

    void fix_limits (array_property& lims)
    {
      if (lims.get ().isempty ())
        return;

      Matrix l = lims.get ().matrix_value ();
      if (l(0) > l(1))
        {
          l(0) = 0;
          l(1) = 1;
          lims = l;
        }
      else if (l(0) == l(1))
        {
          l(0) -= 0.5;
          l(1) += 0.5;
          lims = l;
        }
    }

    OCTINTERP_API Matrix calc_tightbox (const Matrix& init_pos);

    void set_colormap (const octave_value& val)
    {
      set___colormap__ (val);
    }

    void update___colormap__ (void)
    {
      m_colormap.run_listeners (GCB_POSTSET);
    }

    OCTINTERP_API octave_value get_colormap (void) const;

  public:
    OCTINTERP_API Matrix
    get_axis_limits (double xmin, double xmax,
                     double min_pos, double max_neg,
                     const bool logscale);

    OCTINTERP_API void
    check_axis_limits (Matrix &limits, const Matrix kids,
                       const bool logscale, char &update_type);

    void update_xlim ()
    {
      update_axis_limits ("xlim");

      calc_ticks_and_lims (m_xlim, m_xtick, m_xminortickvalues,
                           m_xlimmode.is ("auto"), m_xtickmode.is ("auto"),
                           m_xscale.is ("log"));
      if (m_xticklabelmode.is ("auto"))
        calc_ticklabels (m_xtick, m_xticklabel, m_xscale.is ("log"),
                         m_xaxislocation.is ("origin"),
                         m_yscale.is ("log") ? 2 :
                           (yaxislocation_is ("origin") ? 0 :
                             (yaxislocation_is ("left") ? -1 : 1)),
                         m_xlim);

      fix_limits (m_xlim);

      update_xscale ();

      update_axes_layout ();
    }

    void update_ylim (void)
    {
      update_axis_limits ("ylim");

      calc_ticks_and_lims (m_ylim, m_ytick, m_yminortickvalues,
                           m_ylimmode.is ("auto"), m_ytickmode.is ("auto"),
                           m_yscale.is ("log"));
      if (m_yticklabelmode.is ("auto"))
        calc_ticklabels (m_ytick, m_yticklabel, m_yscale.is ("log"),
                         yaxislocation_is ("origin"),
                         m_xscale.is ("log") ? 2 :
                           (xaxislocation_is ("origin") ? 0 :
                             (xaxislocation_is ("bottom") ? -1 : 1)),
                         m_ylim);

      fix_limits (m_ylim);

      update_yscale ();

      update_axes_layout ();
    }

    void update_zlim (void)
    {
      update_axis_limits ("zlim");

      calc_ticks_and_lims (m_zlim, m_ztick, m_zminortickvalues,
                           m_zlimmode.is ("auto"), m_ztickmode.is ("auto"),
                           m_zscale.is ("log"));
      if (m_zticklabelmode.is ("auto"))
        calc_ticklabels (m_ztick, m_zticklabel, m_zscale.is ("log"), false,
                         2, m_zlim);

      fix_limits (m_zlim);

      update_zscale ();

      update_axes_layout ();
    }

    void trigger_normals_calc (void);

  };

private:
  properties m_properties;

public:
  axes (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), m_properties (mh, p), m_default_properties ()
  {
    m_properties.update_transform ();
  }

  ~axes (void) = default;

  void override_defaults (base_graphics_object& obj)
  {
    // Allow parent (figure) to override first (properties knows how
    // to find the parent object).
    m_properties.override_defaults (obj);

    // Now override with our defaults.  If the default_properties
    // list includes the properties for all defaults (line,
    // surface, etc.) then we don't have to know the type of OBJ
    // here, we just call its set function and let it decide which
    // properties from the list to use.
    obj.set_from_list (m_default_properties);
  }

  void set (const caseless_str& name, const octave_value& value)
  {
    if (name.compare ("default", 7))
      // strip "default", pass rest to function that will
      // parse the remainder and add the element to the
      // default_properties map.
      m_default_properties.set (name.substr (7), value);
    else
      m_properties.set (name, value);
  }

  void set_defaults (const std::string& mode)
  {
    m_properties.set_defaults (*this, mode);
  }

  octave_value get (const caseless_str& name) const
  {
    octave_value retval;

    // FIXME: finish this.
    if (name.compare ("default", 7))
      retval = get_default (name.substr (7));
    else
      retval = m_properties.get (name);

    return retval;
  }

  OCTINTERP_API octave_value get_default (const caseless_str& name) const;

  octave_value get_defaults (void) const
  {
    return m_default_properties.as_struct ("default");
  }

  property_list get_defaults_list (void) const
  {
    return m_default_properties;
  }

  base_properties& get_properties (void) { return m_properties; }

  const base_properties& get_properties (void) const { return m_properties; }

  OCTINTERP_API void update_axis_limits (const std::string& axis_type);

  OCTINTERP_API void update_axis_limits (const std::string& axis_type,
                                         const graphics_handle& h);

  bool valid_object (void) const { return true; }

  OCTINTERP_API void reset_default_properties (void);

  bool has_readonly_property (const caseless_str& pname) const
  {
    bool retval = m_properties.has_readonly_property (pname);
    if (! retval)
      retval = base_properties::has_readonly_property (pname);
    return retval;
  }

protected:
  OCTINTERP_API void initialize (const graphics_object& go);

private:
  property_list m_default_properties;
};

// ---------------------------------------------------------------------

class OCTINTERP_API line : public base_graphics_object
{
public:

  class OCTINTERP_API properties : public base_properties
  {
  public:

    // 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 (line)
      color_property color , color_property (color_values (0, 0, 0), radio_values ("none"))
      string_property displayname , ""
      radio_property linejoin , "{round}|miter|chamfer"
      radio_property linestyle , "{-}|--|:|-.|none"
      double_property linewidth , 0.5
      radio_property marker , "{none}|+|o|*|.|x|||_|s|square|d|diamond|^|v|>|<|p|pentagram|h|hexagram"
      color_property markeredgecolor , color_property (radio_values ("{auto}|none"), color_values (0, 0, 0))
      color_property markerfacecolor , color_property (radio_values ("auto|{none}"), color_values (0, 0, 0))
      double_property markersize , 6
      row_vector_property xdata u , default_data ()
      string_property xdatasource , ""
      row_vector_property ydata u , default_data ()
      string_property ydatasource , ""
      row_vector_property zdata u , Matrix ()
      string_property zdatasource , ""

      // hidden properties for limit computation
      row_vector_property xlim hlr , default_data_lim ()
      row_vector_property ylim hlr , default_data_lim ()
      row_vector_property zlim hlr , Matrix ()
      bool_property xliminclude hl , "on"
      bool_property yliminclude hl , "on"
      bool_property zliminclude hl , "on"
    END_PROPERTIES

  protected:
    void init (void)
    {
      m_linewidth.add_constraint ("min", 0, false);
      m_markersize.add_constraint ("min", 0, false);
    }

  private:
    OCTINTERP_API Matrix compute_xlim (void) const;
    OCTINTERP_API Matrix compute_ylim (void) const;

    void update_xdata (void) { set_xlim (compute_xlim ()); }

    void update_ydata (void) { set_ylim (compute_ylim ()); }

    void update_zdata (void) { set_zlim (m_zdata.get_limits ()); }
  };

private:
  properties m_properties;

public:
  line (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), m_properties (mh, p)
  { }

  ~line (void) = default;

  base_properties& get_properties (void) { return m_properties; }

  const base_properties& get_properties (void) const { return m_properties; }

  bool valid_object (void) const { return true; }

  bool has_readonly_property (const caseless_str& pname) const
  {
    bool retval = m_properties.has_readonly_property (pname);
    if (! retval)
      retval = base_properties::has_readonly_property (pname);
    return retval;
  }
};

// ---------------------------------------------------------------------

class OCTINTERP_API text : public base_graphics_object
{
public:

  class OCTINTERP_API properties : public base_properties
  {
  public:

    OCTINTERP_API double
    get___fontsize_points__ (double box_pix_height = 0) const;

    OCTINTERP_API void update_text_extent (void);

    OCTINTERP_API void update_font (void);

    void set_position (const octave_value& val)
    {
      octave_value new_val (val);

      if (new_val.numel () == 2)
        {
          dim_vector dv (1, 3);

          new_val = new_val.resize (dv, true);
        }

      if (m_position.set (new_val, false))
        {
          set_positionmode ("manual");
          update_position ();
          m_position.run_listeners (GCB_POSTSET);
          mark_modified ();
        }
      else
        set_positionmode ("manual");
    }

    // 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 (text)
      color_property backgroundcolor , color_property (radio_values ("{none}"), color_values (1, 1, 1))
      color_property color u , color_property (color_values (0, 0, 0), radio_values ("none"))
      color_property edgecolor , color_property (radio_values ("{none}"), color_values (0, 0, 0))
      bool_property editing , "off"
      array_property extent rG , Matrix (1, 4, 0.0)
      radio_property fontangle u , "{normal}|italic"
      string_property fontname u , OCTAVE_DEFAULT_FONTNAME
      double_property fontsize u , 10
      bool_property fontsmoothing u , "on"
      radio_property fontunits SU , "inches|centimeters|normalized|{points}|pixels"
      radio_property fontweight u , "{normal}|bold"
      radio_property horizontalalignment mu , "{left}|center|right"
      radio_property interpreter u , "{tex}|none|latex"
      radio_property linestyle , "{-}|--|:|-.|none"
      double_property linewidth , 0.5
      double_property margin , 3
      array_property position smu , Matrix (1, 3, 0.0)
      double_property rotation mu , 0
      text_label_property string u , ""
      radio_property units u , "{data}|pixels|normalized|inches|centimeters|points"
      radio_property verticalalignment mu , "top|cap|{middle}|baseline|bottom"

      // hidden properties for limit computation
      row_vector_property xlim hlr , Matrix ()
      row_vector_property ylim hlr , Matrix ()
      row_vector_property zlim hlr , Matrix ()
      bool_property xliminclude hl , "off"
      bool_property yliminclude hl , "off"
      bool_property zliminclude hl , "off"
      // hidden properties for auto-positioning
      radio_property positionmode hu , "{auto}|manual"
      radio_property rotationmode hu , "{auto}|manual"
      radio_property horizontalalignmentmode hu , "{auto}|manual"
      radio_property verticalalignmentmode hu , "{auto}|manual"
      radio_property __autopos_tag__ h , "{none}|xlabel|ylabel|zlabel|title"
      // hidden property for text rendering
      double_property __fontsize_points__ hgr , 0
    END_PROPERTIES

    OCTINTERP_API Matrix get_data_position (void) const;
    OCTINTERP_API Matrix get_extent_matrix (bool rotated = false) const;
    const uint8NDArray& get_pixels (void) const { return m_pixels; }

    // Text renderer, used for calculation of text size
    octave::text_renderer m_txt_renderer;

  protected:
    void init (void)
    {
      m_position.add_constraint (dim_vector (1, 3));
      m_fontsize.add_constraint ("min", 0.0, false);
      m_linewidth.add_constraint ("min", 0.0, false);
      m_margin.add_constraint ("min", 0.0, false);
      m_cached_units = get_units ();
      update_font ();
    }

  private:
    void update_position (void)
    {
      Matrix pos = get_data_position ();
      Matrix lim;

      lim = Matrix (1, 4, pos(0));
      lim(2) = (lim(2) <= 0 ? octave::numeric_limits<double>::Inf () : lim(2));
      lim(3) = (lim(3) >= 0 ? -octave::numeric_limits<double>::Inf () : lim(3));
      set_xlim (lim);

      lim = Matrix (1, 4, pos(1));
      lim(2) = (lim(2) <= 0 ? octave::numeric_limits<double>::Inf () : lim(2));
      lim(3) = (lim(3) >= 0 ? -octave::numeric_limits<double>::Inf () : lim(3));
      set_ylim (lim);

      if (pos.numel () == 3)
        {
          lim = Matrix (1, 4, pos(2));
          lim(2) = (lim(2) <= 0 ? octave::numeric_limits<double>::Inf ()
                                : lim(2));
          lim(3) = (lim(3) >= 0 ? -octave::numeric_limits<double>::Inf ()
                                : lim(3));
          set_zliminclude ("on");
          set_zlim (lim);
        }
      else
        set_zliminclude ("off");
    }

    OCTINTERP_API void request_autopos (void);
    void update_positionmode (void) { request_autopos (); }
    void update_rotationmode (void) { request_autopos (); }
    void update_horizontalalignmentmode (void) { request_autopos (); }
    void update_verticalalignmentmode (void) { request_autopos (); }

    void update_string (void) { request_autopos (); update_text_extent (); }
    void update_rotation (void) { update_text_extent (); }
    void update_fontname (void) { update_font (); update_text_extent (); }
    void update_fontsize (void) { update_font (); update_text_extent (); }
    void update_fontsmoothing (void) { update_font (); update_text_extent (); }

    void update_color (void)
    {
      if (! m_color.is ("none"))
        {
          update_font ();
          update_text_extent ();
        }
    }

    void update_fontangle (void)
    {
      update_font ();
      update_text_extent ();
    }
    void update_fontweight (void) { update_font (); update_text_extent (); }

    void update_interpreter (void) { update_text_extent (); }
    void update_horizontalalignment (void) { update_text_extent (); }
    void update_verticalalignment (void) { update_text_extent (); }

    OCTINTERP_API void update_units (void);
    OCTINTERP_API void update_fontunits (const caseless_str& old_fontunits);

  private:
    std::string m_cached_units;
    uint8NDArray m_pixels;
  };

private:
  properties m_properties;

public:
  text (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), m_properties (mh, p)
  {
    m_properties.set_clipping ("off");
  }

  ~text (void) = default;

  base_properties& get_properties (void) { return m_properties; }

  const base_properties& get_properties (void) const { return m_properties; }

  bool valid_object (void) const { return true; }

  bool has_readonly_property (const caseless_str& pname) const
  {
    bool retval = m_properties.has_readonly_property (pname);
    if (! retval)
      retval = base_properties::has_readonly_property (pname);
    return retval;
  }
};

// ---------------------------------------------------------------------

class OCTINTERP_API image : public base_graphics_object
{
public:

  class OCTINTERP_API properties : public base_properties
  {
  public:

    bool is_aliminclude (void) const
    { return (m_aliminclude.is_on () && m_alphadatamapping.is ("scaled")); }
    std::string get_aliminclude (void) const
    { return m_aliminclude.current_value (); }

    bool is_climinclude (void) const
    { return (m_climinclude.is_on () && m_cdatamapping.is ("scaled")); }
    std::string get_climinclude (void) const
    { return m_climinclude.current_value (); }

    OCTINTERP_API octave_value get_color_data (void) const;

    void initialize_data (void) { update_cdata (); }

    // 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 (image)
      array_property alphadata u , Matrix (1, 1, 1.0)
      radio_property alphadatamapping al , "{none}|direct|scaled"
      array_property cdata u , default_image_cdata ()
      radio_property cdatamapping al , "scaled|{direct}"
      row_vector_property xdata mu , Matrix ()
      row_vector_property ydata mu , Matrix ()
      // hidden properties for limit computation
      row_vector_property alim hlr , Matrix ()
      row_vector_property clim hlr , Matrix ()
      row_vector_property xlim hlr , Matrix ()
      row_vector_property ylim hlr , Matrix ()
      bool_property aliminclude hlg , "on"
      bool_property climinclude hlg , "on"
      bool_property xliminclude hl , "on"
      bool_property yliminclude hl , "on"
      radio_property xdatamode ha , "{auto}|manual"
      radio_property ydatamode ha , "{auto}|manual"
    END_PROPERTIES

  protected:
    void init (void)
    {
      m_xdata.add_constraint (2);
      m_xdata.add_constraint (dim_vector (0, 0));
      m_ydata.add_constraint (2);
      m_ydata.add_constraint (dim_vector (0, 0));
      m_cdata.add_constraint ("double");
      m_cdata.add_constraint ("single");
      m_cdata.add_constraint ("logical");
      m_cdata.add_constraint ("int8");
      m_cdata.add_constraint ("int16");
      m_cdata.add_constraint ("int32");
      m_cdata.add_constraint ("int64");
      m_cdata.add_constraint ("uint8");
      m_cdata.add_constraint ("uint16");
      m_cdata.add_constraint ("uint32");
      m_cdata.add_constraint ("uint64");
      m_cdata.add_constraint ("real");
      m_cdata.add_constraint (dim_vector (-1, -1));
      m_cdata.add_constraint (dim_vector (-1, -1, 3));
      m_alphadata.add_constraint ("double");
      m_alphadata.add_constraint ("uint8");
      m_alphadata.add_constraint (dim_vector (-1, -1));
    }

  private:
    void update_alphadata (void)
    {
      if (alphadatamapping_is ("scaled"))
        set_alim (m_alphadata.get_limits ());
      else
        m_alim = m_alphadata.get_limits ();
    }

    void update_cdata (void)
    {
      if (cdatamapping_is ("scaled"))
        set_clim (m_cdata.get_limits ());
      else
        m_clim = m_cdata.get_limits ();

      if (m_xdatamode.is ("auto"))
        update_xdata ();

      if (m_ydatamode.is ("auto"))
        update_ydata ();
    }

    void update_xdata (void)
    {
      if (m_xdata.get ().isempty ())
        set_xdatamode ("auto");

      if (m_xdatamode.is ("auto"))
        {
          set_xdata (get_auto_xdata ());
          set_xdatamode ("auto");
        }

      Matrix limits = m_xdata.get_limits ();
      float dp = pixel_xsize ();

      limits(0) = limits(0) - dp;
      limits(1) = limits(1) + dp;
      set_xlim (limits);
    }

    void update_ydata (void)
    {
      if (m_ydata.get ().isempty ())
        set_ydatamode ("auto");

      if (m_ydatamode.is ("auto"))
        {
          set_ydata (get_auto_ydata ());
          set_ydatamode ("auto");
        }

      Matrix limits = m_ydata.get_limits ();
      float dp = pixel_ysize ();

      limits(0) = limits(0) - dp;
      limits(1) = limits(1) + dp;
      set_ylim (limits);
    }

    Matrix get_auto_xdata (void)
    {
      dim_vector dv = get_cdata ().dims ();
      Matrix data;
      if (dv(1) > 0.)
        {
          data = Matrix (1, 2, 1);
          data(1) = dv(1);
        }
      return data;
    }

    Matrix get_auto_ydata (void)
    {
      dim_vector dv = get_cdata ().dims ();
      Matrix data;
      if (dv(0) > 0.)
        {
          data = Matrix (1, 2, 1);
          data(1) = dv(0);
        }
      return data;
    }

    float pixel_size (octave_idx_type dim, const Matrix limits)
    {
      octave_idx_type l = dim - 1;
      float dp;

      if (l > 0 && limits(0) != limits(1))
        dp = (limits(1) - limits(0))/(2*l);
      else
        {
          if (limits(1) == limits(2))
            dp = 0.5;
          else
            dp = (limits(1) - limits(0))/2;
        }
      return dp;
    }

  public:
    float pixel_xsize (void)
    {
      return pixel_size ((get_cdata ().dims ())(1), m_xdata.get_limits ());
    }

    float pixel_ysize (void)
    {
      return pixel_size ((get_cdata ().dims ())(0), m_ydata.get_limits ());
    }
  };

private:
  properties m_properties;

public:
  image (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), m_properties (mh, p)
  {
    m_properties.initialize_data ();
  }

  ~image (void) = default;

  base_properties& get_properties (void) { return m_properties; }

  const base_properties& get_properties (void) const { return m_properties; }

  bool valid_object (void) const { return true; }

  bool has_readonly_property (const caseless_str& pname) const
  {
    bool retval = m_properties.has_readonly_property (pname);
    if (! retval)
      retval = base_properties::has_readonly_property (pname);
    return retval;
  }
};

// ---------------------------------------------------------------------

class OCTINTERP_API light : public base_graphics_object
{
public:

  class OCTINTERP_API properties : public base_properties
  {
    // 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 (light)
      color_property color , color_values (1, 1, 1)
      array_property position , default_light_position ()
      radio_property style , "{infinite}|local"
    END_PROPERTIES

  protected:
    void init (void)
    {
      m_position.add_constraint (dim_vector (1, 3));
    }

  private:
    OCTINTERP_API void update_visible (void);
  };

private:
  properties m_properties;

public:
  light (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), m_properties (mh, p)
  { }

  ~light (void) = default;

  base_properties& get_properties (void) { return m_properties; }

  const base_properties& get_properties (void) const { return m_properties; }

  bool valid_object (void) const { return true; }

  bool has_readonly_property (const caseless_str& pname) const
  {
    bool retval = m_properties.has_readonly_property (pname);
    if (! retval)
      retval = base_properties::has_readonly_property (pname);
    return retval;
  }

protected:
  OCTINTERP_API void initialize (const graphics_object& go);
};

// ---------------------------------------------------------------------

class OCTINTERP_API patch : public base_graphics_object
{
public:

  class OCTINTERP_API properties : public base_properties
  {
  public:

    octave_value get_color_data (void) const;

    // Matlab allows incoherent data to be stored into patch properties.
    // The patch should then be ignored by the renderer.
    bool has_bad_data (std::string& msg) const
    {
      msg = m_bad_data_msg;
      return ! msg.empty ();
    }

    bool is_aliminclude (void) const
    { return (m_aliminclude.is_on () && m_alphadatamapping.is ("scaled")); }
    std::string get_aliminclude (void) const
    { return m_aliminclude.current_value (); }

    bool is_climinclude (void) const
    { return (m_climinclude.is_on () && m_cdatamapping.is ("scaled")); }
    std::string get_climinclude (void) const
    { return m_climinclude.current_value (); }

    OCTINTERP_API bool get_do_lighting (void) const;

    std::vector<std::vector<octave_idx_type>> m_coplanar_last_idx;

    // 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 (patch)
      radio_property alphadatamapping l , "none|{scaled}|direct"
      double_property ambientstrength , 0.3
      radio_property backfacelighting , "unlit|lit|{reverselit}"
      array_property cdata u , Matrix ()
      radio_property cdatamapping l , "{scaled}|direct"
      double_property diffusestrength , 0.6
      string_property displayname , ""
      double_radio_property edgealpha , double_radio_property (1.0, radio_values ("flat|interp"))
      color_property edgecolor , color_property (color_values (0, 0, 0), radio_values ("none|flat|interp"))
      radio_property edgelighting u , "{none}|flat|gouraud|phong"
      double_radio_property facealpha , double_radio_property (1.0, radio_values ("flat|interp"))
      color_property facecolor , color_property (color_values (0, 0, 0), radio_values ("none|flat|interp"))
      radio_property facelighting u , "none|{flat}|gouraud|phong"
      array_property facenormals m , Matrix ()
      radio_property facenormalsmode u , "{auto}|manual"
      array_property faces u , default_patch_faces ()
      array_property facevertexalphadata , Matrix ()
      array_property facevertexcdata u , Matrix ()
      radio_property linestyle , "{-}|--|:|-.|none"
      double_property linewidth , 0.5
      radio_property marker , "{none}|+|o|*|.|x|||_|s|square|d|diamond|^|v|>|<|p|pentagram|h|hexagram"
      color_property markeredgecolor , color_property (radio_values ("none|{auto}|flat"), color_values (0, 0, 0))
      color_property markerfacecolor , color_property (radio_values ("{none}|auto|flat"), color_values (0, 0, 0))
      double_property markersize , 6
      double_property specularcolorreflectance , 1.0
      double_property specularexponent , 10.0
      double_property specularstrength , 0.9
      array_property vertexnormals m , Matrix ()
      radio_property vertexnormalsmode u , "{auto}|manual"
      array_property vertices u , default_patch_vertices ()
      array_property xdata u , default_patch_xdata ()
      array_property ydata u , default_patch_ydata ()
      array_property zdata u , Matrix ()

      // hidden properties for limit computation
      row_vector_property alim hlr , Matrix ()
      row_vector_property clim hlr , Matrix ()
      row_vector_property xlim hlr , Matrix ()
      row_vector_property ylim hlr , Matrix ()
      row_vector_property zlim hlr , Matrix ()
      bool_property aliminclude hlg , "on"
      bool_property climinclude hlg , "on"
      bool_property xliminclude hl , "on"
      bool_property yliminclude hl , "on"
      bool_property zliminclude hl , "on"
    END_PROPERTIES

  protected:
    void init (void)
    {
      m_xdata.add_constraint (dim_vector (-1, -1));
      m_ydata.add_constraint (dim_vector (-1, -1));
      m_zdata.add_constraint (dim_vector (-1, -1));
      m_faces.add_constraint (dim_vector (-1, -1));
      m_vertices.add_constraint (dim_vector (-1, 2));
      m_vertices.add_constraint (dim_vector (-1, 3));
      m_cdata.add_constraint ("double");
      m_cdata.add_constraint ("single");
      m_cdata.add_constraint ("logical");
      m_cdata.add_constraint ("int8");
      m_cdata.add_constraint ("int16");
      m_cdata.add_constraint ("int32");
      m_cdata.add_constraint ("int64");
      m_cdata.add_constraint ("uint8");
      m_cdata.add_constraint ("uint16");
      m_cdata.add_constraint ("uint32");
      m_cdata.add_constraint ("uint64");
      m_cdata.add_constraint ("real");
      m_cdata.add_constraint (dim_vector (-1, -1));
      m_cdata.add_constraint (dim_vector (-1, -1, 3));
      m_facevertexcdata.add_constraint (dim_vector (-1, 1));
      m_facevertexcdata.add_constraint (dim_vector (-1, 3));
      m_facevertexcdata.add_constraint (dim_vector (0, 0));
      m_facevertexalphadata.add_constraint (dim_vector (-1, 1));
      m_facevertexalphadata.add_constraint (dim_vector (0, 0));
      m_facenormals.add_constraint (dim_vector (-1, 3));
      m_facenormals.add_constraint (dim_vector (0, 0));
      m_vertexnormals.add_constraint (dim_vector (-1, 3));
      m_vertexnormals.add_constraint (dim_vector (0, 0));

      m_ambientstrength.add_constraint ("min", 0.0, true);
      m_ambientstrength.add_constraint ("max", 1.0, true);
      m_diffusestrength.add_constraint ("min", 0.0, true);
      m_diffusestrength.add_constraint ("max", 1.0, true);
      m_linewidth.add_constraint ("min", 0.0, false);
      m_markersize.add_constraint ("min", 0.0, false);
      m_specularcolorreflectance.add_constraint ("min", 0.0, true);
      m_specularcolorreflectance.add_constraint ("max", 1.0, true);
      m_specularexponent.add_constraint ("min", 0.0, false);
      m_specularstrength.add_constraint ("min", 0.0, true);
      m_specularstrength.add_constraint ("max", 1.0, true);
    }

  public:
    void update_normals (bool reset, bool force = false)
    {
      update_face_normals (reset, force);
      update_vertex_normals (reset, force);
    }


  private:
    std::string m_bad_data_msg;

    void update_faces (void) { update_data ();}

    void update_vertices (void) { update_data ();}

    void update_facevertexcdata (void) { update_data ();}

    OCTINTERP_API void update_fvc (void);

    void update_xdata (void)
    {
      if (get_xdata ().isempty ())
        {
          // For compatibility with matlab behavior,
          // if x/ydata are set empty, silently empty other *data and
          // faces properties while vertices remain unchanged.
          set_ydata (Matrix ());
          set_zdata (Matrix ());
          set_cdata (Matrix ());
          set_faces (Matrix ());
        }
      else
        {
          update_fvc ();
          update_normals (true);
        }

      set_xlim (m_xdata.get_limits ());
    }

    void update_ydata (void)
    {
      if (get_ydata ().isempty ())
        {
          set_xdata (Matrix ());
          set_zdata (Matrix ());
          set_cdata (Matrix ());
          set_faces (Matrix ());
        }
      else
        {
          update_fvc ();
          update_normals (true);
        }

      set_ylim (m_ydata.get_limits ());
    }

    void update_zdata (void)
    {
      update_fvc ();
      update_normals (true);
      set_zlim (m_zdata.get_limits ());
    }

    void update_cdata (void)
    {
      update_fvc ();
      update_normals (false);

      if (cdatamapping_is ("scaled"))
        set_clim (m_cdata.get_limits ());
      else
        m_clim = m_cdata.get_limits ();
    }

    OCTINTERP_API void update_data (void);

    OCTINTERP_API void calc_face_normals (Matrix& normals);
    OCTINTERP_API void update_face_normals (bool reset, bool force = false);
    OCTINTERP_API void update_vertex_normals (bool reset, bool force = false);

    void update_edgelighting (void)
    {
      update_normals (false);
    }

    void update_facelighting (void)
    {
      update_normals (false);
    }

    void update_facenormalsmode (void)
    {
      update_face_normals (false);
    }

    void update_vertexnormalsmode (void)
    {
      update_vertex_normals (false);
    }

    void update_visible (void)
    {
      if (is_visible ())
        update_normals (false);
    }
  };

private:
  properties m_properties;
  property_list m_default_properties;

public:
  patch (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), m_properties (mh, p)
  { }

  ~patch (void) = default;

  base_properties& get_properties (void) { return m_properties; }

  const base_properties& get_properties (void) const { return m_properties; }

  bool valid_object (void) const { return true; }

  bool has_readonly_property (const caseless_str& pname) const
  {
    bool retval = m_properties.has_readonly_property (pname);
    if (! retval)
      retval = base_properties::has_readonly_property (pname);
    return retval;
  }

  OCTINTERP_API void reset_default_properties (void);

protected:
  OCTINTERP_API void initialize (const graphics_object& go);

};

// ---------------------------------------------------------------------

class OCTINTERP_API scatter : public base_graphics_object
{
public:

  class OCTINTERP_API properties : public base_properties
  {
  public:

    OCTINTERP_API octave_value get_color_data (void) const;

    // Matlab allows incoherent data to be stored in scatter properties.
    // The scatter object should then be ignored by the renderer.
    bool has_bad_data (std::string& msg) const
    {
      msg = m_bad_data_msg;
      return ! msg.empty ();
    }

    bool is_aliminclude (void) const
    { return m_aliminclude.is_on (); }
    std::string get_aliminclude (void) const
    { return m_aliminclude.current_value (); }

    bool is_climinclude (void) const
    { return m_climinclude.is_on (); }
    std::string get_climinclude (void) const
    { return m_climinclude.current_value (); }

    // 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 (scatter)
      array_property annotation , Matrix ()
      array_property cdata mu , Matrix ()
      radio_property cdatamode u , "{auto}|manual"
      string_property cdatasource , ""
      array_property datatiptemplate , Matrix ()
      string_property displayname , ""
      array_property latitudedata , Matrix ()
      string_property latitudedatasource , ""
      double_property linewidth , 0.5
      array_property longitudedata , Matrix ()
      string_property longitudedatasource , ""
      radio_property marker , "{o}|+|*|.|x|||_|s|square|d|diamond|^|v|>|<|p|pentagram|h|hexagram|none"
      double_property markeredgealpha , 1.0
      color_property markeredgecolor , color_property (radio_values ("{flat}|none"), color_values (0, 0, 0))
      double_property markerfacealpha , 1.0
      color_property markerfacecolor , color_property (radio_values ("{none}|auto|flat"), color_values (0, 0, 0))
      array_property rdata , Matrix ()
      string_property rdatasource , ""
      array_property seriesindex u , Matrix ()
      array_property sizedata u , Matrix ()
      string_property sizedatasource , ""
      array_property thetadata , Matrix ()
      string_property thetadatasource , ""
      array_property xdata u , Matrix ()
      string_property xdatasource , ""
      array_property ydata u , Matrix ()
      string_property ydatasource , ""
      array_property zdata u , Matrix ()
      string_property zdatasource , ""

      // hidden properties for limit computation
      row_vector_property alim hlr , Matrix ()
      row_vector_property clim hlr , Matrix ()
      row_vector_property xlim hlr , Matrix ()
      row_vector_property ylim hlr , Matrix ()
      row_vector_property zlim hlr , Matrix ()
      bool_property aliminclude hlg , "on"
      bool_property climinclude hlg , "on"
      bool_property xliminclude hl , "on"
      bool_property yliminclude hl , "on"
      bool_property zliminclude hl , "on"
    END_PROPERTIES

  protected:
    void init (void)
    {
      m_xdata.add_constraint (dim_vector (-1, 1));
      m_xdata.add_constraint (dim_vector (1, -1));
      m_xdata.add_constraint (dim_vector (-1, 0));
      m_xdata.add_constraint (dim_vector (0, -1));
      m_ydata.add_constraint (dim_vector (-1, 1));
      m_ydata.add_constraint (dim_vector (1, -1));
      m_ydata.add_constraint (dim_vector (-1, 0));
      m_ydata.add_constraint (dim_vector (0, -1));
      m_zdata.add_constraint (dim_vector (-1, 1));
      m_zdata.add_constraint (dim_vector (1, -1));
      m_zdata.add_constraint (dim_vector (-1, 0));
      m_zdata.add_constraint (dim_vector (0, -1));
      m_sizedata.add_constraint ("min", 0.0, false);
      m_sizedata.add_constraint (dim_vector (-1, 1));
      m_sizedata.add_constraint (dim_vector (1, -1));
      m_sizedata.add_constraint (dim_vector (-1, 0));
      m_sizedata.add_constraint (dim_vector (0, -1));
      m_cdata.add_constraint ("double");
      m_cdata.add_constraint ("single");
      m_cdata.add_constraint ("logical");
      m_cdata.add_constraint ("int8");
      m_cdata.add_constraint ("int16");
      m_cdata.add_constraint ("int32");
      m_cdata.add_constraint ("int64");
      m_cdata.add_constraint ("uint8");
      m_cdata.add_constraint ("uint16");
      m_cdata.add_constraint ("uint32");
      m_cdata.add_constraint ("uint64");
      m_cdata.add_constraint ("real");
      m_cdata.add_constraint (dim_vector (-1, 1));
      m_cdata.add_constraint (dim_vector (-1, 3));
      m_cdata.add_constraint (dim_vector (-1, 0));
      m_cdata.add_constraint (dim_vector (0, -1));

      m_linewidth.add_constraint ("min", 0.0, false);
      m_seriesindex.add_constraint (dim_vector (1, 1));
      m_seriesindex.add_constraint (dim_vector (-1, 0));
      m_seriesindex.add_constraint (dim_vector (0, -1));
    }

  public:
    OCTINTERP_API void update_color (void);

  private:
    std::string m_bad_data_msg;

    void update_xdata (void)
    {
      if (get_xdata ().isempty ())
        {
          // For compatibility with Matlab behavior,
          // if x/ydata are set empty, silently empty other *data properties.
          set_ydata (Matrix ());
          set_zdata (Matrix ());
          bool cdatamode_auto = m_cdatamode.is ("auto");
          set_cdata (Matrix ());
          if (cdatamode_auto)
            set_cdatamode ("auto");
        }

      set_xlim (m_xdata.get_limits ());

      update_data ();
    }

    void update_ydata (void)
    {
      if (get_ydata ().isempty ())
        {
          set_xdata (Matrix ());
          set_zdata (Matrix ());
          bool cdatamode_auto = m_cdatamode.is ("auto");
          set_cdata (Matrix ());
          if (cdatamode_auto)
            set_cdatamode ("auto");
        }

      set_ylim (m_ydata.get_limits ());

      update_data ();
    }

    void update_zdata (void)
    {
      set_zlim (m_zdata.get_limits ());

      update_data ();
    }

    void update_sizedata (void)
    {
      update_data ();
    }

    void update_cdata (void)
    {
      if (get_cdata ().matrix_value ().rows () == 1)
        set_clim (m_cdata.get_limits ());
      else
        m_clim = m_cdata.get_limits ();

      update_data ();
    }

    void update_cdatamode (void)
    {
      if (m_cdatamode.is ("auto"))
        update_color ();
    }

    void update_seriesindex (void)
    {
      if (m_cdatamode.is ("auto"))
        update_color ();
    }

    void update_data (void);

  };

private:
  properties m_properties;
  property_list m_default_properties;

public:
  scatter (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), m_properties (mh, p)
  {
    // FIXME: seriesindex should increment by one each time a new scatter
    // object is added to the axes.
  }

  ~scatter (void) = default;

  base_properties& get_properties (void) { return m_properties; }

  const base_properties& get_properties (void) const { return m_properties; }

  bool valid_object (void) const { return true; }

  bool has_readonly_property (const caseless_str& pname) const
  {
    bool retval = m_properties.has_readonly_property (pname);
    if (! retval)
      retval = base_properties::has_readonly_property (pname);
    return retval;
  }

protected:
  OCTINTERP_API void initialize (const graphics_object& go);

};

// ---------------------------------------------------------------------

class OCTINTERP_API surface : public base_graphics_object
{
public:

  class OCTINTERP_API properties : public base_properties
  {
  public:

    octave_value get_color_data (void) const;

    bool is_aliminclude (void) const
    { return (m_aliminclude.is_on () && m_alphadatamapping.is ("scaled")); }
    std::string get_aliminclude (void) const
    { return m_aliminclude.current_value (); }

    bool is_climinclude (void) const
    { return (m_climinclude.is_on () && m_cdatamapping.is ("scaled")); }
    std::string get_climinclude (void) const
    { return m_climinclude.current_value (); }

    OCTINTERP_API bool get_do_lighting (void) 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 (surface)
      array_property alphadata u , Matrix (1, 1, 1.0)
      radio_property alphadatamapping l , "none|direct|{scaled}"
      double_property ambientstrength , 0.3
      radio_property backfacelighting , "unlit|lit|{reverselit}"
      array_property cdata u , default_surface_cdata ()
      radio_property cdatamapping al , "{scaled}|direct"
      string_property cdatasource , ""
      double_property diffusestrength , 0.6
      string_property displayname , ""
      double_radio_property edgealpha , double_radio_property (1.0, radio_values ("flat|interp"))
      color_property edgecolor , color_property (color_values (0, 0, 0), radio_values ("none|flat|interp"))
      radio_property edgelighting u , "{none}|flat|gouraud|phong"
      double_radio_property facealpha , double_radio_property (1.0, radio_values ("flat|interp|texturemap"))
      color_property facecolor , color_property (radio_values ("none|{flat}|interp|texturemap"), color_values (0, 0, 0))
      radio_property facelighting u , "none|{flat}|gouraud|phong"
      array_property facenormals m , Matrix ()
      radio_property facenormalsmode u , "{auto}|manual"
      radio_property linestyle , "{-}|--|:|-.|none"
      double_property linewidth , 0.5
      radio_property marker , "{none}|+|o|*|.|x|||_|s|square|d|diamond|^|v|>|<|p|pentagram|h|hexagram"
      color_property markeredgecolor , color_property (radio_values ("none|{auto}|flat"), color_values (0, 0, 0))
      color_property markerfacecolor , color_property (radio_values ("{none}|auto|flat"), color_values (0, 0, 0))
      double_property markersize , 6
      radio_property meshstyle , "{both}|row|column"
      double_property specularcolorreflectance , 1
      double_property specularexponent , 10
      double_property specularstrength , 0.9
      array_property vertexnormals m , Matrix ()
      radio_property vertexnormalsmode u , "{auto}|manual"
      array_property xdata u , default_surface_xdata ()
      string_property xdatasource , ""
      array_property ydata u , default_surface_ydata ()
      string_property ydatasource , ""
      array_property zdata u , default_surface_zdata ()
      string_property zdatasource , ""

      // hidden properties for limit computation
      row_vector_property alim hlr , Matrix ()
      row_vector_property clim hlr , Matrix ()
      row_vector_property xlim hlr , Matrix ()
      row_vector_property ylim hlr , Matrix ()
      row_vector_property zlim hlr , Matrix ()
      bool_property aliminclude hlg , "on"
      bool_property climinclude hlg , "on"
      bool_property xliminclude hl , "on"
      bool_property yliminclude hl , "on"
      bool_property zliminclude hl , "on"
    END_PROPERTIES

  protected:
    void init (void)
    {
      m_xdata.add_constraint (dim_vector (-1, -1));
      m_ydata.add_constraint (dim_vector (-1, -1));
      m_zdata.add_constraint (dim_vector (-1, -1));
      m_cdata.add_constraint ("double");
      m_cdata.add_constraint ("single");
      m_cdata.add_constraint ("logical");
      m_cdata.add_constraint ("int8");
      m_cdata.add_constraint ("int16");
      m_cdata.add_constraint ("int32");
      m_cdata.add_constraint ("int64");
      m_cdata.add_constraint ("uint8");
      m_cdata.add_constraint ("uint16");
      m_cdata.add_constraint ("uint32");
      m_cdata.add_constraint ("uint64");
      m_cdata.add_constraint ("real");
      m_cdata.add_constraint (dim_vector (-1, -1));
      m_cdata.add_constraint (dim_vector (-1, -1, 3));
      m_alphadata.add_constraint ("double");
      m_alphadata.add_constraint ("uint8");
      m_alphadata.add_constraint (dim_vector (-1, -1));
      m_facenormals.add_constraint (dim_vector (-1, -1, 3));
      m_facenormals.add_constraint (dim_vector (0, 0));
      m_vertexnormals.add_constraint (dim_vector (-1, -1, 3));
      m_vertexnormals.add_constraint (dim_vector (0, 0));

      m_ambientstrength.add_constraint ("min", 0.0, true);
      m_ambientstrength.add_constraint ("max", 1.0, true);
      m_diffusestrength.add_constraint ("min", 0.0, true);
      m_diffusestrength.add_constraint ("max", 1.0, true);
      m_linewidth.add_constraint ("min", 0.0, false);
      m_markersize.add_constraint ("min", 0.0, false);
      m_specularcolorreflectance.add_constraint ("min", 0.0, true);
      m_specularcolorreflectance.add_constraint ("max", 1.0, true);
      m_specularexponent.add_constraint ("min", 0.0, false);
      m_specularstrength.add_constraint ("min", 0.0, true);
      m_specularstrength.add_constraint ("max", 1.0, true);
    }

  public:
    void update_normals (bool reset, bool force = false)
    {
      update_face_normals (reset, force);
      update_vertex_normals (reset, force);
    }


  private:
    void update_alphadata (void)
    {
      if (alphadatamapping_is ("scaled"))
        set_alim (m_alphadata.get_limits ());
      else
        m_alim = m_alphadata.get_limits ();
    }

    void update_cdata (void)
    {
      if (cdatamapping_is ("scaled"))
        set_clim (m_cdata.get_limits ());
      else
        m_clim = m_cdata.get_limits ();
    }

    void update_xdata (void)
    {
      update_normals (true);
      set_xlim (m_xdata.get_limits ());
    }

    void update_ydata (void)
    {
      update_normals (true);
      set_ylim (m_ydata.get_limits ());
    }

    void update_zdata (void)
    {
      update_normals (true);
      set_zlim (m_zdata.get_limits ());
    }

    OCTINTERP_API void update_face_normals (bool reset, bool force = false);
    OCTINTERP_API void update_vertex_normals (bool reset, bool force = false);

    void update_facenormalsmode (void)
    { update_face_normals (false); }

    void update_vertexnormalsmode (void)
    { update_vertex_normals (false); }

    void update_edgelighting (void)
    { update_normals (false); }

    void update_facelighting (void)
    { update_normals (false); }

    void update_visible (void)
    {
      if (is_visible ())
        update_normals (false);
    }

  };

private:
  properties m_properties;

public:
  surface (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), m_properties (mh, p)
  { }

  ~surface (void) = default;

  base_properties& get_properties (void) { return m_properties; }

  const base_properties& get_properties (void) const { return m_properties; }

  bool valid_object (void) const { return true; }

  bool has_readonly_property (const caseless_str& pname) const
  {
    bool retval = m_properties.has_readonly_property (pname);
    if (! retval)
      retval = base_properties::has_readonly_property (pname);
    return retval;
  }
};

// ---------------------------------------------------------------------

class OCTINTERP_API hggroup : public base_graphics_object
{
public:

  class OCTINTERP_API properties : public base_properties
  {
  public:

    OCTINTERP_API void
    remove_child (const graphics_handle& h, bool from_root = false);

    OCTINTERP_API void adopt (const graphics_handle& h);

    // 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 (hggroup)
      string_property displayname , ""

      // hidden properties for limit computation
      row_vector_property alim hr , Matrix ()
      row_vector_property clim hr , Matrix ()
      row_vector_property xlim hr , Matrix ()
      row_vector_property ylim hr , Matrix ()
      row_vector_property zlim hr , Matrix ()
      bool_property aliminclude h , "on"
      bool_property climinclude h , "on"
      bool_property xliminclude h , "on"
      bool_property yliminclude h , "on"
      bool_property zliminclude h , "on"
    END_PROPERTIES

  private:
    OCTINTERP_API void update_limits (void) const;

    OCTINTERP_API void update_limits (const graphics_handle& h) const;

  protected:
    void init (void)
    { }

  };

private:
  properties m_properties;

public:
  hggroup (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), m_properties (mh, p)
  { }

  ~hggroup (void) = default;

  base_properties& get_properties (void) { return m_properties; }

  const base_properties& get_properties (void) const { return m_properties; }

  bool valid_object (void) const { return true; }

  OCTINTERP_API void update_axis_limits (const std::string& axis_type);

  OCTINTERP_API void update_axis_limits (const std::string& axis_type,
                                         const graphics_handle& h);

  bool has_readonly_property (const caseless_str& pname) const
  {
    bool retval = m_properties.has_readonly_property (pname);
    if (! retval)
      retval = base_properties::has_readonly_property (pname);
    return retval;
  }

};

// ---------------------------------------------------------------------

class OCTINTERP_API uimenu : public base_graphics_object
{
public:

  class OCTINTERP_API properties : public base_properties
  {
  public:

    void remove_child (const graphics_handle& h, bool from_root = false)
    {
      base_properties::remove_child (h, from_root);
    }

    void adopt (const graphics_handle& h)
    {
      base_properties::adopt (h);
    }

    // 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 (uimenu)
      string_property accelerator , ""
      callback_property callback , Matrix ()
      bool_property checked , "off"
      bool_property enable , "on"
      color_property foregroundcolor , color_values (0, 0, 0)
      string_property label gs , ""
      double_property position , 0
      bool_property separator , "off"
      string_property text , ""

      // Octave-specific properties
      string_property __fltk_label__ h , ""
      any_property __object__ h , Matrix ()
    END_PROPERTIES

    // Redirect calls from "Label" to "Text".
    std::string get_label (void) const
    {
      return get_text ();
    }

    void set_label (const octave_value& val)
    {
      set_text (val);
    }

  protected:
    void init (void)
    {
      m_position.add_constraint ("min", 0, false);
    }
  };

private:
  properties m_properties;

public:
  uimenu (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), m_properties (mh, p)
  { }

  ~uimenu (void) = default;

  base_properties& get_properties (void) { return m_properties; }

  const base_properties& get_properties (void) const { return m_properties; }

  bool valid_object (void) const { return true; }

  bool has_readonly_property (const caseless_str& pname) const
  {
    bool retval = m_properties.has_readonly_property (pname);
    if (! retval)
      retval = base_properties::has_readonly_property (pname);
    return retval;
  }

};

// ---------------------------------------------------------------------

// FIXME: This class has been renamed to "contextmenu" in Matlab R2020a.
class OCTINTERP_API uicontextmenu : public base_graphics_object
{
public:

  class OCTINTERP_API properties : public base_properties
  {
  public:

    void add_dependent_obj (graphics_handle gh)
    { m_dependent_obj_list.push_back (gh); }

    // FIXME: the list may contain duplicates.
    //        Should we return only unique elements?
    const std::list<graphics_handle> get_dependent_obj_list (void)
    { return m_dependent_obj_list; }

    // 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 (uicontextmenu)
      callback_property callback , Matrix ()
      array_property position , Matrix (1, 2, 0.0)

      // Octave-specific properties
      any_property __object__ h , Matrix ()
    END_PROPERTIES

  protected:
    void init (void)
    {
      m_position.add_constraint (dim_vector (1, 2));
      m_position.add_constraint (dim_vector (2, 1));
      m_visible.set (octave_value (false));
    }

  private:
    // List of objects that might depend on this uicontextmenu object
    std::list<graphics_handle> m_dependent_obj_list;

    OCTINTERP_API void update_beingdeleted (void);

  };

private:
  properties m_properties;

public:
  uicontextmenu (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), m_properties (mh, p)
  { }

  ~uicontextmenu (void) = default;

  base_properties& get_properties (void) { return m_properties; }

  const base_properties& get_properties (void) const { return m_properties; }

  bool valid_object (void) const { return true; }

  bool has_readonly_property (const caseless_str& pname) const
  {
    bool retval = m_properties.has_readonly_property (pname);
    if (! retval)
      retval = base_properties::has_readonly_property (pname);
    return retval;
  }

};

// ---------------------------------------------------------------------

class OCTINTERP_API uicontrol : public base_graphics_object
{
public:

  class OCTINTERP_API properties : public base_properties
  {
  public:

    OCTINTERP_API Matrix
    get_boundingbox (bool internal = false,
                     const Matrix& parent_pix_size = Matrix ()) const;

    OCTINTERP_API double
    get___fontsize_points__ (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 (uicontrol)
      color_property backgroundcolor , color_values (0.94, 0.94, 0.94)
      callback_property callback , Matrix ()
      array_property cdata , Matrix ()
      bool_property clipping , "on"
      radio_property enable , "{on}|inactive|off"
      array_property extent rG , Matrix (1, 4, 0.0)
      radio_property fontangle u , "{normal}|italic"
      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 , "{normal}|bold"
      color_property foregroundcolor , color_values (0, 0, 0)
      radio_property horizontalalignment , "left|{center}|right"
      callback_property keypressfcn , Matrix ()
      double_property listboxtop , 1
      double_property max , 1
      double_property min , 0
      array_property position , default_control_position ()
      array_property sliderstep , default_control_sliderstep ()
      string_array_property string u , ""
      radio_property style S , "{pushbutton}|togglebutton|radiobutton|checkbox|edit|text|slider|frame|listbox|popupmenu"
      string_property tooltipstring , ""
      radio_property units u , "normalized|inches|centimeters|points|{pixels}|characters"
      row_vector_property value , Matrix (1, 1, 0.0)
      radio_property verticalalignment , "top|{middle}|bottom"

      // Octave-specific properties
      bool_property __focus__ h , "off"
      any_property __object__ h , Matrix ()
    END_PROPERTIES

  private:
    std::string m_cached_units;

  protected:
    void init (void)
    {
      m_cdata.add_constraint ("double");
      m_cdata.add_constraint ("single");
      m_cdata.add_constraint ("uint8");
      m_cdata.add_constraint (dim_vector (-1, -1, 3));
      m_cdata.add_constraint (dim_vector (0, 0));
      m_position.add_constraint (dim_vector (1, 4));
      m_sliderstep.add_constraint (dim_vector (1, 2));
      m_fontsize.add_constraint ("min", 0.0, false);
      m_cached_units = get_units ();
    }

    OCTINTERP_API void update_text_extent (void);

    void update_string (void) { update_text_extent (); }
    void update_fontname (void) { update_text_extent (); }
    void update_fontsize (void) { update_text_extent (); }
    void update_fontangle (void)
    {
      update_text_extent ();
    }
    void update_fontweight (void) { update_text_extent (); }

    OCTINTERP_API void update_fontunits (const caseless_str& old_units);

    OCTINTERP_API void update_units (void);

  };

private:
  properties m_properties;

public:
  uicontrol (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), m_properties (mh, p)
  { }

  ~uicontrol (void) = default;

  base_properties& get_properties (void) { return m_properties; }

  const base_properties& get_properties (void) const { return m_properties; }

  bool valid_object (void) const { return true; }

  bool has_readonly_property (const caseless_str& pname) const
  {
    bool retval = m_properties.has_readonly_property (pname);
    if (! retval)
      retval = base_properties::has_readonly_property (pname);
    return retval;
  }
};

// ---------------------------------------------------------------------

class OCTINTERP_API uibuttongroup : public base_graphics_object
{
public:

  class OCTINTERP_API properties : public base_properties
  {
  public:

    OCTINTERP_API void
    remove_child (const graphics_handle& h, bool from_root = false);

    OCTINTERP_API void adopt (const graphics_handle& h);

    OCTINTERP_API Matrix
    get_boundingbox (bool internal = false,
                     const Matrix& parent_pix_size = Matrix ()) const;

    OCTINTERP_API double
    get___fontsize_points__ (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 (uibuttongroup)
      color_property backgroundcolor , color_values (0.94, 0.94, 0.94)
      radio_property bordertype , "none|{etchedin}|etchedout|beveledin|beveledout|line"
      double_property borderwidth , 1
      bool_property clipping , "on"
      radio_property fontangle , "{normal}|italic"
      string_property fontname , OCTAVE_DEFAULT_FONTNAME
      double_property fontsize , 10
      radio_property fontunits S , "inches|centimeters|normalized|{points}|pixels"
      radio_property fontweight , "{normal}|bold"
      color_property foregroundcolor , color_values (0, 0, 0)
      color_property highlightcolor , color_values (1, 1, 1)
      array_property position S , default_panel_position ()
      // FIXME: "resizefcn" is no longer recommended by Matlab,
      //        and has been replaced with "sizechangedfcn"
      //        Eventually this will need to be hidden, and then removed.
      callback_property resizefcn , Matrix ()
      handle_property selectedobject S , graphics_handle ()
      callback_property selectionchangedfcn , Matrix ()
      color_property shadowcolor , color_values (0.7, 0.7, 0.7)
      callback_property sizechangedfcn , Matrix ()
      radio_property units S , "{normalized}|inches|centimeters|points|pixels|characters"
      string_property title , ""
      radio_property titleposition , "{lefttop}|centertop|righttop|leftbottom|centerbottom|rightbottom"

      // Octave-specific properties
      any_property __object__ h , Matrix ()
    END_PROPERTIES

  protected:
    void init (void)
    {
      m_position.add_constraint (dim_vector (1, 4));
      m_borderwidth.add_constraint ("min", 0.0, true);
      m_fontsize.add_constraint ("min", 0.0, false);
    }

    // void update_text_extent (void);
    // void update_string (void) { update_text_extent (); }
    // void update_fontname (void) { update_text_extent (); }
    // void update_fontsize (void) { update_text_extent (); }
    // void update_fontangle (void) { update_text_extent (); }
    // void update_fontweight (void) { update_fontweight (); }

    OCTINTERP_API void update_units (const caseless_str& old_units);
    OCTINTERP_API void update_fontunits (const caseless_str& old_units);

  };

private:
  properties m_properties;

public:
  uibuttongroup (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), m_properties (mh, p)
  { }

  ~uibuttongroup (void) = default;

  base_properties& get_properties (void) { return m_properties; }

  const base_properties& get_properties (void) const { return m_properties; }

  bool valid_object (void) const { return true; }

  bool has_readonly_property (const caseless_str& pname) const
  {
    bool retval = m_properties.has_readonly_property (pname);
    if (! retval)
      retval = base_properties::has_readonly_property (pname);
    return retval;
  }

};

// ---------------------------------------------------------------------

class OCTINTERP_API uipanel : public base_graphics_object
{
public:

  class OCTINTERP_API properties : public base_properties
  {
  public:

    OCTINTERP_API Matrix
    get_boundingbox (bool internal = false,
                     const Matrix& parent_pix_size = Matrix ()) const;

    OCTINTERP_API double
    get___fontsize_points__ (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 (uipanel)
      color_property backgroundcolor , color_values (0.94, 0.94, 0.94)
      radio_property bordertype , "none|{etchedin}|etchedout|beveledin|beveledout|line"
      double_property borderwidth , 1
      radio_property fontangle , "{normal}|italic"
      string_property fontname , OCTAVE_DEFAULT_FONTNAME
      double_property fontsize , 10
      radio_property fontunits S , "inches|centimeters|normalized|{points}|pixels"
      radio_property fontweight , "{normal}|bold"
      color_property foregroundcolor , color_values (0, 0, 0)
      color_property highlightcolor , color_values (1, 1, 1)
      array_property position S , default_panel_position ()
      // FIXME: "resizefcn" is no longer recommended by Matlab,
      //        and has been replaced with "sizechangedfcn"
      //        Eventually this will need to be hidden, and then removed.
      callback_property resizefcn , Matrix ()
      color_property shadowcolor , color_values (0.7, 0.7, 0.7)
      callback_property sizechangedfcn , Matrix ()
      string_property title , ""
      radio_property titleposition , "{lefttop}|centertop|righttop|leftbottom|centerbottom|rightbottom"
      radio_property units S , "{normalized}|inches|centimeters|points|pixels|characters"
      // Octave-specific properties
      any_property __object__ h , Matrix ()
    END_PROPERTIES

  protected:
    void init (void)
    {
      m_borderwidth.add_constraint ("min", 0.0, true);
      m_fontsize.add_constraint ("min", 0.0, false);
      m_position.add_constraint (dim_vector (1, 4));
    }

    OCTINTERP_API void update_units (const caseless_str& old_units);
    OCTINTERP_API void update_fontunits (const caseless_str& old_units);

  };

private:
  properties m_properties;

public:
  uipanel (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), m_properties (mh, p)
  { }

  ~uipanel (void) = default;

  base_properties& get_properties (void) { return m_properties; }

  const base_properties& get_properties (void) const { return m_properties; }

  bool valid_object (void) const { return true; }

  bool has_readonly_property (const caseless_str& pname) const
  {
    bool retval = m_properties.has_readonly_property (pname);
    if (! retval)
      retval = base_properties::has_readonly_property (pname);
    return retval;
  }
};

// ---------------------------------------------------------------------

class OCTINTERP_API uitable : public base_graphics_object
{
public:

  class OCTINTERP_API properties : public base_properties
  {
  public:

    OCTINTERP_API Matrix
    get_boundingbox (bool internal = false,
                     const Matrix& parent_pix_size = Matrix ()) const;

    OCTINTERP_API double
    get___fontsize_points__ (double box_pix_height = 0) const;

    OCTINTERP_API 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.

    // FIXME: keypressfcn, keyreleasefcn, rearrangeablecolumns properties
    //        seem to have been removed from Matlab.

    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 , Cell ()
      any_property columnname , "numbered"
      any_property columnwidth S , "auto"
      any_property data u , Matrix ()
      bool_property enable , "on"
      array_property extent rG , Matrix (1, 4, 0.0)
      radio_property fontangle u , "{normal}|italic"
      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 , "{normal}|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

    OCTINTERP_API Matrix get_extent_matrix (void) const;

    OCTINTERP_API Matrix get_backgroundcolor_rgb (void);

    OCTINTERP_API Matrix get_alternatebackgroundcolor_rgb (void);

  protected:
    void init (void)
    {
      m_position.add_constraint (dim_vector (1, 4));
      m_extent.add_constraint (dim_vector (1, 4));
      m_backgroundcolor.add_constraint ("double");
      m_backgroundcolor.add_constraint (dim_vector (-1, 3));
      m_columneditable.add_constraint ("logical");
    }

    OCTINTERP_API void update_units (const caseless_str& old_units);
    OCTINTERP_API 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 m_properties;

public:
  uitable (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), m_properties (mh, p)
  { }

  ~uitable (void) { }

  base_properties& get_properties (void) { return m_properties; }

  const base_properties& get_properties (void) const { return m_properties; }

  bool valid_object (void) const { return true; }

  bool has_readonly_property (const caseless_str& pname) const
  {
    bool retval = m_properties.has_readonly_property (pname);
    if (! retval)
      retval = base_properties::has_readonly_property (pname);
    return retval;
  }
};

// ---------------------------------------------------------------------

class OCTINTERP_API uitoolbar : public base_graphics_object
{
public:

  class OCTINTERP_API properties : public base_properties
  {
  public:

    // 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 (uitoolbar)
      // Octave-specific properties
      any_property __object__ h , Matrix ()
    END_PROPERTIES

  protected:
    void init (void)
    { }
  };

private:
  properties m_properties;

public:
  uitoolbar (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), m_properties (mh, p), m_default_properties ()
  { }

  ~uitoolbar (void) = default;

  void override_defaults (base_graphics_object& obj)
  {
    // Allow parent (figure) to override first (properties knows how
    // to find the parent object).
    m_properties.override_defaults (obj);

    // Now override with our defaults.  If the default_properties
    // list includes the properties for all defaults (line,
    // surface, etc.) then we don't have to know the type of OBJ
    // here, we just call its set function and let it decide which
    // properties from the list to use.
    obj.set_from_list (m_default_properties);
  }

  void set (const caseless_str& name, const octave_value& value)
  {
    if (name.compare ("default", 7))
      // strip "default", pass rest to function that will
      // parse the remainder and add the element to the
      // default_properties map.
      m_default_properties.set (name.substr (7), value);
    else
      m_properties.set (name, value);
  }

  octave_value get (const caseless_str& name) const
  {
    octave_value retval;

    if (name.compare ("default", 7))
      retval = get_default (name.substr (7));
    else
      retval = m_properties.get (name);

    return retval;
  }

  OCTINTERP_API octave_value get_default (const caseless_str& name) const;

  octave_value get_defaults (void) const
  {
    return m_default_properties.as_struct ("default");
  }

  property_list get_defaults_list (void) const
  {
    return m_default_properties;
  }

  base_properties& get_properties (void) { return m_properties; }

  const base_properties& get_properties (void) const { return m_properties; }

  bool valid_object (void) const { return true; }

  OCTINTERP_API void reset_default_properties (void);

  bool has_readonly_property (const caseless_str& pname) const
  {
    bool retval = m_properties.has_readonly_property (pname);
    if (! retval)
      retval = base_properties::has_readonly_property (pname);
    return retval;
  }

private:
  property_list m_default_properties;
};

// ---------------------------------------------------------------------

class OCTINTERP_API uipushtool : public base_graphics_object
{
public:

  class OCTINTERP_API properties : public base_properties
  {
  public:

    // 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 (uipushtool)
      array_property cdata , Matrix ()
      callback_property clickedcallback , Matrix ()
      bool_property enable , "on"
      bool_property separator , "off"
      string_property tooltipstring , ""

      // Octave-specific properties
      string_property __named_icon__ , ""
      any_property __object__ h , Matrix ()
    END_PROPERTIES

  protected:
    void init (void)
    {
      m_cdata.add_constraint ("double");
      m_cdata.add_constraint ("single");
      m_cdata.add_constraint ("uint8");
      m_cdata.add_constraint (dim_vector (-1, -1, 3));
      m_cdata.add_constraint (dim_vector (0, 0));
    }
  };

private:
  properties m_properties;

public:
  uipushtool (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), m_properties (mh, p)
  { }

  ~uipushtool (void) = default;

  base_properties& get_properties (void) { return m_properties; }

  const base_properties& get_properties (void) const { return m_properties; }

  bool valid_object (void) const { return true; }

  bool has_readonly_property (const caseless_str& pname) const
  {
    bool retval = m_properties.has_readonly_property (pname);
    if (! retval)
      retval = base_properties::has_readonly_property (pname);
    return retval;
  }

};

// ---------------------------------------------------------------------

class OCTINTERP_API uitoggletool : public base_graphics_object
{
public:

  class OCTINTERP_API properties : public base_properties
  {
  public:

    // 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 (uitoggletool)
      array_property cdata , Matrix ()
      callback_property clickedcallback , Matrix ()
      bool_property enable , "on"
      callback_property offcallback , Matrix ()
      callback_property oncallback , Matrix ()
      bool_property separator , "off"
      bool_property state , "off"
      string_property tooltipstring , ""

      // Octave-specific properties
      string_property __named_icon__ , ""
      any_property __object__ h , Matrix ()
    END_PROPERTIES

  protected:
    void init (void)
    {
      m_cdata.add_constraint ("double");
      m_cdata.add_constraint ("single");
      m_cdata.add_constraint ("uint8");
      m_cdata.add_constraint (dim_vector (-1, -1, 3));
      m_cdata.add_constraint (dim_vector (0, 0));
    }
  };

private:
  properties m_properties;

public:
  uitoggletool (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), m_properties (mh, p)
  { }

  ~uitoggletool (void) = default;

  base_properties& get_properties (void) { return m_properties; }

  const base_properties& get_properties (void) const { return m_properties; }

  bool valid_object (void) const { return true; }

  bool has_readonly_property (const caseless_str& pname) const
  {
    bool retval = m_properties.has_readonly_property (pname);
    if (! retval)
      retval = base_properties::has_readonly_property (pname);
    return retval;
  }

};

// ---------------------------------------------------------------------

OCTINTERP_API octave_value
get_property_from_handle (double handle, const std::string& property,
                          const std::string& func);
OCTINTERP_API bool
set_property_in_handle (double handle, const std::string& property,
                        const octave_value& arg, const std::string& func);

// ---------------------------------------------------------------------

class graphics_event;

class
OCTINTERP_API
base_graphics_event
{
public:
  enum priority { INTERRUPT, QUEUE, CANCEL };

  friend class graphics_event;

  base_graphics_event (void)
    : m_busyaction (QUEUE)
  { };

  base_graphics_event (int busyaction)
    : m_busyaction (busyaction)
  { };

  virtual ~base_graphics_event (void) = default;

  int get_busyaction (void) { return m_busyaction; };

  virtual void execute (void) = 0;

 private:
  int m_busyaction;
};

class
OCTINTERP_API
graphics_event
{
public:

  typedef void (*event_fcn) (void*);

  graphics_event (void) = default;

  graphics_event (base_graphics_event *new_rep) : m_rep (new_rep) { }

  graphics_event (const graphics_event&) = default;

  ~graphics_event (void) = default;

  graphics_event& operator = (const graphics_event&) = default;

  int get_busyaction (void)
  {
    if (ok ())
      return m_rep->get_busyaction ();
    else
      error ("graphics_event::busyaction: invalid graphics_event");
  }

  void execute (void)
  {
    if (ok ())
      m_rep->execute ();
  }

  bool ok (void) const { return (m_rep != nullptr); }

  static OCTINTERP_API graphics_event
  create_callback_event (const graphics_handle& h,
                         const std::string& name,
                         const octave_value& data = Matrix (),
                         int busyaction = base_graphics_event::QUEUE);

  static OCTINTERP_API graphics_event
  create_callback_event (const graphics_handle& h,
                         const octave_value& cb,
                         const octave_value& data = Matrix (),
                         int busyaction = base_graphics_event::QUEUE);

  static OCTINTERP_API graphics_event
  create_mcode_event (const graphics_handle& h, const std::string& cmd,
                      int busyaction);

  static OCTINTERP_API graphics_event
  create_function_event (event_fcn fcn, void *data = nullptr);

  static OCTINTERP_API graphics_event
  create_set_event (const graphics_handle& h, const std::string& name,
                    const octave_value& value, bool notify_toolkit = true,
                    bool redraw_figure = false);
private:

  std::shared_ptr <base_graphics_event> m_rep;
};

class OCTINTERP_API gh_manager
{
public:

  OCTINTERP_API gh_manager (octave::interpreter& interp);

  // FIXME: eventually eliminate these static functions and access
  // gh_manager object through the interpreter.

  OCTINTERP_API graphics_handle get_handle (bool integer_figure_handle);

  OCTINTERP_API void free (const graphics_handle& h, bool from_root = false);

  OCTINTERP_API void renumber_figure (const graphics_handle& old_gh,
                                      const graphics_handle& new_gh);

  graphics_handle lookup (double val) const
  {
    const_iterator p = (octave::math::isnan (val)
                        ? m_handle_map.end () : m_handle_map.find (val));

    return (p != m_handle_map.end ()) ? p->first : graphics_handle ();
  }

  graphics_handle lookup (const octave_value& val) const
  {
    return (val.is_real_scalar ()
            ? lookup (val.double_value ()) : graphics_handle ());
  }

  graphics_object get_object (double val) const
  {
    return get_object (lookup (val));
  }

  graphics_object get_object (const graphics_handle& h) const
  {
    const_iterator p = (h.ok () ? m_handle_map.find (h) : m_handle_map.end ());

    return (p != m_handle_map.end ()) ? p->second : graphics_object ();
  }

  OCTINTERP_API graphics_handle
  make_graphics_handle (const std::string& go_name,
                        const graphics_handle& p,
                        bool integer_figure_handle = false,
                        bool call_createfcn = true,
                        bool notify_toolkit = true);

  OCTINTERP_API graphics_handle
  make_figure_handle (double val, bool notify_toolkit = true);

  OCTINTERP_API void push_figure (const graphics_handle& h);

  OCTINTERP_API void pop_figure (const graphics_handle& h);

  graphics_handle current_figure (void) const
  {
    graphics_handle retval;

    for (const auto& hfig : m_figure_list)
      {
        if (is_handle_visible (hfig))
          retval = hfig;
      }

    return retval;
  }

  Matrix handle_list (bool show_hidden = false)
  {
    Matrix retval (1, m_handle_map.size ());

    octave_idx_type i = 0;
    for (const auto& h_iter : m_handle_map)
      {
        graphics_handle h = h_iter.first;

        if (show_hidden || is_handle_visible (h))
          retval(i++) = h.value ();
      }

    retval.resize (1, i);

    return retval;
  }

  void lock (void) { m_graphics_lock.lock (); }

  bool try_lock (void) { return m_graphics_lock.try_lock (); }

  void unlock (void) { m_graphics_lock.unlock (); }

  Matrix figure_handle_list (bool show_hidden = false)
  {
    Matrix retval (1, m_figure_list.size ());

    octave_idx_type i = 0;
    for (const auto& hfig : m_figure_list)
      {
        if (show_hidden || is_handle_visible (hfig))
          retval(i++) = hfig.value ();
      }

    retval.resize (1, i);

    return retval;
  }

  OCTINTERP_API void
  execute_listener (const graphics_handle& h, const octave_value& l);

  void execute_callback (const graphics_handle& h,
                         const std::string& name,
                         const octave_value& data = Matrix ())
  {
    octave_value cb;

    if (true)
      {
        octave::autolock guard (graphics_lock ());

        graphics_object go = get_object (h);

        if (go.valid_object ())
          cb = go.get (name);
      }

    execute_callback (h, cb, data);
  }

  OCTINTERP_API void
  execute_callback (const graphics_handle& h, const octave_value& cb,
                    const octave_value& data = Matrix ());

  OCTINTERP_API void
  post_callback (const graphics_handle& h, const std::string& name,
                 const octave_value& data = Matrix ());

  OCTINTERP_API void
  post_function (graphics_event::event_fcn fcn, void *fcn_data = nullptr);

  OCTINTERP_API void
  post_set (const graphics_handle& h, const std::string& name,
            const octave_value& value, bool notify_toolkit = true,
            bool redraw_figure = false);

  OCTINTERP_API int process_events (bool force = false);

  OCTINTERP_API void enable_event_processing (bool enable = true);

  bool is_handle_visible (const graphics_handle& h) const
  {
    bool retval = false;

    graphics_object go = get_object (h);

    if (go.valid_object ())
      retval = go.is_handle_visible ();

    return retval;
  }

  OCTINTERP_API void close_all_figures (void);

  OCTINTERP_API void restore_gcbo (void);

  OCTINTERP_API void post_event (const graphics_event& e);

  octave::mutex graphics_lock (void)
  {
    return m_graphics_lock;
  }

private:

  typedef std::map<graphics_handle, graphics_object>::iterator iterator;
  typedef std::map<graphics_handle, graphics_object>::const_iterator
    const_iterator;

  typedef std::set<graphics_handle>::iterator free_list_iterator;
  typedef std::set<graphics_handle>::const_iterator const_free_list_iterator;

  typedef std::list<graphics_handle>::iterator figure_list_iterator;
  typedef std::list<graphics_handle>::const_iterator const_figure_list_iterator;

  octave::interpreter& m_interpreter;

  // A map of handles to graphics objects.
  std::map<graphics_handle, graphics_object> m_handle_map;

  // The available graphics handles.
  std::set<graphics_handle> m_handle_free_list;

  // The next handle available if m_handle_free_list is empty.
  double m_next_handle;

  // The allocated figure handles.  Top of the stack is most recently
  // created.
  std::list<graphics_handle> m_figure_list;

  // The lock for accessing the graphics sytsem.
  octave::mutex m_graphics_lock;

  // The list of events queued by graphics toolkits.
  std::list<graphics_event> m_event_queue;

  // The stack of callback objects.
  std::list<graphics_object> m_callback_objects;

  // A flag telling whether event processing must be constantly on.
  int m_event_processing;
};

OCTINTERP_API void
get_children_limits (double& min_val, double& max_val,
                     double& min_pos, double& max_neg,
                     const Matrix& kids, char limit_type);

OCTINTERP_API int calc_dimensions (const graphics_object& gh);

// This function is NOT equivalent to the scripting language function gcf.
OCTINTERP_API graphics_handle gcf (void);

// This function is NOT equivalent to the scripting language function gca.
OCTINTERP_API graphics_handle gca (void);

OCTINTERP_API void close_all_figures (void);

OCTAVE_NAMESPACE_END

#if defined (OCTAVE_PROVIDE_DEPRECATED_SYMBOLS)

OCTAVE_DEPRECATED (7, "use 'octave::base_scaler' instead")
typedef octave::base_scaler base_scaler;

OCTAVE_DEPRECATED (7, "use 'octave::lin_scaler' instead")
typedef octave::lin_scaler lin_scaler;

OCTAVE_DEPRECATED (7, "use 'octave::log_scaler' instead")
typedef octave::log_scaler log_scaler;

OCTAVE_DEPRECATED (7, "use 'octave::neg_log_scaler' instead")
typedef octave::neg_log_scaler neg_log_scaler;

OCTAVE_DEPRECATED (7, "use 'octave::scaler' instead")
typedef octave::scaler scaler;

OCTAVE_DEPRECATED (7, "use 'octave::base_property' instead")
typedef octave::base_property base_property;

OCTAVE_DEPRECATED (7, "use 'octave::string_property' instead")
typedef octave::string_property string_property;

OCTAVE_DEPRECATED (7, "use 'octave::string_array_property' instead")
typedef octave::string_array_property string_array_property;

OCTAVE_DEPRECATED (7, "use 'octave::text_label_property' instead")
typedef octave::text_label_property text_label_property;

OCTAVE_DEPRECATED (7, "use 'octave::radio_values' instead")
typedef octave::radio_values radio_values;

OCTAVE_DEPRECATED (7, "use 'octave::radio_property' instead")
typedef octave::radio_property radio_property;

OCTAVE_DEPRECATED (7, "use 'octave::color_values' instead")
typedef octave::color_values color_values;

OCTAVE_DEPRECATED (7, "use 'octave::color_property' instead")
typedef octave::color_property color_property;

OCTAVE_DEPRECATED (7, "use 'octave::double_property' instead")
typedef octave::double_property double_property;

OCTAVE_DEPRECATED (7, "use 'octave::double_radio_property' instead")
typedef octave::double_radio_property double_radio_property;

OCTAVE_DEPRECATED (7, "use 'octave::array_property' instead")
typedef octave::array_property array_property;

OCTAVE_DEPRECATED (7, "use 'octave::row_vector_property' instead")
typedef octave::row_vector_property row_vector_property;

OCTAVE_DEPRECATED (7, "use 'octave::bool_property' instead")
typedef octave::bool_property bool_property;

OCTAVE_DEPRECATED (7, "use 'octave::handle_property' instead")
typedef octave::handle_property handle_property;

OCTAVE_DEPRECATED (7, "use 'octave::any_property' instead")
typedef octave::any_property any_property;

OCTAVE_DEPRECATED (7, "use 'octave::children_property' instead")
typedef octave::children_property children_property;

OCTAVE_DEPRECATED (7, "use 'octave::callback_property' instead")
typedef octave::callback_property callback_property;

OCTAVE_DEPRECATED (7, "use 'octave::property' instead")
typedef octave::property property;

OCTAVE_DEPRECATED (7, "use 'octave::pval_vector' instead")
typedef octave::pval_vector pval_vector;

OCTAVE_DEPRECATED (7, "use 'octave::property_list' instead")
typedef octave::property_list property_list;

OCTAVE_DEPRECATED (7, "use 'octave::base_properties' instead")
typedef octave::base_properties base_properties;

OCTAVE_DEPRECATED (7, "use 'octave::base_graphics_object' instead")
typedef octave::base_graphics_object base_graphics_object;

OCTAVE_DEPRECATED (7, "use 'octave::graphics_object' instead")
typedef octave::graphics_object graphics_object;

OCTAVE_DEPRECATED (7, "use 'octave::root_figure' instead")
typedef octave::root_figure root_figure;

OCTAVE_DEPRECATED (7, "use 'octave::figure' instead")
typedef octave::figure figure;

OCTAVE_DEPRECATED (7, "use 'octave::graphics_xform' instead")
typedef octave::graphics_xform graphics_xform;

OCTAVE_DEPRECATED (7, "use 'octave::axes' instead")
typedef octave::axes axes;

OCTAVE_DEPRECATED (7, "use 'octave::line' instead")
typedef octave::line line;

OCTAVE_DEPRECATED (7, "use 'octave::text' instead")
typedef octave::text text;

OCTAVE_DEPRECATED (7, "use 'octave::image' instead")
typedef octave::image image;

OCTAVE_DEPRECATED (7, "use 'octave::light' instead")
typedef octave::light light;

OCTAVE_DEPRECATED (7, "use 'octave::patch' instead")
typedef octave::patch patch;

OCTAVE_DEPRECATED (7, "use 'octave::scatter' instead")
typedef octave::scatter scatter;

OCTAVE_DEPRECATED (7, "use 'octave::surface' instead")
typedef octave::surface surface;

OCTAVE_DEPRECATED (7, "use 'octave::hggroup' instead")
typedef octave::hggroup hggroup;

OCTAVE_DEPRECATED (7, "use 'octave::uimenu' instead")
typedef octave::uimenu uimenu;

OCTAVE_DEPRECATED (7, "use 'octave::uicontextmenu' instead")
typedef octave::uicontextmenu uicontextmenu;

OCTAVE_DEPRECATED (7, "use 'octave::uicontrol' instead")
typedef octave::uicontrol uicontrol;

OCTAVE_DEPRECATED (7, "use 'octave::uibuttongroup' instead")
typedef octave::uibuttongroup uibuttongroup;

OCTAVE_DEPRECATED (7, "use 'octave::uipanel' instead")
typedef octave::uipanel uipanel;

OCTAVE_DEPRECATED (7, "use 'octave::uitable' instead")
typedef octave::uitable uitable;

OCTAVE_DEPRECATED (7, "use 'octave::uitoolbar' instead")
typedef octave::uitoolbar uitoolbar;

OCTAVE_DEPRECATED (7, "use 'octave::uipushtool' instead")
typedef octave::uipushtool uipushtool;

OCTAVE_DEPRECATED (7, "use 'octave::uitoggletool' instead")
typedef octave::uitoggletool uitoggletool;

OCTAVE_DEPRECATED (7, "use 'octave::base_graphics_event' instead")
typedef octave::base_graphics_event base_graphics_event;

OCTAVE_DEPRECATED (7, "use 'octave::graphics_event' instead")
typedef octave::graphics_event graphics_event;

OCTAVE_DEPRECATED (7, "use 'octave::gh_manager' instead")
typedef octave::gh_manager gh_manager;

#endif

#endif