view src/graphics.h.in @ 7948:af10baa63915 ss-3-1-50

3.1.50 snapshot
author John W. Eaton <jwe@octave.org>
date Fri, 18 Jul 2008 17:42:48 -0400
parents 78400fde223e
children 9cd3ee5298a0
line wrap: on
line source

/*

Copyright (C) 2007 John W. Eaton

This file is part of Octave.

Octave is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 3 of the License, or (at your
option) any later version.

Octave is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License
along with Octave; see the file COPYING.  If not, see
<http://www.gnu.org/licenses/>.

*/

#if !defined (graphics_h)
#define graphics_h 1

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <cctype>

#include <algorithm>
#include <list>
#include <map>
#include <set>
#include <string>

#include "gripes.h"
#include "oct-map.h"
#include "oct-mutex.h"
#include "ov.h"

class caseless_str : public std::string
{
public:
  typedef std::string::iterator iterator;
  typedef std::string::const_iterator const_iterator;

  caseless_str (void) : std::string () { }
  caseless_str (const std::string& s) : std::string (s) { }
  caseless_str (const char *s) : std::string (s) { }

  caseless_str (const caseless_str& name) : std::string (name) { }

  caseless_str& operator = (const caseless_str& pname)
  {
    std::string::operator = (pname);
    return *this;
  }

  operator std::string (void) const { return *this; }

  // Case-insensitive comparison.
  bool compare (const std::string& s, size_t limit = NPOS) const
  {
    const_iterator p1 = begin ();
    const_iterator p2 = s.begin ();

    size_t k = 0;

    while (p1 != end () && p2 != s.end () && k++ < limit)
      {
	if (std::tolower (*p1) != std::tolower (*p2))
	  return false;

	*p1++;
	*p2++;
      }

    return (limit == NPOS) ? size () == s.size () : k == limit;
  }
};

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

class graphics_handle
{
public:
  graphics_handle (void) : val (octave_NaN) { }

  graphics_handle (const octave_value& a);

  graphics_handle (int a) : val (a) { }

  graphics_handle (double a) : val (a) { }

  graphics_handle (const graphics_handle& a) : val (a.val) { }

  graphics_handle& operator = (const graphics_handle& a)
  {
    if (&a != this)
      val = a.val;

    return *this;
  }

  ~graphics_handle (void) { }

  double value (void) const { return val; }

  octave_value as_octave_value (void) const
  {
    return ok () ? octave_value (val) : octave_value (Matrix ());
  }

  graphics_handle operator ++ (void)
  {
    ++val;
    return *this;
  }

  graphics_handle operator ++ (int)
  {
    graphics_handle h = *this;
    ++val;
    return h;
  }

  graphics_handle operator -- (void)
  {
    --val;
    return *this;
  }

  graphics_handle operator -- (int)
  {
    graphics_handle h = *this;
    --val;
    return h;
  }

  bool ok (void) const { return ! xisnan (val); }

private:
  double val;
};

inline bool
operator == (const graphics_handle& a, const graphics_handle& b)
{
  return a.value () == b.value ();
}

inline bool
operator != (const graphics_handle& a, const graphics_handle& b)
{
  return a.value () != b.value ();
}

inline bool
operator < (const graphics_handle& a, const graphics_handle& b)
{
  return a.value () < b.value ();
}

inline bool
operator <= (const graphics_handle& a, const graphics_handle& b)
{
  return a.value () <= b.value ();
}

inline bool
operator >= (const graphics_handle& a, const graphics_handle& b)
{
  return a.value () >= b.value ();
}

inline bool
operator > (const graphics_handle& a, const graphics_handle& b)
{
  return a.value () > b.value ();
}

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

class base_scaler
{
public:
  base_scaler (void) { }

  virtual ~base_scaler (void) { }

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

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

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

  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 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 scaler
{
public:
  scaler (void) : rep (new base_scaler ()) { }

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

  ~scaler (void) { delete rep; }

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

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

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

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

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

  scaler& operator = (const scaler& s)
    {
      if (rep)
	{
	  delete rep;
	  rep = 0;
	}

      rep = s.rep->clone ();

      return *this;
    }

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

      if (s == "log")
	rep = new log_scaler ();
      else if (s == "linear")
	rep = new lin_scaler ();
      else
	rep = new base_scaler ();

      return *this;
    }

private:
  base_scaler *rep;
};

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

class property;

enum listener_mode { POSTSET };

class base_property
{
public:
  friend class property;

public:
  base_property (void) : count (1) { }

  base_property (const std::string& s, const graphics_handle& h)
    : count (1), name (s), parent (h), hidden (false) { }

  base_property (const base_property& p)
    : count (1), name (p.name), parent (p.parent), hidden (p.hidden) { }

  virtual ~base_property (void) { }

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

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

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

  graphics_handle get_parent (void) const { return parent; }

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

  bool is_hidden (void) const { return hidden; }

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

  void set (const octave_value& v, bool do_run = true)
    {
      do_set (v);

      if (do_run && ! error_state)
	run_listeners (POSTSET);
    }

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

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

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

  OCTINTERP_API void run_listeners (listener_mode mode = POSTSET);

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

protected:
  virtual void do_set (const octave_value&)
    { error ("set: invalid property \"%s\"", 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 count;
  std::string name;
  graphics_handle parent;
  bool hidden;
  listener_map listeners;
};

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

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

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

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

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

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

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

protected:
  void do_set (const octave_value& val)
    {
      if (val.is_string ())
        str = val.string_value ();
      else
        error ("set: invalid string property value for \"%s\"",
               get_name ().c_str ());
    }

private:
  std::string str;
};

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

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

  radio_values (const radio_values& a)
    : default_val (a.default_val), possible_vals (a.possible_vals) { }

  radio_values& operator = (const radio_values& a)
  {
    if (&a != this)
      {
	default_val = a.default_val;
	possible_vals = a.possible_vals;
      }

    return *this;
  }

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

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

    if (! contains (val))
      {
	error ("invalid value = %s", val.c_str ());
	retval = false;
      }

    return retval;
  }
  
  bool contains (const std::string& val)
  {
    return (possible_vals.find (val) != possible_vals.end ());
  }

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

class 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),
      vals (v), current_val (v.default_value ()) { }

  radio_property (const std::string& nm, const graphics_handle& h,
                  const std::string& v)
    : base_property (nm, h),
      vals (v), current_val (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),
      vals (v), current_val (def) { }

  radio_property (const radio_property& p)
    : base_property (p), vals (p.vals), current_val (p.current_val) { }

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

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

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

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

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

protected:
  void do_set (const octave_value& newval) 
  {
    if (newval.is_string ())
      {
        std::string s = newval.string_value ();
        if (vals.validate (s))
          current_val = s;
        else
          error ("set: invalid value for radio property \"%s\" (value = %s)",
              get_name ().c_str (), s.c_str ());
      }
    else	
      error ("set: invalid value for radio property \"%s\"",
          get_name ().c_str ());
  }

private:
  radio_values vals;
  std::string current_val;
};

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

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

    validate ();
  }

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

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

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

    return *this;
  }

  Matrix rgb (void) const { return xrgb; }

  operator octave_value (void) const { return xrgb; }

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

private:
  Matrix xrgb;

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

class color_property : public base_property
{
public:
  color_property (const color_values& c, const radio_values& v)
    : base_property ("", graphics_handle ()),
      current_type (color_t), color_val (c), radio_val (v),
      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),
      current_type (color_t), color_val (c), radio_val (v),
      current_val (v.default_value ())
  { }

  color_property (const std::string& nm, const graphics_handle& h,
                  const radio_values& v)
    : base_property (nm, h),
      current_type (radio_t), color_val (color_values ()), radio_val (v),
      current_val (v.default_value ())
  { }

  color_property (const std::string& nm, const graphics_handle& h,
                  const std::string& v)
    : base_property (nm, h),
      current_type (radio_t), color_val (color_values ()), radio_val (v),
      current_val (radio_val.default_value ())
  { }
  
  color_property (const std::string& nm, const graphics_handle& h,
                  const color_property& v)
    : base_property (nm, h),
      current_type (v.current_type), color_val (v.color_val),
      radio_val (v.radio_val), current_val (v.current_val)
  { }

  color_property (const color_property& p)
    : base_property (p), current_type (p.current_type),
      color_val (p.color_val), radio_val (p.radio_val),
      current_val (p.current_val) { }

  octave_value get (void) const
  {
    if (current_type == color_t)
      return color_val.rgb ();

    return current_val;
  }

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

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

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

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

    return color_val.rgb ();
  }

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

    return 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); }

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

private:
  enum current_enum { color_t, radio_t } current_type;
  color_values color_val;
  radio_values radio_val;
  std::string current_val;
};

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

class double_property : public base_property
{
public:
  double_property (const std::string& nm, const graphics_handle& h,
                   double d = 0)
    : base_property (nm, h),
      current_val (d) { }

  double_property (const double_property& p)
    : base_property (p), current_val (p.current_val) { }

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

