/*

Copyright (C) 2007, 2008, 2009 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 "lo-ieee.h"

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

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

class 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 = std::string::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 == std::string::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) : id (-1), count (1) { }

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

  base_property (const base_property& p)
    : id (-1), 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; }

  int get_id (void) const { return id; }

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

  // Sets property value, notifies backend.
  // If do_run is true, runs associated listeners.
  bool set (const octave_value& v, bool do_run = true);
  
  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);
    }

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

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

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

	      l.resize (l.length () - 1);
	    }
	}
      else
	l.resize (0);

    }

  OCTINTERP_API void run_listeners (listener_mode mode = POSTSET);

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

protected:
  virtual bool do_set (const octave_value&)
    {
      error ("set: invalid property \"%s\"", name.c_str ());
      return false;
    }

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 id;
  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:
  bool do_set (const octave_value& val)
    {
      if (val.is_string ())
	{
	  std::string new_str = val.string_value ();

	  if (new_str != str)
	    {
	      str = new_str;
	      return true;
	    }
	}
      else
        error ("set: invalid string property value for \"%s\"",
               get_name ().c_str ());
      return false;
    }

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:
  bool do_set (const octave_value& newval) 
  {
    if (newval.is_string ())
      {
        std::string s = newval.string_value ();
        if (vals.validate (s))
	  {
	    if (s != current_val)
	      {
		current_val = s;
		return true;
	      }
	  }
        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 ());
    return false;
  }

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

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

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

  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 bool 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:
  bool do_set (const octave_value& v)
    {
      if (v.is_scalar_type () && v.is_real_type ())
	{
	  double new_val = v.double_value ();

	  if (new_val != current_val)
	    {
	      current_val = new_val;
	      return true;
	    }
	}
      else
        error ("set: invalid value for double property \"%s\"",
               get_name ().c_str ());
      return false;
    }

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 bool 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:
  bool do_set (const octave_value& v)
    {
      if (validate (v))
	{
	  // FIXME -- should we check for actual data change?
	  if (! is_equal (v))
	    {
	      data = v;

	      get_data_limits ();

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

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

  OCTINTERP_API bool is_equal (const octave_value& v) const;

  OCTINTERP_API void get_data_limits (void);

protected:
  octave_value 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:
  bool do_set (const octave_value& v)
  {
    bool retval = 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);
	  }

	return retval;
      }

    return false;
  }

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

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

class 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 bool 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:
  bool do_set (const octave_value& v)
    {
      data = v;
      return true;
    }

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:
  bool do_set (const octave_value& v)
    {
      if (validate (v))
	{
	  callback = v;
	  return true;
	}
      else
        error ("invalid value for callback property \"%s\"",
               get_name ().c_str ());
      return false;
    }

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

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

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

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

  bool set (const octave_value& val)
    { return 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 delete_listener (const octave_value& v = octave_value (), 
			listener_mode mode = POSTSET)
  { rep->delete_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 graphics_object;

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 redraw_figure (const graphics_object&) const
    { gripe_invalid ("redraw_figure"); }

  virtual void print_figure (const graphics_object&, 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);
    }

  // Called when graphics object using this backend changes it's property.
  virtual void property_changed (const graphics_object&, int)
    { gripe_invalid ("property_changed"); }

  void property_changed (const graphics_handle&, int);
  
  // Called when new object using this backend is created.
  virtual void object_created (const graphics_object&)
    { gripe_invalid ("object_created"); }

  void object_created (const graphics_handle&);

  // Called when object using this backend is destroyed.
  virtual void object_destroyed (const graphics_object&)
    { gripe_invalid ("object_destroyed"); }

  void object_destroyed (const graphics_handle&);

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 redraw_figure (const graphics_object& go) const
    { rep->redraw_figure (go); }
  
  void print_figure (const graphics_object& go, const std::string& term,
		     const std::string& file, bool mono,
		     const std::string& debug_file = "") const
    { rep->print_figure (go, 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 (); }

  // Notifies backend that object't property has changed.
  void property_changed (const graphics_object& go, int id)
    { rep->property_changed (go, id); }
  
  void property_changed (const graphics_handle& h, int id)
    { rep->property_changed (h, id); }

  // Notifies backend that new object was created.
  void object_created (const graphics_object& go)
    { rep->object_created (go); }
  
  void object_created (const graphics_handle& h)
    { rep->object_created (h); }
  
  // Notifies backend that object was destroyed.
  // This is called only for explicitly deleted object. Children are
  // deleted implicitly and backend isn't notified.
  void object_destroyed (const graphics_object& go)
    { rep->object_destroyed (go); }
  
  void object_destroyed (const graphics_handle& h)
    { rep->object_destroyed (h); }
  
  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 ());

  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&)
  {
    panic_impossible ();
  }

  void set (const caseless_str& pname, const std::string& cname,
	    const octave_value& val);

  virtual octave_value get (const caseless_str& pname) const;

  virtual octave_value get (bool all = false) const;

  virtual property get_property (const caseless_str& pname);

  bool has_property (const caseless_str& pname);

  bool is_modified (void) const { return is___modified__ (); }
 
  virtual void remove_child (const graphics_handle& h);

  virtual void adopt (const graphics_handle& h)
  {
    octave_idx_type n = children.numel ();
    children.resize (n+1, 1);
    for (octave_idx_type i = n; i > 0; i--)
      children(i) = children(i-1);
    children(0) = 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);

  virtual void delete_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);

  Matrix get_all_children (void) const { return children; }

  void set_children (const octave_value& val);

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

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

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

  static property_list::pval_map_type factory_defaults (void);

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

  virtual octave_value get_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; }

  bool is_handle_visible (void) const
  {
    return ! handlevisibility.is ("off");
  }

  static std::map<std::string, std::set<std::string> > all_dynamic_properties;
 
  static bool has_dynamic_property (const std::string& pname,
				    const std::string& cname);

protected:
  void set_dynamic (const caseless_str& pname, const std::string& cname,
		    const octave_value& val);

  octave_value get_dynamic (const caseless_str& pname) const;

  octave_value get_dynamic (bool all = false) const;

  property get_property_dynamic (const caseless_str& pname);

  BEGIN_BASE_PROPERTIES
    // properties common to all objects
    bool_property beingdeleted , "off"
    radio_property busyaction , "{queue}|cancel"
    callback_property buttondownfcn , Matrix ()
    // FIXME -- use a property class for children.
    Matrix children Gfs , Matrix ()
    bool_property clipping , "on"
    callback_property createfcn , Matrix ()
    callback_property deletefcn , Matrix ()
    radio_property handlevisibility , "{on}|callback|off"
    bool_property hittest , "on"
    bool_property interruptible , "on"
    handle_property parent fs , p
    bool_property selected , "off"
    bool_property selectionhighlight , "on"
    string_property tag s , ""
    string_property type frs , ty
    any_property userdata , Matrix ()
    bool_property visible , "on"
    // additional (octave-specific) properties
    bool_property __modified__ s , "on"
    graphics_handle __myhandle__ fhrs , mh
    // FIXME -- should this really be here?
    handle_property uicontextmenu , graphics_handle ()
  END_PROPERTIES

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

          return a1 < b1;
        }
    };

  std::map<caseless_str, property, cmp_caseless_str> 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);
    }

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

  virtual void remove_all_listeners (void);

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

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

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

  bool is_handle_visible (void) const
  { return get_properties ().is_handle_visible (); }
  
  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); }

  void delete_property_listener (const std::string& nm, const octave_value& v,
				 listener_mode mode = POSTSET)
    { rep->delete_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:
    void remove_child (const graphics_handle& h);
    
    // See the genprops.awk script for an explanation of the
    // properties declarations.

    BEGIN_PROPERTIES (root_figure, root)
      handle_property currentfigure S , graphics_handle ()
      handle_property callbackobject Sr , graphics_handle ()
      double_property screendepth r , default_screendepth ()
      array_property screensize r , default_screensize ()
      double_property screenpixelsperinch r , default_screenpixelsperinch ()
      radio_property units U , "inches|centimeters|normalized|points|{pixels}"
      bool_property showhiddenhandles , "off"
    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 remove_child (const graphics_handle& h);

    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) 
    { 
      if (backend)
	backend.object_destroyed (__myhandle__);
      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);

    std::string get_title (void) const;

    // 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 , default_figure_paperposition ()
      radio_property paperpositionmode , "auto|{manual}"
      array_property papersize , default_figure_papersize ()
      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|<custom>"
      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 (); 
  }

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

    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;

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

    void delete_text_child (handle_property& h);

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

    // properties which are not in matlab: interpreter

    BEGIN_PROPERTIES (axes)
      array_property position u , default_axes_position ()
      bool_property box , "on"
      bool_property key , "off"
      bool_property keybox , "off"
      bool_property keyreverse , "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"
      handle_property xlabel SOf , gh_manager::make_graphics_handle ("text", __myhandle__, false)
      handle_property ylabel SOf , gh_manager::make_graphics_handle ("text", __myhandle__, false)
      handle_property zlabel SOf , gh_manager::make_graphics_handle ("text", __myhandle__, false)
      handle_property title SOf , gh_manager::make_graphics_handle ("text", __myhandle__, false)
      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 , default_axes_tick ()
      row_vector_property ytick m , default_axes_tick ()
      row_vector_property ztick m , default_axes_tick ()
      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"
      radio_property interpreter , "tex|{none}|latex"
      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"
      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 , OCTAVE_DEFAULT_FONTNAME
      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 , default_axes_ticklength ()
      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);

  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)
  {
    remove_all_listeners ();
    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 ()
      string_property xdatasource , ""
      string_property ydatasource , ""
      string_property zdatasource , ""
      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 , OCTAVE_DEFAULT_FONTNAME
      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}|pixels"
      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 ("logical");
	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"
      string_property xdatasource , ""
      string_property ydatasource , ""
      string_property zdatasource , ""
      string_property cdatasource , ""
      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 graphics_event;

class
base_graphics_event
{
public:
  friend class graphics_event;

  base_graphics_event (void) : count (1) { }

  virtual ~base_graphics_event (void) { }

  virtual void execute (void) = 0;

private:
  int count;
};

class
graphics_event
{
public:
  typedef void (*event_fcn) (void*);

  graphics_event (void) : rep (0) { }

  graphics_event (const graphics_event& e)
    {
      rep = e.rep;
      rep->count++;
    }

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

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

	  rep = e.rep;
	  if (rep)
	    rep->count++;
	}

      return *this;
    }

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

  bool ok (void) const
    { return (rep != 0); }

  static graphics_event
      create_callback_event (const graphics_handle& h,
			     const std::string& name,
			     const octave_value& data = Matrix ());

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

  static graphics_event
      create_set_event (const graphics_handle& h,
			const std::string& name,
			const octave_value& value);
private:
  base_graphics_event *rep;
};

class OCTINTERP_API gh_manager
{
protected:

  gh_manager (void);

public:

  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 (graphics_event::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);
  }

  static bool is_handle_visible (const graphics_handle& h)
  {
    bool retval = false;

    graphics_object go = get_object (h);

    if (go.valid_object ())
      retval = go.is_handle_visible ();

    return retval;
  }

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

    ~autolock (void) { unlock (); }

  private:

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

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<graphics_event> 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 (graphics_event::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 graphics_event& 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: ***
*/