  double double_value (void) const { return current_val; }

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

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

protected:
  void do_set (const octave_value& v)
    {
      if (v.is_scalar_type () && v.is_real_type ())
        current_val = v.double_value ();
      else
        error ("set: invalid value for double property \"%s\"",
               get_name ().c_str ());
    }

private:
  double current_val;
};

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

class double_radio_property : public base_property
{
public:
  double_radio_property (double d, const radio_values& v)
      : base_property ("", graphics_handle ()),
        current_type (double_t), dval (d), radio_val (v),
	current_val (v.default_value ())
  { }

  double_radio_property (const std::string& nm, const graphics_handle& h,
			 const std::string& v)
      : base_property (nm, h),
        current_type (radio_t), dval (0), radio_val (v),
	current_val (radio_val.default_value ())
  { }

  double_radio_property (const std::string& nm, const graphics_handle& h,
			 const double_radio_property& v)
      : base_property (nm, h),
        current_type (v.current_type), dval (v.dval),
	radio_val (v.radio_val), current_val (v.current_val)
  { }

  double_radio_property (const double_radio_property& p)
    : base_property (p), current_type (p.current_type),
      dval (p.dval), radio_val (p.radio_val),
      current_val (p.current_val) { }

  octave_value get (void) const
  {
    if (current_type == double_t)
      return dval;

    return current_val;
  }

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

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

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

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

    return dval;
  }

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

    return 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 void do_set (const octave_value& v);

private:
  enum current_enum { double_t, radio_t } current_type;
  double dval;
  radio_values radio_val;
  std::string current_val;
};

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

class array_property : public base_property
{
public:
  array_property (void)
      : base_property ("", graphics_handle ()), data (Matrix ())
    {
      get_data_limits ();
    }

  array_property (const std::string& nm, const graphics_handle& h,
                  const octave_value& m)
      : base_property (nm, h), data (m)
    {
      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), data (p.data),
      xmin (p.xmin), xmax (p.xmax), xminp (p.xminp) { }

  octave_value get (void) const { return data; }

  void add_constraint (const std::string& type)
    { type_constraints.push_back (type); }

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

  double min_val (void) const { return xmin; }
  double max_val (void) const { return xmax; }
  double min_pos (void) const { return xminp; }

  Matrix get_limits (void) const
    {
      Matrix m (1, 3);
      
      m(0) = min_val ();
      m(1) = max_val ();
      m(2) = min_pos ();

      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->type_constraints = type_constraints;
      p->size_constraints = size_constraints;
      
      return p;
    }

protected:
  void do_set (const octave_value& v)
    {
      if (validate (v))
	{
	  data = v;

	  get_data_limits ();
	}
      else
        error ("invalid value for array property \"%s\"",
               get_name ().c_str ());
    }

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

  OCTINTERP_API void get_data_limits (void);

protected:
  octave_value data;
  double xmin;
  double xmax;
  double xminp;
  std::list<std::string> type_constraints;
  std::list<dim_vector> size_constraints;
};

class 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));
  }

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

  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 (octave_idx_type len)
  {
    size_constraints.remove (dim_vector (1, -1));
    size_constraints.remove (dim_vector (-1, 1));

    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->type_constraints = type_constraints;
      p->size_constraints = size_constraints;

      return p;
    }

protected:
  void do_set (const octave_value& v)
  {
    array_property::do_set (v);

    if (! error_state)
      {
	dim_vector dv = data.dims ();

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

	    data = data.reshape (dv);
	  }
      }
  }

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

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

class 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 ("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:
  void do_set (const octave_value& val)
    {
      if (val.is_bool_scalar ())
        radio_property::do_set (val.bool_value () ? "on" : "off");
      else
        radio_property::do_set (val);
    }
};

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

class 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),
      current_val (val) { }

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

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

  graphics_handle handle_value (void) const { return 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;
    }

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

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

private:
  graphics_handle current_val;
};

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

class 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), data (m) { }

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

  octave_value get (void) const { return data; }

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

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

protected:
  void do_set (const octave_value& v) { data = v; }

private:
  octave_value data;
};

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

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

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

  octave_value get (void) const { return callback; }

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

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

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

protected:
  void do_set (const octave_value& v)
    {
      if (validate (v))
        callback = v;
      else
        error ("invalid value for callback property \"%s\"",
               get_name ().c_str ());
    }

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

private:
  octave_value callback;
};

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

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

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

  property (const property& p)
    {
      rep = p.rep;
      rep->count++;
    }

  ~property (void)
    {
      if (--rep->count <= 0)
        delete rep;
    }

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

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

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

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

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

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

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

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

  void set (const octave_value& val)
    { rep->set (val); }

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

  property& operator = (const property& p)
    {
      if (rep && --rep->count <= 0)
        delete rep;
      
      rep = p.rep;
      rep->count++;

      return *this;
    }

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

  void run_listeners (listener_mode mode = POSTSET)
    { rep->run_listeners (mode); }

  OCTINTERP_API static
      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 (rep->clone ()); }

  /*
  const string_property& as_string_property (void) const
    { return *(dynamic_cast<string_property*> (rep)); }

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

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

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

  const bool_property& as_bool_property (void) const
    { return *(dynamic_cast<bool_property*> (rep)); }
  
  const handle_property& as_handle_property (void) const
    { return *(dynamic_cast<handle_property*> (rep)); }
    */

private:
  base_property *rep;
};

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

class property_list
{
public:
  typedef std::map<std::string, octave_value> 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 ())
    : plist_map (m) { }

  ~property_list (void) { }

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

  octave_value lookup (const caseless_str& name) const;

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

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

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

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

  Octave_map as_struct (const std::string& prefix_arg) const;

private:
  plist_map_type plist_map;
};

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

class graphics_backend;

class base_graphics_backend
{
public:
  friend class graphics_backend;

public:
  base_graphics_backend (const std::string& nm)
      : name (nm), count (0) { }

  virtual ~base_graphics_backend (void) { }

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

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

  virtual void close_figure (const octave_value& /*pstream*/) const
    { gripe_invalid ("close_figure"); }

  virtual void redraw_figure (const graphics_handle&) const
    { gripe_invalid ("redraw_figure"); }

  virtual void print_figure (const graphics_handle&, const std::string&,
			     const std::string&, bool,
			     const std::string& = "") const
    { gripe_invalid ("print_figure"); }

  virtual Matrix get_canvas_size (const graphics_handle&) const
    {
      gripe_invalid ("get_canvas_size");
      return Matrix (1, 2, 0.0);
    }

  virtual double get_screen_resolution (void) const
    {
      gripe_invalid ("get_screen_resolution");
      return 72.0;
    }
  
  virtual Matrix get_screen_size (void) const
    {
      gripe_invalid ("get_screen_size");
      return Matrix (1, 2, 0.0);
    }

  virtual void set_figure_position (const graphics_handle&, const Matrix&) const
    { gripe_invalid ("set_figure_position"); }

private:
  std::string name;
  int count;

private:
  void gripe_invalid (const std::string& fname) const
    {
      if (! is_valid ())
	error ("%s: invalid graphics backend", fname.c_str ());
    }
};

class graphics_backend
{
public:
  graphics_backend (void)
      : rep (new base_graphics_backend ("unknown"))
    {
      rep->count++;
    }

  graphics_backend (base_graphics_backend* b)
      : rep (b)
    {
      rep->count++;
    }

  graphics_backend (const graphics_backend& b)
      : rep (b.rep)
    {
      rep->count++;
    }

  ~graphics_backend (void)
    {
      if (--rep->count == 0)
	delete rep;
    }

  graphics_backend& operator = (const graphics_backend& b)
    {
      if (rep != b.rep)
	{
	  if (--rep->count == 0)
	    delete rep;

	  rep = b.rep;
	  rep->count++;
	}

      return *this;
    }

  operator bool (void) const { return rep->is_valid (); }

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

  void close_figure (const octave_value& pstream) const
    { rep->close_figure (pstream); }

  void redraw_figure (const graphics_handle& fh) const
    { rep->redraw_figure (fh); }
  
  void print_figure (const graphics_handle& fh, const std::string& term,
		     const std::string& file, bool mono,
		     const std::string& debug_file = "") const
    { rep->print_figure (fh, term, file, mono, debug_file); }

  Matrix get_canvas_size (const graphics_handle& fh) const
    { return rep->get_canvas_size (fh); }

  double get_screen_resolution (void) const
    { return rep->get_screen_resolution (); }

  Matrix get_screen_size (void) const
    { return rep->get_screen_size (); }

  void set_figure_position (const graphics_handle& h, const Matrix& pos) const
    { rep->set_figure_position (h, pos); }

  OCTINTERP_API static graphics_backend default_backend (void);

  static void register_backend (const graphics_backend& b)
    { available_backends[b.get_name ()] = b; }

  static void unregister_backend (const std::string& name)
    { available_backends.erase (name); }

  static graphics_backend find_backend (const std::string& name)
  {
    const_available_backends_iterator p = available_backends.find (name);

    if (p != available_backends.end ())
      return p->second;
    else
      return default_backend ();
  }

  static Cell available_backends_list (void)
  {
    Cell m (1 , available_backends.size ());
    const_available_backends_iterator p;
    int i;
    
    for (i = 0,p = available_backends.begin (); p !=  available_backends.end (); p++,i++)
      m(i) = p->first;

    return m;
  }
	

private:
  base_graphics_backend *rep;

  static OCTINTERP_API std::map<std::string, graphics_backend> available_backends;

  typedef std::map<std::string, graphics_backend>::iterator available_backends_iterator;
  typedef std::map<std::string, graphics_backend>::const_iterator const_available_backends_iterator;
};

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

class base_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 ())
    : beingdeleted ("beingdeleted", mh, false),
      busyaction ("parent", mh, "{queue}|cancel"),
      buttondownfcn ("buttondownfcn", mh, Matrix ()),
      children (),
      clipping ("clipping", mh, true),
      createfcn ("createfcn", mh, Matrix ()),
      deletefcn ("deletefcn", mh, Matrix ()),
      handlevisibility ("handlevisibility", mh, "{on}|callback|off"),
      hittest ("hittest", mh, true),
      interruptible ("interruptible", mh, true),
      parent ("parent", mh, p), 
      selected ("selected", mh, false),
      selectionhighlight ("selectionhighlight", mh, true),
      tag ("tag", mh),
      type ("type", mh, ty),
      userdata ("userdata", mh, Matrix ()),
      visible ("visible", mh, true),
      __modified__ ("__modified__", mh, true),
      __myhandle__ (mh),
      uicontextmenu ("uicontextmenu", mh, graphics_handle ())
    { }

  virtual ~base_properties (void) { }

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

  void mark_modified (void);

  void override_defaults (base_graphics_object& obj);

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

  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 (__myhandle__);
      all_props[name] = p;
    }

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

  virtual octave_value get (const caseless_str&) const;

  virtual octave_value get (bool all = false) const;

  virtual property get_property (const caseless_str&);

  bool has_property (const caseless_str&);

  std::string get_tag (void) const { return tag.string_value (); }

  graphics_handle get_parent (void) const { return parent.handle_value (); }

  std::string get_type (void) const { return type.string_value (); }

  bool is_modified (void) const { return __modified__.is_on (); }
 
  graphics_handle get___myhandle__ (void) const { return __myhandle__; }
  
  std::string get_busyaction (void) const { return busyaction.current_value (); }

  octave_value get_buttondownfcn (void) const { return buttondownfcn.get (); }

  bool is_clipping (void) const { return clipping.is_on (); }
  std::string get_clipping (void) const { return clipping.current_value (); }

  void execute_createfcn (const octave_value& data = octave_value ()) const
    { createfcn.execute (data); }

  octave_value get_createfcn (void) const { return createfcn.get (); }

  void execute_deletefcn (const octave_value& data = octave_value ()) const
    { deletefcn.execute (data); }

  octave_value get_deletefcn (void) const { return deletefcn.get (); }

  std::string get_handlevisibility (void) const { return handlevisibility.current_value (); }

  bool is_hittest (void) const { return hittest.is_on (); }
  std::string get_hittest (void) const { return hittest.current_value (); }

  bool is_interruptible (void) const { return interruptible.is_on (); }
  std::string get_interruptible (void) const { return interruptible.current_value (); }

  bool is_selected (void) const { return selected.is_on (); }
  std::string get_selected (void) const { return selected.current_value (); }

  bool is_selectionhighlight (void) const { return selectionhighlight.is_on (); }
  std::string get_selectionhighlight (void) const { return selectionhighlight.current_value (); }
  
  octave_value get_uicontextmenu (void) const { return uicontextmenu.get (); }

  octave_value get_userdata (void) const { return userdata.get (); }
 
  bool is_visible (void) const { return visible.is_on (); }
  std::string get_visible (void) const { return visible.current_value (); }

  bool is_beingdeleted (void) const { return beingdeleted.is_on (); }
  std::string get_beingdeleted (void) const { return beingdeleted.current_value (); }

  virtual void remove_child (const graphics_handle& h);

  virtual void adopt (const graphics_handle& h)
  {
    octave_idx_type n = children.numel ();
    children.resize (1, n+1);
    children(n) = h.value ();
    mark_modified ();
  }

  virtual graphics_backend get_backend (void) const;

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

  virtual void update_boundingbox (void);

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

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

  void set_parent (const octave_value& val);

  void set_modified (const octave_value& val) { __modified__ = val; }

  void set_busyaction (const octave_value& val)
  {
    if (! error_state)
      {
        busyaction = val;
        mark_modified ();
      }
  }

  void set_buttondownfcn (const octave_value& val)
  {
    if (! error_state)
      {
        buttondownfcn = val;
        mark_modified ();
      }
  }

  void set_clipping (const octave_value& val)
  {
    if (! error_state)
      {
        clipping = val;
        mark_modified ();
      }
  }

  void set_createfcn (const octave_value& val)
  {
    if (! error_state)
      {
        createfcn = val;
        mark_modified ();
      }
  }

  void set_deletefcn (const octave_value& val)
  {
    if (! error_state)
      {
        deletefcn = val;
        mark_modified ();
      }
  }

  void set_handlevisibility (const octave_value& val)
  {
    if (! error_state)
      {
        handlevisibility = val;
        mark_modified ();
      }
  }

  void set_hittest (const octave_value& val)
  {
    if (! error_state)
      {
        hittest = val;
        mark_modified ();
      }
  }

  void set_interruptible (const octave_value& val)
  {
    if (! error_state)
      {
        interruptible = val;
        mark_modified ();
      }
  }

  void set_selected (const octave_value& val)
  {
    if (! error_state)
      {
        selected = val;
        mark_modified ();
      }
  }

  void set_selectionhighlight (const octave_value& val)
  {
    if (! error_state)
      {
        selectionhighlight = val;
        mark_modified ();
      }
  }

  void set_uicontextmenu (const octave_value& val)
  {
    if (! error_state)
      {
        uicontextmenu = val;
        mark_modified ();
      }
  }

  void set_userdata (const octave_value& val)
  {
    if (! error_state)
      {
        userdata = val;
        mark_modified ();
      }
  }

  virtual void set_visible (const octave_value& val)
  {
    if (! error_state)
      {
        visible = val;
        mark_modified ();
      }
  }

  void set_beingdeleted (const octave_value& val)
  {
    if (! error_state)
      {
	beingdeleted = val;
	mark_modified ();
      }
  }



  void reparent (const graphics_handle& new_parent) { 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 delete_children (void);

  Matrix get_children (void) const { return children; }

  // FIXME -- these functions should be generated automatically by the
  // genprops.awk script.
  //
  // EMIT_BASE_PROPERTIES_GET_FUNCTIONS

  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 octave_value get_clim (void) const { return octave_value (); }
  virtual octave_value get_alim (void) const { return octave_value (); }

  virtual bool is_xliminclude (void) const { return false; }
  virtual bool is_yliminclude (void) const { return false; }
  virtual bool is_zliminclude (void) const { return false; }
  virtual bool is_climinclude (void) const { return false; }
  virtual bool is_aliminclude (void) const { return false; }
  
protected:
  // properties common to all objects
  bool_property beingdeleted;
  radio_property busyaction;
  callback_property buttondownfcn;
  // FIXME: use a property class for children
  Matrix children;
  bool_property clipping;
  callback_property createfcn;
  callback_property deletefcn;
  radio_property handlevisibility;
  bool_property hittest;
  bool_property interruptible;
  handle_property parent;
  bool_property selected;
  bool_property selectionhighlight;
  string_property tag;
  string_property type;
  any_property userdata;
  bool_property visible;
  // additional (octave-specific) properties
  bool_property __modified__;
  graphics_handle __myhandle__;
  // FIXME: should this really be here?
  handle_property uicontextmenu;

protected:
  std::map<caseless_str, property> all_props;

protected:
  void insert_static_property (const std::string& name, base_property& p)
    { insert_property (name, property (&p, true)); }
  
  virtual void init (void) { }
};

class OCTINTERP_API base_graphics_object
{
public:
  friend class graphics_object;

  base_graphics_object (void) : count (1) { }

  base_graphics_object (const base_graphics_object&) { }

  virtual ~base_graphics_object (void) { }

  virtual void mark_modified (void)
  {
    if (valid_object ())
      get_properties ().mark_modified ();
    else
      error ("base_graphics_object::mark_modified: invalid graphics object");
  }

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

  virtual void set_from_list (property_list& plist)
  {
    if (valid_object ())
      get_properties ().set_from_list (*this, plist);
    else
      error ("base_graphics_object::set_from_list: invalid graphics object");
  }

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

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

  virtual octave_value get (bool all = false) const
  {
    if (valid_object ())
      return get_properties ().get (all);
    else
      {
        error ("base_graphics_object::get: invalid graphics object");
        return octave_value ();
      }
  }

  virtual octave_value get (const caseless_str& pname) const
  {
    if (valid_object ())
      return get_properties ().get (pname);
    else
      {
        error ("base_graphics_object::get: invalid graphics object");
        return octave_value ();
      }
  }

  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");
    return octave_value ();
  }

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

  virtual graphics_handle get_parent (void) const
  {
    if (valid_object ())
      return get_properties ().get_parent ();
    else
      {
        error ("base_graphics_object::get_parent: invalid graphics object");
        return graphics_handle ();
      }
  }

  graphics_handle get_handle (void) const
  {
    if (valid_object ())
      return get_properties ().get___myhandle__ ();
    else
      {
        error ("base_graphics_object::get_handle: invalid graphics object");
        return graphics_handle ();
      }
  }

  virtual void remove_child (const graphics_handle& h)
  {
    if (valid_object ())
      get_properties ().remove_child (h);
    else
      error ("base_graphics_object::remove_child: invalid graphics object");
  }

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

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

  virtual void defaults (void) const
  {
    if (valid_object ())
      {
        std::string msg = (type () + "::defaults");
        gripe_not_implemented (msg.c_str ());
      }
    else
      error ("base_graphics_object::default: invalid graphics object");
  }

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

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

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

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

  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 graphics_backend get_backend (void) const
  {
    if (valid_object ())
      return get_properties ().get_backend ();
    else
      {
	error ("base_graphics_object::get_backend: invalid graphics object");
	return graphics_backend ();
      }
  }

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

protected:
  // A reference count.
  int count;
};

class OCTINTERP_API graphics_object
{
public:
  graphics_object (void) : rep (new base_graphics_object ()) { }

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

  graphics_object (const graphics_object& obj)
  {
    rep = obj.rep;
    rep->count++;
  }

  graphics_object& operator = (const graphics_object& obj)
  {
    if (rep != obj.rep)
      {
	if (--rep->count == 0)
	  delete rep;

	rep = obj.rep;
	rep->count++;
      }

    return *this;
  }

  ~graphics_object (void)
  {
    if (--rep->count == 0)
      delete rep;
  }

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

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

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

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

  void set (const octave_value_list& args);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  // FIXME -- these functions should be generated automatically by the
  // genprops.awk script.
  //
  // EMIT_GRAPHICS_OBJECT_GET_FUNCTIONS

  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 (); }
  
  octave_value get_clim (void) const
  { return get_properties ().get_clim (); }
  
  octave_value get_alim (void) const
  { return get_properties ().get_alim (); }

  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_climinclude (void) const
  { return get_properties ().is_climinclude (); }
  
  bool is_aliminclude (void) const
  { return get_properties ().is_aliminclude (); }
  
  graphics_backend get_backend (void) const { return rep->get_backend (); }

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

private:
  base_graphics_object *rep;
};

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

class OCTINTERP_API root_figure : 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.

    BEGIN_PROPERTIES(root_figure)
      handle_property currentfigure S , graphics_handle ()
      handle_property callbackobject Sr , graphics_handle ()
    END_PROPERTIES

  private:
    std::list<graphics_handle> cbo_stack;
  };

private:
  properties xproperties;

public:

  root_figure (void) : xproperties (0, graphics_handle ()), default_properties () { }

  ~root_figure (void) { xproperties.delete_children (); }

  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 (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.
      default_properties.set (name.substr (7), value);
    else
      xproperties.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 = xproperties.get (name);

    return retval;
  }

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

    if (retval.is_undefined ())
      {
	// no default property found, use factory default
	retval = 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 = 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 default_properties.as_struct ("default");
  }

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

  base_properties& get_properties (void) { return xproperties; }

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

  bool valid_object (void) const { return true; }

private:
  property_list default_properties;

  static property_list factory_properties;

  static 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 close (bool pop = true);

    void set_visible (const octave_value& val);

    graphics_backend get_backend (void) const
      {
	if (! backend)
	  backend = graphics_backend::default_backend ();

	return backend;
      }

    void set_backend (const graphics_backend& b) 
    { 
      close (false);
      backend = b; 
      __backend__ = b.get_name ();
      __plot_stream__ = Matrix ();
      mark_modified ();
    }

    void set___backend__ (const octave_value& val)
    {
      if (! error_state)
	{
	  if (val.is_string ())
	    {
	      std::string nm = val.string_value ();
	      graphics_backend b = graphics_backend::find_backend (nm);
	      if (b.get_name () != nm)
		{
		  error ("set___backend__: invalid backend");
		}
	      else
		{
		  set_backend (b);
		  mark_modified ();
		}
	    }
	  else
	    error ("set___backend__ must be a string");
	}
    }

    Matrix get_boundingbox (bool internal = false) const;

    void set_boundingbox (const Matrix& bb);

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

    BEGIN_PROPERTIES(figure)
      any_property __plot_stream__ h , Matrix ()
      bool_property __enhanced__ h , "on"
      radio_property nextplot , "new|{add}|replace_children|replace"
      callback_property closerequestfcn , "closereq"
      handle_property currentaxes S , graphics_handle ()
      array_property colormap , jet_colormap ()
      radio_property paperorientation , "{portrait}|landscape|rotated"
      color_property color , color_values (1, 1, 1)
      array_property alphamap , Matrix (64, 1, 1)
      string_property currentcharacter r , ""
      handle_property currentobject r , graphics_handle ()
      array_property current_point r , Matrix (2, 1, 0)
      bool_property dockcontrols , "off"
      bool_property doublebuffer , "on"
      string_property filename r , ""
      bool_property integerhandle , "on"
      bool_property inverthardcopy , "off"
      callback_property keypressfcn , Matrix ()
      callback_property keyreleasefcn , Matrix ()
      radio_property menubar , "none|{figure}"
      double_property mincolormap , 64
      string_property name , ""
      bool_property numbertitle , "on"
      radio_property paperunits , "{inches}|centimeters|normalized|points"
      array_property paperposition , Matrix (1, 4 , 0) 
      radio_property paperpositionmode , "auto|{manual}"
      array_property papersize r , Matrix (1, 4, 0)
      radio_property papertype , "{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"
      radio_property pointer , "crosshair|fullcrosshair|{arrow}|ibeam|watch|topl|topr|botl|botr|left|top|right|bottom|circle|cross|fleur|custom|hand"
      array_property pointershapecdata , Matrix (16, 16, 0)
      array_property pointershapehotspot , Matrix (1, 2, 0)
      array_property position S , default_figure_position ()
      radio_property renderer , "{painters}|zbuffer|opengl|none"
      radio_property renderermode , "{auto}|manual"
      bool_property resize , "on"
      callback_property resizefcn , Matrix ()
      radio_property selectiontype , "{normal}|open|alt|extend"
      radio_property toolbar , "none|{auto}|figure"
      radio_property units , "inches|centimeters|normalized|points|{pixels}|characters"
      callback_property windowbuttondownfcn , Matrix ()
      callback_property windowbuttonmotionfcn , Matrix ()
      callback_property windowbuttonupfcn , Matrix ()
      callback_property windowbuttonwheelfcn , Matrix ()
      radio_property windowstyle , "{normal}|modal|docked"
      string_property wvisual , ""
      radio_property wvisualmode , "{auto}|manual"
      string_property xdisplay , ""
      string_property xvisual , ""
      radio_property xvisualmode , "{auto}|manual"
      callback_property buttondownfcn , Matrix ()
      string_property __backend__ s , "gnuplot"
    END_PROPERTIES
    
  protected:
    void init (void)
      {
        colormap.add_constraint (dim_vector (-1, 3));
	alphamap.add_constraint (dim_vector (-1, 1));
	paperposition.add_constraint (dim_vector (1, 4));
	pointershapecdata.add_constraint (dim_vector (16, 16));
	pointershapehotspot.add_constraint (dim_vector (1, 2));
	position.add_constraint (dim_vector (1, 4));
      }

  private:
    mutable graphics_backend backend;
  };

private:
  properties xproperties;

public:
  figure (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), xproperties (mh, p), default_properties ()
  {
    xproperties.override_defaults (*this);
  }

  ~figure (void)
  {
    xproperties.delete_children (); 
    xproperties.close ();
  }

  void override_defaults (base_graphics_object& obj)
  {
    // Allow parent (root figure) to override first (properties knows how
    // to find the parent object).
    xproperties.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 (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.
      default_properties.set (name.substr (7), value);
    else
      xproperties.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 = xproperties.get (name);

    return retval;
  }

  octave_value get_default (const caseless_str& name) const;

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

  base_properties& get_properties (void) { return xproperties; }

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

  bool valid_object (void) const { return true; }

private:
  property_list default_properties;
};

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

class OCTINTERP_API graphics_xform
{
public:
  graphics_xform (void)
      : xform (xform_eye ()), xform_inv (xform_eye ())
    {
      sx = sy = sz = "linear";
    }

  graphics_xform (const Matrix& xm, const Matrix& xim,
		  const scaler& x, const scaler& y, const scaler& z)
      : xform (xm), xform_inv (xim), sx (x), sy (y), sz (z) { }

  graphics_xform (const graphics_xform& g)
      : xform (g.xform), xform_inv (g.xform_inv), sx (g.sx),
        sy (g.sy), sz (g.sz) { }

  ~graphics_xform (void) { }

  graphics_xform& operator = (const graphics_xform& g)
    {
      xform = g.xform;
      xform_inv = g.xform_inv;
      sx = g.sx;
      sy = g.sy;
      sz = g.sz;

      return *this;
    }

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

  static Matrix xform_eye (void);

  ColumnVector transform (double x, double y, double z,
			  bool scale = true) const;
  
  ColumnVector untransform (double x, double y, double z,
			    bool scale = true) const;

  Matrix xscale (const Matrix& m) const { return sx.scale (m); }
  Matrix yscale (const Matrix& m) const { return sy.scale (m); }
  Matrix zscale (const Matrix& m) const { return sz.scale (m); }

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

      if (sx.is_linear () && sy.is_linear ()
	  && (! has_z || sz.is_linear ()))
	return m;

      Matrix retval (m.dims ());

      int r = m.rows ();

      for (int i = 0; i < r; i++)
	{
	  retval(i,0) = sx.scale (m(i,0));
	  retval(i,1) = sy.scale (m(i,1));
	  if (has_z)
	    retval(i,2) = sz.scale (m(i,2));
	}

      return retval;
    }

private:
  Matrix xform;
  Matrix xform_inv;
  scaler sx, sy, sz;
};

class OCTINTERP_API axes : public base_graphics_object
{
public:
  class OCTINTERP_API properties : public base_properties
  {
  public:
    void set_defaults (base_graphics_object& obj, const std::string& mode);

    void remove_child (const graphics_handle& h);

    void delete_children (void);

    const scaler& get_x_scaler (void) const { return sx; }
    const scaler& get_y_scaler (void) const { return sy; }
    const scaler& get_z_scaler (void) const { return sz; }

    Matrix get_boundingbox (bool internal = false) const;

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

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

    graphics_xform get_transform (void) const
      { return graphics_xform (x_render, x_render_inv, sx, sy, sz); }

    Matrix get_transform_matrix (void) const { return x_render; }
    Matrix get_inverse_transform_matrix (void) const { return x_render_inv; }
    Matrix get_opengl_matrix_1 (void) const { return x_gl_mat1; }
    Matrix get_opengl_matrix_2 (void) const { return x_gl_mat2; }
    Matrix get_transform_zlim (void) const { return x_zlim; }

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

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

    void zoom (const Matrix& xl, const Matrix& yl);
    void unzoom (void);
    void clear_zoom_stack (void);

  private:
    scaler sx, sy, sz;
    Matrix x_render, x_render_inv;
    Matrix x_gl_mat1, x_gl_mat2;
    Matrix x_zlim;
    std::list<octave_value> zoom_stack;

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

    BEGIN_PROPERTIES(axes)
      array_property position u , default_axes_position ()
      mutable handle_property title GSO , graphics_handle ()
      bool_property box , "on"
      bool_property key , "off"
      bool_property keybox , "off"
      any_property keypos , 1
      array_property colororder , default_colororder ()
      array_property dataaspectratio m , Matrix (1, 3, 1.0)
      radio_property dataaspectratiomode , "{auto}|manual"
      radio_property layer , "{bottom}|top"
      row_vector_property xlim mu , default_lim ()
      row_vector_property ylim mu , default_lim ()
      row_vector_property zlim mu , default_lim ()
      row_vector_property clim m , default_lim ()
      row_vector_property alim m , default_lim ()
      radio_property xlimmode al , "{auto}|manual"
      radio_property ylimmode al , "{auto}|manual"
      radio_property zlimmode al , "{auto}|manual"
      radio_property climmode al , "{auto}|manual"
      radio_property alimmode    , "{auto}|manual"
      mutable handle_property xlabel GSO , graphics_handle ()
      mutable handle_property ylabel GSO , graphics_handle ()
      mutable handle_property zlabel GSO , graphics_handle ()
      bool_property xgrid , "off"
      bool_property ygrid , "off"
      bool_property zgrid , "off"
      bool_property xminorgrid , "off"
      bool_property yminorgrid , "off"
      bool_property zminorgrid , "off"
      row_vector_property xtick m , Matrix ()
      row_vector_property ytick m , Matrix ()
      row_vector_property ztick m , Matrix ()
      radio_property xtickmode , "{auto}|manual"
      radio_property ytickmode , "{auto}|manual"
      radio_property ztickmode , "{auto}|manual"
      bool_property xminortick , "off"
      bool_property yminortick , "off"
      bool_property zminortick , "off"
      // FIXME: should be kind of string array
      any_property xticklabel m , ""
      any_property yticklabel m , ""
      any_property zticklabel m , ""
      radio_property xticklabelmode , "{auto}|manual"
      radio_property yticklabelmode , "{auto}|manual"
      radio_property zticklabelmode , "{auto}|manual"
      color_property color , color_property (color_values (1, 1, 1), radio_values ("none"))
      color_property xcolor , color_values (0, 0, 0)
      color_property ycolor , color_values (0, 0, 0)
      color_property zcolor , color_values (0, 0, 0)
      radio_property xscale alu , "{linear}|log"
      radio_property yscale alu , "{linear}|log"
      radio_property zscale alu , "{linear}|log"
      radio_property xdir u , "{normal}|reverse"
      radio_property ydir u , "{normal}|reverse"
      radio_property zdir u , "{normal}|reverse"
      radio_property yaxislocation , "{left}|right|zero"
      radio_property xaxislocation , "{bottom}|top|zero"
      array_property view u , Matrix ()
      radio_property nextplot , "add|replace_children|{replace}"
      array_property outerposition u , default_axes_outerposition ()
      radio_property activepositionproperty , "{outerposition}|position"
      radio_property __colorbar__ h , "{none}|north|south|east|west|northoutside|southoutside|eastoutside|westoutside"
      color_property ambientlightcolor , color_values (1, 1, 1)
      array_property cameraposition m , Matrix (1, 3, 0.0)
      array_property cameratarget m , Matrix (1, 3, 0.0)
      array_property cameraupvector m , Matrix ()
      double_property cameraviewangle m , 10.0
      radio_property camerapositionmode , "{auto}|manual"
      radio_property cameratargetmode , "{auto}|manual"
      radio_property cameraupvectormode , "{auto}|manual"
      radio_property cameraviewanglemode , "{auto}|manual"
      array_property currentpoint , Matrix (2, 3, 0.0)
      radio_property drawmode , "{normal}|fast"
      radio_property fontangle , "{normal}|italic|oblique"
      string_property fontname , "Helvetica"
      double_property fontsize , 12
      radio_property fontunits , "{points}|normalized|inches|centimeters|pixels"
      radio_property fontweight , "{normal}|light|demi|bold"
      radio_property gridlinestyle , "-|--|{:}|-.|none"
      // FIXME: should be kind of string array
      string_property linestyleorder , "-"
      double_property linewidth , 0.5
      radio_property minorgridlinestyle , "-|--|{:}|-.|none"
      array_property plotboxaspectratio m , Matrix (1, 3, 1.0)
      radio_property plotboxaspectratiomode , "{auto}|manual"
      radio_property projection , "{orthographic}|perpective"
      radio_property tickdir m , "{in}|out"
      radio_property tickdirmode , "{auto}|manual"
      array_property ticklength , Matrix (1, 2, 0.1)
      array_property tightinset r , Matrix (1, 4, 0.0)
      // FIXME: uicontextmenu should be moved here
      radio_property units , "{normalized}|inches|centimeters|points|pixels|characters"
      // hidden properties for transformation computation
      array_property x_viewtransform h , Matrix (4, 4, 0.0)
      array_property x_projectiontransform h , Matrix (4, 4, 0.0)
      array_property x_viewporttransform h , Matrix (4, 4, 0.0)
      array_property x_normrendertransform h , Matrix (4, 4, 0.0)
      array_property x_rendertransform h , Matrix (4, 4, 0.0)
   END_PROPERTIES

  protected:
    void init (void)
      {
        position.add_constraint (dim_vector (1, 4));
	position.add_constraint (dim_vector (0, 0));
        outerposition.add_constraint (dim_vector (1, 4));
        colororder.add_constraint (dim_vector (-1, 3));
        dataaspectratio.add_constraint (dim_vector (1, 3));
        plotboxaspectratio.add_constraint (dim_vector (1, 3));
	xlim.add_constraint (2);
	ylim.add_constraint (2);
	zlim.add_constraint (2);
	clim.add_constraint (2);
	alim.add_constraint (2);
        xtick.add_constraint (dim_vector (1, -1));
        ytick.add_constraint (dim_vector (1, -1));
        ztick.add_constraint (dim_vector (1, -1));
        Matrix vw (1, 2, 0);
        vw(1) = 90;
        view = vw;
        view.add_constraint (dim_vector (1, 2));
	cameraposition.add_constraint (dim_vector (1, 3));
	Matrix upv (1, 3, 0.0);
	upv(2) = 1.0;
	cameraupvector = upv;
	cameraupvector.add_constraint (dim_vector (1, 3));
	currentpoint.add_constraint (dim_vector (2, 3));
	ticklength.add_constraint (dim_vector (1, 2));
	tightinset.add_constraint (dim_vector (1, 4));

	x_zlim.resize (1, 2);
	sx = "linear";
	sy = "linear";
	sz = "linear";
      }

  private:
    void update_xscale (void) { sx = get_xscale (); }
    void update_yscale (void) { sy = get_yscale (); }
    void update_zscale (void) { sz = get_zscale (); }

    void update_view (void) { update_camera (); }

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

    void sync_positions (void);
    void update_outerposition (void) { sync_positions ();}
    void update_position (void) { sync_positions (); }

    double calc_tick_sep (double minval, double maxval);
    void calc_ticks_and_lims (array_property& lims, array_property& ticks, bool limmode_is_auto, bool is_logscale);
    void fix_limits (array_property& lims)
    {
      if (lims.get ().is_empty ()) 
	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;
	}
    }      

  public:
    Matrix get_axis_limits (double xmin, double xmax, double min_pos, bool logscale);
    
    void update_xlim (bool do_clr_zoom = true)
    {
      if (xtickmode.is ("auto"))
	calc_ticks_and_lims (xlim, xtick, xlimmode.is ("auto"), xscale.is ("log"));

      fix_limits (xlim);

      if (do_clr_zoom)
	zoom_stack.clear ();
    }

    void update_ylim (bool do_clr_zoom = true)
    {
      if (ytickmode.is ("auto"))
	calc_ticks_and_lims (ylim, ytick, ylimmode.is ("auto"), yscale.is ("log"));

      fix_limits (ylim);

      if (do_clr_zoom)
	zoom_stack.clear ();
    }

    void update_zlim (void)
    {
      if (ztickmode.is ("auto"))
	calc_ticks_and_lims (zlim, ztick, zlimmode.is ("auto"), zscale.is ("log"));

      fix_limits (zlim);

      zoom_stack.clear ();
    }
    
  };

private:
  properties xproperties;

public:
  axes (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), xproperties (mh, p), default_properties ()
  {
    xproperties.override_defaults (*this);
    xproperties.update_transform ();
  }

  ~axes (void) { xproperties.delete_children (); }

  void override_defaults (base_graphics_object& obj)
  {
    // Allow parent (figure) to override first (properties knows how
    // to find the parent object).
    xproperties.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 (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.
      default_properties.set (name.substr (7), value);
    else
      xproperties.set (name, value);
  }

  void set_defaults (const std::string& mode)
  {
    xproperties.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 = xproperties.get (name);

    return retval;
  }

  octave_value get_default (const caseless_str& name) const;

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

  base_properties& get_properties (void) { return xproperties; }

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

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

  bool valid_object (void) const { return true; }

private:
  property_list 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.

    // properties which are not in matlab:
    // ldata, udata, xldata, xudata, keylabel, interpreter

    BEGIN_PROPERTIES(line)
      row_vector_property xdata u , default_data ()
      row_vector_property ydata u , default_data ()
      row_vector_property zdata u , Matrix ()
      row_vector_property ldata u , Matrix ()
      row_vector_property udata u , Matrix ()
      row_vector_property xldata u , Matrix ()
      row_vector_property xudata u , Matrix ()
      color_property color , color_values (0, 0, 0)
      radio_property linestyle , "{-}|--|:|-.|none"
      double_property linewidth , 0.5
      radio_property marker , "{none}|s|o|x|+|.|*|<|>|v|^|d|p|h"
      color_property markeredgecolor , "{auto}|none"
      color_property markerfacecolor , "auto|{none}"
      double_property markersize , 6
      string_property keylabel , ""
      radio_property interpreter , "{tex}|none|latex"
      string_property displayname , ""
      radio_property erasemode , "{normal}|none|xor|background"
      // 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 , "on"
      bool_property yliminclude hl , "on"
      bool_property zliminclude hl , "off"
    END_PROPERTIES

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

    void update_xdata (void) { set_xlim (compute_xlim ()); }
    void update_xldata (void) { set_xlim (compute_xlim ()); }
    void update_xudata (void) { set_xlim (compute_xlim ()); }
    
    void update_ydata (void) { set_ylim (compute_ylim ()); }
    void update_ldata (void) { set_ylim (compute_ylim ()); }
    void update_udata (void) { set_ylim (compute_ylim ()); }

    void update_zdata (void)
      {
	set_zlim (zdata.get_limits ());
	set_zliminclude (get_zdata ().numel () > 0);
      }
  };

private:
  properties xproperties;

public:
  line (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), xproperties (mh, p)
  {
    xproperties.override_defaults (*this);
  }

  ~line (void) { xproperties.delete_children (); }

  base_properties& get_properties (void) { return xproperties; }

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

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

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

class OCTINTERP_API text : 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.

    BEGIN_PROPERTIES(text)
      string_property string , ""
      radio_property units , "{data}|pixels|normalized|inches|centimeters|points"
      array_property position u , Matrix (1, 3, 0.0)
      double_property rotation , 0
      radio_property horizontalalignment , "{left}|center|right"
      color_property color , color_values (0, 0, 0)
      string_property fontname , "Helvetica"
      double_property fontsize , 10
      radio_property fontangle , "{normal}|italic|oblique"
      radio_property fontweight , "light|{normal}|demi|bold"
      radio_property interpreter , "{tex}|none|latex"
      color_property backgroundcolor , "{none}"
      string_property displayname , ""
      color_property edgecolor , "{none}"
      radio_property erasemode , "{normal}|none|xor|background"
      bool_property editing , "off"
      radio_property fontunits , "inches|centimeters|normalized|{points}|pixel"
      radio_property linestyle , "{-}|--|:|-.|none"
      double_property linewidth , 0.5
      double_property margin , 1
      radio_property verticalalignment , "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 , "on"
      bool_property yliminclude hl , "on"
      bool_property zliminclude hl , "off"
    END_PROPERTIES

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

  private:
    void update_position (void)
      {
	Matrix pos = get_position ().matrix_value ();
	Matrix lim;

	lim = Matrix (1, 3, pos(0));
	lim(2) = (lim(2) <= 0 ? octave_Inf : lim(2));
	set_xlim (lim);

	lim = Matrix (1, 3, pos(1));
	lim(2) = (lim(2) <= 0 ? octave_Inf : lim(2));
	set_ylim (lim);

	if (pos.numel () == 3)
	  {
	    lim = Matrix (1, 3, pos(2));
	    lim(2) = (lim(2) <= 0 ? octave_Inf : lim(2));
	    set_zliminclude ("on");
	    set_zlim (lim);
	  }
	else
	  set_zliminclude ("off");
      }
  };

private:
  properties xproperties;

public:
  text (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), xproperties (mh, p)
  {
    xproperties.override_defaults (*this);
  }

  ~text (void) { xproperties.delete_children (); }

  base_properties& get_properties (void) { return xproperties; }

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

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

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

class OCTINTERP_API image : public base_graphics_object
{
public:
  class OCTINTERP_API properties : public base_properties
  {
  public:
    bool is_climinclude (void) const
      { return (climinclude.is_on () && cdatamapping.is ("scaled")); }
    std::string get_climinclude (void) const
      { return climinclude.current_value (); }

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

    BEGIN_PROPERTIES(image)
      row_vector_property xdata u , Matrix ()
      row_vector_property ydata u , Matrix ()
      array_property cdata u , Matrix ()
      radio_property cdatamapping al , "{scaled}|direct"
      // hidden properties for limit computation
      row_vector_property xlim hlr , Matrix()
      row_vector_property ylim hlr , Matrix()
      row_vector_property clim hlr , Matrix()
      bool_property xliminclude hl , "on"
      bool_property yliminclude hl , "on"
      bool_property climinclude hlg , "on"
    END_PROPERTIES

  protected:
    void init (void)
      {
	xdata.add_constraint (2);
	ydata.add_constraint (2);
	cdata.add_constraint ("double");
	cdata.add_constraint ("uint8");
	cdata.add_constraint (dim_vector (-1, -1));
	cdata.add_constraint (dim_vector (-1, -1, 3));
      }

  private:
    // FIXME: limits should take pixel width into account
    void update_xdata (void)
      { set_xlim (xdata.get_limits ()); }

    // FIXME: idem
    void update_ydata (void)
      { set_ylim (ydata.get_limits ()); }

    void update_cdata (void)
      {
	if (cdatamapping_is ("scaled"))
	  set_clim (cdata.get_limits ());
	else
	  clim = cdata.get_limits ();
      }
  };

private:
  properties xproperties;

public:
  image (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), xproperties (mh, p)
  {
    xproperties.override_defaults (*this);
  }

  ~image (void) { xproperties.delete_children (); }

  base_properties& get_properties (void) { return xproperties; }

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

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

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

class OCTINTERP_API patch : public base_graphics_object
{
public:
  class OCTINTERP_API properties : public base_properties
  {
  public:
    octave_value get_color_data (void) const;
    
    bool is_climinclude (void) const
      { return (climinclude.is_on () && cdatamapping.is ("scaled")); }
    std::string get_climinclude (void) const
      { return climinclude.current_value (); }

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

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

    BEGIN_PROPERTIES(patch)
      array_property xdata u , Matrix ()
      array_property ydata u , Matrix ()
      array_property zdata u , Matrix ()
      array_property cdata u , Matrix ()
      radio_property cdatamapping l , "{scaled}|direct"
      array_property faces , Matrix ()
      array_property facevertexalphadata , Matrix ()
      array_property facevertexcdata , Matrix ()
      array_property vertices , Matrix ()
      array_property vertexnormals , Matrix ()
      radio_property normalmode , "{auto}|manual"
      color_property facecolor , "{flat}|none|interp"
      double_radio_property facealpha , double_radio_property (1.0, radio_values ("flat|interp"))
      radio_property facelighting , "flat|{none}|gouraud|phong"
      color_property edgecolor , color_property (color_values (0, 0, 0), radio_values ("flat|none|interp"))
      double_radio_property edgealpha , double_radio_property (1.0, radio_values ("flat|interp"))
      radio_property edgelighting , "{none}|flat|gouraud|phong"
      radio_property backfacelighting , "{reverselit}|unlit|lit"
      double_property ambientstrength , 0.3
      double_property diffusestrength , 0.6
      double_property specularstrength , 0.6
      double_property specularexponent , 10.0
      double_property specularcolorreflectance , 1.0
      radio_property erasemode , "{normal}|background|xor|none"
      radio_property linestyle , "{-}|--|:|-.|none"
      double_property linewidth , 0.5
      radio_property marker , "{none}|s|o|x|+|.|*|<|>|v|^|d|p|h"
      color_property markeredgecolor , "{auto}|none"
      color_property markerfacecolor , "auto|{none}"
      double_property markersize , 6
      string_property keylabel , ""
      radio_property interpreter , "{tex}|none|latex"
      radio_property alphadatamapping l , "none|{scaled}|direct"
      // hidden properties for limit computation
      row_vector_property xlim hlr , Matrix ()
      row_vector_property ylim hlr , Matrix ()
      row_vector_property zlim hlr , Matrix ()
      row_vector_property clim hlr , Matrix ()
      row_vector_property alim hlr , Matrix ()
      bool_property xliminclude hl , "on"
      bool_property yliminclude hl , "on"
      bool_property zliminclude hl , "on"
      bool_property climinclude hlg , "on"
      bool_property aliminclude hlg , "on"
    END_PROPERTIES

  protected:
    void init (void)
      {
	xdata.add_constraint (dim_vector (-1, -1));
	ydata.add_constraint (dim_vector (-1, -1));
	zdata.add_constraint (dim_vector (-1, -1));
        vertices.add_constraint (dim_vector (-1, 2));
        vertices.add_constraint (dim_vector (-1, 3));
	cdata.add_constraint (dim_vector (-1, -1));
	cdata.add_constraint (dim_vector (-1, -1, 3));
	facevertexcdata.add_constraint (dim_vector (-1, 1));
	facevertexcdata.add_constraint (dim_vector (-1, 3));
	facevertexalphadata.add_constraint (dim_vector (-1, 1));
      }

  private:
    void update_xdata (void) { set_xlim (xdata.get_limits ()); }
    void update_ydata (void) { set_ylim (ydata.get_limits ()); }
    void update_zdata (void) { set_zlim (zdata.get_limits ()); }
    
    void update_cdata (void)
      {
	if (cdatamapping_is ("scaled"))
	  set_clim (cdata.get_limits ());
	else
	  clim = cdata.get_limits ();
      }
  };

private:
  properties xproperties;

public:
  patch (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), xproperties (mh, p)
  {
    xproperties.override_defaults (*this);
  }

  ~patch (void) { xproperties.delete_children (); }

  base_properties& get_properties (void) { return xproperties; }

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

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

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

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_climinclude (void) const
      { return (climinclude.is_on () && cdatamapping.is ("scaled")); }
    std::string get_climinclude (void) const
      { return climinclude.current_value (); }

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

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

    BEGIN_PROPERTIES(surface)
      array_property xdata u , Matrix ()
      array_property ydata u , Matrix ()
      array_property zdata u , Matrix ()
      array_property cdata u , Matrix ()
      radio_property cdatamapping al , "{scaled}|direct"
      color_property facecolor , "{flat}|none|interp|texturemap"
      double_radio_property facealpha , double_radio_property (1.0, radio_values ("flat|interp"))
      color_property edgecolor , color_property (color_values (0, 0, 0), radio_values ("flat|none|interp"))
      radio_property linestyle , "{-}|--|:|-.|none"
      double_property linewidth , 0.5
      radio_property marker , "{none}|s|o|x|+|.|*|<|>|v|^|d|p|h"
      color_property markeredgecolor , "{auto}|none"
      color_property markerfacecolor , "auto|{none}"
      double_property markersize , 6
      string_property keylabel , ""
      radio_property interpreter , "{tex}|none|latex"
      array_property alphadata u , Matrix ()
      radio_property alphadatamapping l , "none|direct|{scaled}"
      double_property ambientstrength , 0.3
      radio_property backfacelighting , "unlit|lit|{reverselit}"
      double_property diffusestrength , 0.6
      double_radio_property edgealpha , double_radio_property (1.0, radio_values ("flat|interp"))
      radio_property edgelighting , "{none}|flat|gouraud|phong"
      radio_property erasemode , "{normal}|none|xor|background"
      radio_property facelighting , "{none}|flat|gouraud|phong"
      radio_property meshstyle , "{both}|row|column"
      radio_property normalmode u , "{auto}|manual"
      double_property specularcolorreflectance , 1
      double_property specularexponent , 10
      double_property specularstrength , 0.9
      array_property vertexnormals u , Matrix ()
      // hidden properties for limit computation
      row_vector_property xlim hlr , Matrix ()
      row_vector_property ylim hlr , Matrix ()
      row_vector_property zlim hlr , Matrix ()
      row_vector_property clim hlr , Matrix ()
      row_vector_property alim hlr , Matrix ()
      bool_property xliminclude hl , "on"
      bool_property yliminclude hl , "on"
      bool_property zliminclude hl , "on"
      bool_property climinclude hlg , "on"
      bool_property aliminclude hlg , "on"
    END_PROPERTIES

  protected:
    void init (void)
      {
	xdata.add_constraint (dim_vector (-1, -1));
	ydata.add_constraint (dim_vector (-1, -1));
	zdata.add_constraint (dim_vector (-1, -1));
	alphadata.add_constraint ("double");
	alphadata.add_constraint ("uint8");
	alphadata.add_constraint (dim_vector (-1, -1));
	vertexnormals.add_constraint (dim_vector (-1, -1, 3));
	cdata.add_constraint ("double");
	cdata.add_constraint ("uint8");
	cdata.add_constraint (dim_vector (-1, -1));
	cdata.add_constraint (dim_vector (-1, -1, 3));
      }

  private:
    void update_normals (void);

    void update_xdata (void)
      {
	update_normals ();
	set_xlim (xdata.get_limits ());
      }
 
    void update_ydata (void)
      {
	update_normals ();
	set_ylim (ydata.get_limits ());
      }

    void update_zdata (void)
      {
	update_normals ();
	set_zlim (zdata.get_limits ());
      }

    void update_cdata (void)
      {
	if (cdatamapping_is ("scaled"))
	  set_clim (cdata.get_limits ());
	else
	  clim = cdata.get_limits ();
      }

    void update_alphadata (void)
      {
	if (alphadatamapping_is ("scaled"))
	  set_alim (alphadata.get_limits ());
	else
	  alim = alphadata.get_limits ();
      }

    void update_normalmode (void)
      { update_normals (); }

    void update_vertexnormals (void)
      { set_normalmode ("manual"); }
  };

private:
  properties xproperties;

public:
  surface (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), xproperties (mh, p)
  {
    xproperties.override_defaults (*this);
  }

  ~surface (void) { xproperties.delete_children (); }

  base_properties& get_properties (void) { return xproperties; }

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

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

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

class OCTINTERP_API hggroup : public base_graphics_object
{
public:
  class OCTINTERP_API properties : public base_properties
  {
  public:
    void remove_child (const graphics_handle& h)
      {
	base_properties::remove_child (h);
	update_limits ();
      }

    void adopt (const graphics_handle& h)
      {
	base_properties::adopt (h);
	update_limits ();
      }

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

    BEGIN_PROPERTIES(hggroup)
      // hidden properties for limit computation
      row_vector_property xlim hr , Matrix()
      row_vector_property ylim hr , Matrix()
      row_vector_property zlim hr , Matrix()
      row_vector_property clim hr , Matrix()
      row_vector_property alim hr , Matrix()
      bool_property xliminclude h , "on"
      bool_property yliminclude h , "on"
      bool_property zliminclude h , "on"
      bool_property climinclude h , "on"
      bool_property aliminclude h , "on"
    END_PROPERTIES

  private:
    void update_limits (void)
      {
	update_axis_limits ("xlim");
	update_axis_limits ("ylim");
	update_axis_limits ("zlim");
	update_axis_limits ("clim");
	update_axis_limits ("alim");
      }

  protected:
    void init (void)
      { }
  };

private:
  properties xproperties;

public:
  hggroup (const graphics_handle& mh, const graphics_handle& p)
    : base_graphics_object (), xproperties (mh, p)
  {
    xproperties.override_defaults (*this);
  }

  ~hggroup (void) { xproperties.delete_children (); }

  base_properties& get_properties (void) { return xproperties; }

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

  bool valid_object (void) const { return true; }
  
  void update_axis_limits (const std::string& axis_type);
};

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

octave_value
get_property_from_handle (double handle, const std::string &property,
			  const std::string &func);
bool
set_property_in_handle (double handle, const std::string &property,
			const octave_value &arg, const std::string &func);

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

class OCTINTERP_API gh_manager
{
protected:

  gh_manager (void);

public:

  typedef void (*event_fcn) (void*);

  static bool instance_ok (void)
  {
    bool retval = true;

    if (! instance)
      instance = new gh_manager ();

    if (! instance)
      {
	::error ("unable to create gh_manager!");

	retval = false;
      }

    return retval;
  }

  static void free (const graphics_handle& h)
  {
    if (instance_ok ())
      instance->do_free (h);
  }

  static graphics_handle lookup (double val)
  {
    return instance_ok () ? instance->do_lookup (val) : graphics_handle ();
  }

  static graphics_object get_object (const graphics_handle& h)
  {
    return instance_ok () ? instance->do_get_object (h) : graphics_object ();
  }

  static graphics_handle
  make_graphics_handle (const std::string& go_name,
			const graphics_handle& parent, bool do_createfcn = true)
  {
    return instance_ok ()
      ? instance->do_make_graphics_handle (go_name, parent, do_createfcn)
      : graphics_handle ();
  }

  static graphics_handle make_figure_handle (double val)
  {
    return instance_ok ()
      ? instance->do_make_figure_handle (val) : graphics_handle ();
  }

  static void push_figure (const graphics_handle& h)
  {
    if (instance_ok ())
      instance->do_push_figure (h);
  }

  static void pop_figure (const graphics_handle& h)
  {
    if (instance_ok ())
      instance->do_pop_figure (h);
  }

  static graphics_handle current_figure (void)
  {
    return instance_ok ()
      ? instance->do_current_figure () : graphics_handle ();
  }

  static Matrix handle_list (void)
  {
    return instance_ok () ? instance->do_handle_list () : Matrix ();
  }

  static void lock (void)
  {
    if (instance_ok ())
      instance->do_lock ();
  }

  static void unlock (void)
  {
    if (instance_ok ())
      instance->do_unlock ();
  }

  static Matrix figure_handle_list (void)
  {
    return instance_ok () ? instance->do_figure_handle_list () : Matrix ();
  }

  static void execute_callback (const graphics_handle& h,
				const std::string& name,
				const octave_value& data = Matrix ())
  {
    graphics_object go = get_object (h);

    if (go.valid_object ())
      {
	octave_value cb = go.get (name);

	if (! error_state)
	  execute_callback (h, cb, data);
      }
  }

  static void execute_callback (const graphics_handle& h,
				const octave_value& cb,
				const octave_value& data = Matrix ())
  {
    if (instance_ok ())
      instance->do_execute_callback (h, cb, data);
  }

  static void post_callback (const graphics_handle& h,
			     const std::string& name,
			     const octave_value& data = Matrix ())
  {
    if (instance_ok ())
      instance->do_post_callback (h, name, data);
  }

  static void post_function (event_fcn fcn, void* data = 0)
  {
    if (instance_ok ())
      instance->do_post_function (fcn, data);
  }

  static void post_set (const graphics_handle& h,
			const std::string& name,
			const octave_value& value)
  {
    if (instance_ok ())
      instance->do_post_set (h, name, value);
  }

  static int process_events (void)
  {
    return (instance_ok () ?  instance->do_process_events () : 0);
  }

  static int flush_events (void)
  {
    return (instance_ok () ?  instance->do_process_events (true) : 0);
  }

public:
  class autolock
  {
  public:
    autolock (void) { lock (); }

    ~autolock (void) { unlock (); }

  private:

    // No copying!
    autolock (const autolock&);
    autolock& operator = (const autolock&);
  };

public:
  class event_data
    {
      public:
	event_data (void) : rep (0) { }

	event_data (const event_data& d)
	  {
	    rep = d.rep;
	    if (rep)
	      rep->refcount++;
	  }

	virtual ~event_data (void)
	  {
	    if (rep && --rep->refcount == 0)
	      {
		delete rep;
		rep = 0;
	      }
	  }

	event_data& operator = (const event_data& d)
	  {
	    if (d.rep != rep)
	      {
		if (rep && --rep->refcount == 0)
		  delete rep;

		rep = d.rep;
		if (rep)
		  rep->refcount++;
	      }

	    return *this;
	  }

	virtual void execute (void)
	  { if (rep) rep->execute (); }

	bool ok (void) const { return (rep != 0); }
	
	static event_data
	    create_callback_event (const graphics_handle& h,
				   const std::string& name,
				   const octave_value& data = Matrix ());

	static event_data
	    create_function_event (event_fcn fcn, void *data = 0);

	static event_data
	    create_set_event (const graphics_handle& h,
			      const std::string& name,
			      const octave_value& value);

      protected:
	explicit event_data (int /* dummy */)
	    : refcount (0) { }

      private:
	union
	  {
	    event_data *rep;
	    int refcount;
	  };
    };

private:

  static gh_manager *instance;

  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;

  // A map of handles to graphics objects.
  std::map<graphics_handle, graphics_object> handle_map;

  // The available graphics handles.
  std::set<graphics_handle> handle_free_list;

  // The next handle available if handle_free_list is empty.
  double next_handle;

  // The allocated figure handles.  Top of the stack is most recently
  // created.
  std::list<graphics_handle> figure_list;

  // The lock for accessing the graphics sytsem
  octave_mutex graphics_lock;

  // The list of event queued by backends
  std::list<event_data> event_queue;

  // The stack of callback objects
  std::list<graphics_object> callback_objects;

  graphics_handle get_handle (const std::string& go_name);

  void do_free (const graphics_handle& h);

  graphics_handle do_lookup (double val)
  {
    iterator p = (xisnan (val) ? handle_map.end () : handle_map.find (val));

    return (p != handle_map.end ()) ? p->first : graphics_handle ();
  }

  graphics_object do_get_object (const graphics_handle& h)
  {
    iterator p = (h.ok () ? handle_map.find (h) : handle_map.end ());

    return (p != handle_map.end ()) ? p->second : graphics_object ();
  }

  graphics_handle do_make_graphics_handle (const std::string& go_name,
					   const graphics_handle& p, bool do_createfcn);

  graphics_handle do_make_figure_handle (double val);

  Matrix do_handle_list (void)
  {
    Matrix retval (1, handle_map.size ());
    octave_idx_type i = 0;
    for (const_iterator p = handle_map.begin (); p != handle_map.end (); p++)
      {
	graphics_handle h = p->first;
	retval(i++) = h.value ();
      }
    return retval;
  }

  Matrix do_figure_handle_list (void)
  {
    Matrix retval (1, figure_list.size ());
    octave_idx_type i = 0;
    for (const_figure_list_iterator p = figure_list.begin ();
	 p != figure_list.end ();
	 p++)
      {
	graphics_handle h = *p;
	retval(i++) = h.value ();
      }
    return retval;
  }

  void do_push_figure (const graphics_handle& h);

  void do_pop_figure (const graphics_handle& h);

  graphics_handle do_current_figure (void) const
  {
    return figure_list.empty () ? graphics_handle () : figure_list.front ();
  }

  void do_lock (void) { graphics_lock.lock (); }
  
  void do_unlock (void) { graphics_lock.unlock (); }

  void do_execute_callback (const graphics_handle& h, const octave_value& cb,
			    const octave_value& data);

  void do_post_callback (const graphics_handle& h, const std::string name,
			 const octave_value& data);

  void do_post_function (event_fcn fcn, void* fcn_data);

  void do_post_set (const graphics_handle& h, const std::string name,
		    const octave_value& value);

  int do_process_events (bool force = false);

  static void restore_gcbo (void*)
  {
    if (instance_ok ())
      instance->do_restore_gcbo ();
  }

  void do_restore_gcbo (void);

  void do_post_event (const event_data& e);
};


// 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);

#endif

/*
;;; Local Variables: ***
;;; mode: C++ ***
;;; End: ***
*/