src/graphics.cc
author John W. Eaton <jwe@octave.org>
Thu Jul 16 11:56:44 2009 -0400 (2009-07-16)
changeset 9384 44300eb51817
parent 9318 282968d49949
child 9416 7ed8182b783d
permissions -rw-r--r--
graphics.cc (get_array_limits): require min_pos value to be greater than zero
     1 /*
     2 
     3 Copyright (C) 2007, 2008, 2009 John W. Eaton
     4 
     5 This file is part of Octave.
     6 
     7 Octave is free software; you can redistribute it and/or modify it
     8 under the terms of the GNU General Public License as published by the
     9 Free Software Foundation; either version 3 of the License, or (at your
    10 option) any later version.
    11 
    12 Octave is distributed in the hope that it will be useful, but WITHOUT
    13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    15 for more details.
    16 
    17 You should have received a copy of the GNU General Public License
    18 along with Octave; see the file COPYING.  If not, see
    19 <http://www.gnu.org/licenses/>.
    20 
    21 */
    22 
    23 #ifdef HAVE_CONFIG_H
    24 #include <config.h>
    25 #endif
    26 
    27 #include <cctype>
    28 #include <cfloat>
    29 #include <cstdlib>
    30 
    31 #include <algorithm>
    32 #include <list>
    33 #include <map>
    34 #include <set>
    35 #include <string>
    36 #include <sstream>
    37 
    38 #include "file-ops.h"
    39 #include "file-stat.h"
    40 
    41 #include "cmd-edit.h"
    42 #include "defun.h"
    43 #include "display.h"
    44 #include "error.h"
    45 #include "graphics.h"
    46 #include "input.h"
    47 #include "ov.h"
    48 #include "oct-obj.h"
    49 #include "oct-map.h"
    50 #include "ov-fcn-handle.h"
    51 #include "parse.h"
    52 #include "toplev.h"
    53 #include "unwind-prot.h"
    54 
    55 // forward declaration
    56 static octave_value xget (const graphics_handle& h, const caseless_str& name);
    57 
    58 static void
    59 gripe_set_invalid (const std::string& pname)
    60 {
    61   error ("set: invalid value for %s property", pname.c_str ());
    62 }
    63 
    64 static Matrix
    65 jet_colormap (void)
    66 {
    67   Matrix cmap (64, 3, 0.0);
    68 
    69   for (octave_idx_type i = 0; i < 64; i++)
    70     {
    71       // This is the jet colormap.  It would be nice to be able
    72       // to feval the jet function but since there is a static
    73       // property object that includes a colormap_property
    74       // object, we need to initialize this before main is even
    75       // called, so calling an interpreted function is not
    76       // possible.
    77 
    78       double x = i / 63.0;
    79 
    80       if (x >= 3.0/8.0 && x < 5.0/8.0)
    81         cmap(i,0) = 4.0 * x - 3.0/2.0;
    82       else if (x >= 5.0/8.0 && x < 7.0/8.0)
    83         cmap(i,0) = 1.0;
    84       else if (x >= 7.0/8.0)
    85         cmap(i,0) = -4.0 * x + 9.0/2.0;
    86 
    87       if (x >= 1.0/8.0 && x < 3.0/8.0)
    88         cmap(i,1) = 4.0 * x - 1.0/2.0;
    89       else if (x >= 3.0/8.0 && x < 5.0/8.0)
    90         cmap(i,1) = 1.0;
    91       else if (x >= 5.0/8.0 && x < 7.0/8.0)
    92         cmap(i,1) = -4.0 * x + 7.0/2.0;
    93 
    94       if (x < 1.0/8.0)
    95         cmap(i,2) = 4.0 * x + 1.0/2.0;
    96       else if (x >= 1.0/8.0 && x < 3.0/8.0)
    97         cmap(i,2) = 1.0;
    98       else if (x >= 3.0/8.0 && x < 5.0/8.0)
    99         cmap(i,2) = -4.0 * x + 5.0/2.0;
   100     }
   101 
   102   return cmap;
   103 }
   104 
   105 static double
   106 default_screendepth (void)
   107 {
   108   return display_info::depth ();
   109 }
   110 
   111 static Matrix
   112 default_screensize (void)
   113 {
   114   Matrix retval (1, 4, 1.0);
   115 
   116   retval(2) = display_info::width ();
   117   retval(3) = display_info::height ();
   118 
   119   return retval;
   120 }
   121 
   122 static double
   123 default_screenpixelsperinch (void)
   124 {
   125   return (display_info::x_dpi () + display_info::y_dpi ()) / 2;
   126 }
   127 
   128 static Matrix
   129 default_colororder (void)
   130 {
   131   Matrix retval (7, 3, 0.0);
   132 
   133   retval(0,2) = 1.0;
   134 
   135   retval(1,1) = 0.5;
   136 
   137   retval(2,0) = 1.0;
   138 
   139   retval(3,1) = 0.75;
   140   retval(3,2) = 0.75;
   141 
   142   retval(4,0) = 0.75;
   143   retval(4,2) = 0.75;
   144 
   145   retval(5,0) = 0.75;
   146   retval(5,1) = 0.75;
   147 
   148   retval(6,0) = 0.25;
   149   retval(6,1) = 0.25;
   150   retval(6,2) = 0.25;
   151 
   152   return retval;
   153 }
   154 
   155 static Matrix
   156 default_lim (void)
   157 {
   158   Matrix m (1, 2, 0);
   159   m(1) = 1;
   160   return m;
   161 }
   162 
   163 static Matrix
   164 default_data (void)
   165 {
   166   Matrix retval (1, 2);
   167 
   168   retval(0) = 0;
   169   retval(1) = 1;
   170 
   171   return retval;
   172 }
   173 
   174 static Matrix
   175 default_axes_position (void)
   176 {
   177   Matrix m (1, 4, 0.0);
   178   m(0) = 0.13;
   179   m(1) = 0.11;
   180   m(2) = 0.775;
   181   m(3) = 0.815;
   182   return m;
   183 }
   184 
   185 static Matrix
   186 default_axes_outerposition (void)
   187 {
   188   Matrix m (1, 4, 0.0);
   189   m(2) = m(3) = 1.0;
   190   return m;
   191 }
   192 
   193 static Matrix
   194 default_axes_tick (void)
   195 {
   196   Matrix m (1, 6, 0.0);
   197   m(0) = 0.0;
   198   m(1) = 0.2;
   199   m(2) = 0.4;
   200   m(3) = 0.6;
   201   m(4) = 0.8;
   202   m(5) = 1.0;
   203   return m;
   204 }
   205 
   206 static Matrix
   207 default_axes_ticklength (void)
   208 {
   209   Matrix m (1, 2, 0.01);
   210   m(1) = 0.025;
   211   return m;
   212 }
   213 
   214 static Matrix
   215 default_figure_position (void)
   216 {
   217   Matrix m (1, 4, 0.0);
   218   m(0) = 300;
   219   m(1) = 200;
   220   m(2) = 560;
   221   m(3) = 420;
   222   return m;
   223 }
   224 
   225 static Matrix
   226 default_figure_papersize (void)
   227 {
   228   Matrix m (1, 2, 0.0);
   229   m(0) = 8.5;
   230   m(1) = 11.0;
   231   return m;
   232 }
   233 
   234 static Matrix
   235 default_figure_paperposition (void)
   236 {
   237   Matrix m (1, 4, 0.0);
   238   m(0) = 0.25;
   239   m(1) = 2.50;
   240   m(2) = 8.00;
   241   m(3) = 6.00;
   242   return m;
   243 }
   244 
   245 static Matrix
   246 convert_position (const Matrix& pos, const caseless_str& from_units,
   247 		  const caseless_str& to_units,
   248 		  const Matrix& parent_dim = Matrix (1, 2, 0.0))
   249 {
   250   Matrix retval (1, 4);
   251   double res = 0;
   252 
   253   if (from_units.compare ("pixels"))
   254     retval = pos;
   255   else if (from_units.compare ("normalized"))
   256     {
   257       retval(0) = pos(0) * parent_dim(0) + 1;
   258       retval(1) = pos(1) * parent_dim(1) + 1;
   259       retval(2) = pos(2) * parent_dim(0);
   260       retval(3) = pos(3) * parent_dim(1);
   261     }
   262   else if (from_units.compare ("characters"))
   263     {
   264       if (res <= 0)
   265 	res = xget (0, "screenpixelsperinch").double_value ();
   266 
   267       double f = 0.0;
   268 
   269       // FIXME -- this assumes the system font is Helvetica 10pt 
   270       //          (for which "x" requires 6x12 pixels at 74.951 pixels/inch)
   271       f = 12.0 * res / 74.951;
   272 
   273       if (f > 0)
   274 	{
   275 	  retval(0) = 0.5 * pos(0) * f;
   276 	  retval(1) = pos(1) * f;
   277 	  retval(2) = 0.5 * pos(2) * f;
   278 	  retval(3) = pos(3) * f;
   279 	}
   280     }
   281   else
   282     {
   283       if (res <= 0)
   284 	res = xget (0, "screenpixelsperinch").double_value ();
   285 
   286       double f = 0.0;
   287 
   288       if (from_units.compare ("points"))
   289 	f = res / 72.0;
   290       else if (from_units.compare ("inches"))
   291 	f = res;
   292       else if (from_units.compare ("centimeters"))
   293 	f = res / 2.54;
   294 
   295       if (f > 0)
   296 	{
   297 	  retval(0) = pos(0) * f + 1;
   298 	  retval(1) = pos(1) * f + 1;
   299 	  retval(2) = pos(2) * f;
   300 	  retval(3) = pos(3) * f;
   301 	}
   302     }
   303 
   304   if (! to_units.compare ("pixels"))
   305     {
   306       if (to_units.compare ("normalized"))
   307 	{
   308 	  retval(0) = (retval(0) - 1) / parent_dim(0);
   309 	  retval(1) = (retval(1) - 1) / parent_dim(1);
   310 	  retval(2) /= parent_dim(0);
   311 	  retval(3) /= parent_dim(1);
   312 	}
   313       else if (to_units.compare ("characters"))
   314 	{
   315 	  if (res <= 0)
   316 	    res = xget (0, "screenpixelsperinch").double_value ();
   317 
   318 	  double f = 0.0;
   319 
   320 	  f = 12.0 * res / 74.951;
   321 
   322 	  if (f > 0)
   323 	    {
   324 	      retval(0) = 2 * retval(0) / f;
   325 	      retval(1) = retval(1) / f;
   326 	      retval(2) = 2 * retval(2) / f;
   327 	      retval(3) = retval(3) / f;
   328 	    }
   329 	}
   330       else
   331 	{
   332 	  if (res <= 0)
   333 	    res = xget (0, "screenpixelsperinch").double_value ();
   334 
   335 	  double f = 0.0;
   336 
   337 	  if (to_units.compare ("points"))
   338 	    f = res / 72.0;
   339 	  else if (to_units.compare ("inches"))
   340 	    f = res;
   341 	  else if (to_units.compare ("centimeters"))
   342 	    f = res / 2.54;
   343 
   344 	  if (f > 0)
   345 	    {
   346 	      retval(0) = (retval(0) - 1) / f;
   347 	      retval(1) = (retval(1) - 1) / f;
   348 	      retval(2) /= f;
   349 	      retval(3) /= f;
   350 	    }
   351 	}
   352     }
   353 
   354   return retval;
   355 }
   356 
   357 static graphics_object
   358 xget_ancestor (const graphics_object& go_arg, const std::string& type)
   359 {
   360   graphics_object go = go_arg;
   361 
   362   do
   363     {
   364       if (go.valid_object ())
   365 	{
   366 	  if (go.isa (type))
   367 	    return go;
   368 	  else
   369 	    go = gh_manager::get_object (go.get_parent ());
   370 	}
   371       else
   372 	return graphics_object ();
   373     }
   374  while (true);
   375 }
   376 
   377 static octave_value
   378 convert_cdata (const base_properties& props, const octave_value& cdata,
   379 	       bool is_scaled, int cdim)
   380 {
   381   dim_vector dv (cdata.dims ());
   382 
   383   if (dv.length () == cdim && dv(cdim-1) == 3)
   384     return cdata;
   385 
   386   Matrix cmap (1, 3, 0.0);
   387   Matrix clim (1, 2, 0.0);
   388 
   389   graphics_object go = gh_manager::get_object (props.get___myhandle__ ());
   390   graphics_object fig = xget_ancestor (go, "figure");
   391 
   392   if (fig.valid_object ())
   393     {
   394       Matrix _cmap = fig.get (caseless_str ("colormap")).matrix_value ();
   395 
   396       if (! error_state)
   397 	cmap = _cmap;
   398     }
   399 
   400   if (is_scaled)
   401     {
   402       graphics_object ax = xget_ancestor (go, "axes");
   403 
   404       if (ax.valid_object ())
   405 	{
   406 	  Matrix _clim = ax.get (caseless_str ("clim")).matrix_value ();
   407 
   408 	  if (! error_state)
   409 	    clim = _clim;
   410 	}
   411     }
   412 
   413   dv.resize (cdim);
   414   dv(cdim-1) = 3;
   415 
   416   NDArray a (dv);
   417 
   418   octave_idx_type lda = a.numel () / static_cast<octave_idx_type> (3);
   419   octave_idx_type nc = cmap.rows ();
   420 
   421   double *av = a.fortran_vec ();
   422   const double *cmapv = cmap.data ();
   423   const double *cv = 0;
   424   const octave_uint8 *icv = 0;
   425 
   426   if (cdata.is_integer_type ())
   427     icv = cdata.uint8_array_value ().data ();
   428   else
   429     cv = cdata.array_value ().data ();
   430 
   431   for (octave_idx_type i = 0; i < lda; i++)
   432     {
   433       double x = (cv ? cv[i] : double (icv[i]));
   434 
   435       if (is_scaled)
   436 	x = xround ((nc - 1) * (x - clim(0)) / (clim(1) - clim(0)));
   437       else
   438 	x = xround (x - 1);
   439 
   440       if (x < 0)
   441 	x = 0;
   442       else if (x >= nc)
   443 	x = (nc - 1);
   444 
   445       octave_idx_type idx = static_cast<octave_idx_type> (x);
   446 
   447       av[i]       = cmapv[idx];
   448       av[i+lda]   = cmapv[idx+nc];
   449       av[i+2*lda] = cmapv[idx+2*nc];
   450     }
   451 
   452   return octave_value (a);
   453 }
   454 
   455 template<class T>
   456 static void
   457 get_array_limits (const Array<T>& m, double& emin, double& emax,
   458 		  double& eminp)
   459 {
   460   const T *data = m.data ();
   461   octave_idx_type n = m.numel ();
   462 
   463   for (octave_idx_type i = 0; i < n; i++)
   464     {
   465       double e = double (data[i]);
   466 
   467       if (! (xisinf (e) || xisnan (e)))
   468 	{
   469 	  if (e < emin)
   470 	    emin = e;
   471 
   472 	  if (e > emax)
   473 	    emax = e;
   474 
   475 	  if (e > 0 && e < eminp)
   476 	    eminp = e;
   477 	}
   478     }
   479 }
   480 
   481 static bool
   482 lookup_object_name (const caseless_str& name, caseless_str& go_name,
   483 		    caseless_str& rest)
   484 {
   485   int len = name.length ();
   486   int offset = 0;
   487   bool result = false;
   488 
   489   if (len >= 4)
   490     {
   491       caseless_str pfx = name.substr (0, 4);
   492 
   493       if (pfx.compare ("axes") || pfx.compare ("line")
   494 	  || pfx.compare ("text"))
   495 	offset = 4;
   496       else if (len >= 5)
   497 	{
   498 	  pfx = name.substr (0, 5);
   499 
   500 	  if (pfx.compare ("image") || pfx.compare ("patch"))
   501 	    offset = 5;
   502 	  else if (len >= 6)
   503 	    {
   504 	      pfx = name.substr (0, 6);
   505 
   506 	      if (pfx.compare ("figure"))
   507 		offset = 6;
   508 	      else if (len >= 7)
   509 		{
   510 		  pfx = name.substr (0, 7);
   511 
   512 		  if (pfx.compare ("surface") || pfx.compare ("hggroup"))
   513 		    offset = 7;
   514 		}
   515 	    }
   516 	}
   517 
   518       if (offset > 0)
   519 	{
   520 	  go_name = pfx;
   521 	  rest = name.substr (offset);
   522 	  result = true;
   523 	}
   524     }
   525 
   526   return result;
   527 }
   528 
   529 static base_graphics_object*
   530 make_graphics_object_from_type (const caseless_str& type,
   531 				const graphics_handle& h = graphics_handle (),
   532 				const graphics_handle& p = graphics_handle ())
   533 {
   534   base_graphics_object *go = 0;
   535 
   536   if (type.compare ("figure"))
   537     go = new figure (h, p);
   538   else if (type.compare ("axes"))
   539     go = new axes (h, p);
   540   else if (type.compare ("line"))
   541     go = new line (h, p);
   542   else if (type.compare ("text"))
   543     go = new text (h, p);
   544   else if (type.compare ("image"))
   545     go = new image (h, p);
   546   else if (type.compare ("patch"))
   547     go = new patch (h, p);
   548   else if (type.compare ("surface"))
   549     go = new surface (h, p);
   550   else if (type.compare ("hggroup"))
   551     go = new hggroup (h, p);
   552 
   553   return go;
   554 }
   555 
   556 // ---------------------------------------------------------------------
   557 
   558 bool
   559 base_property::set (const octave_value& v, bool do_run )
   560 {
   561   if (do_set (v))
   562     {
   563 
   564       // notify backend
   565       if (id >= 0)
   566 	{
   567 	  graphics_object go = gh_manager::get_object (parent);
   568 	  if (go)
   569 	    {
   570 	      graphics_backend backend = go.get_backend();
   571 	      if (backend)
   572 		backend.property_changed (go, id);
   573 	    }
   574 	}
   575 
   576       // run listeners
   577       if (do_run && ! error_state)
   578 	run_listeners (POSTSET);
   579 
   580       return true;
   581     }
   582 
   583   return false;
   584 }
   585 
   586 
   587 void
   588 base_property::run_listeners (listener_mode mode)
   589 {
   590   const octave_value_list& l = listeners[mode];
   591 
   592   for (int i = 0; i < l.length (); i++)
   593     {
   594       gh_manager::execute_callback (parent, l(i), octave_value ());
   595 
   596       if (error_state)
   597 	break;
   598     }
   599 }
   600 
   601 radio_values::radio_values (const std::string& opt_string)
   602 {
   603   size_t beg = 0;
   604   size_t len = opt_string.length ();
   605   bool done = len == 0;
   606 
   607   while (! done)
   608     {
   609       size_t end = opt_string.find ('|', beg);
   610 
   611       if (end == std::string::npos)
   612 	{
   613 	  end = len;
   614 	  done = true;
   615 	}
   616 
   617       std::string t = opt_string.substr (beg, end-beg);
   618 
   619       // Might want more error checking here...
   620       if (t[0] == '{')
   621 	{
   622 	  t = t.substr (1, t.length () - 2);
   623 	  default_val = t;
   624 	}
   625       else if (beg == 0) // ensure default value
   626 	default_val = t;
   627 
   628       possible_vals.insert (t);
   629 
   630       beg = end + 1;
   631     }
   632 }
   633 
   634 bool
   635 color_values::str2rgb (std::string str)
   636 {
   637   double tmp_rgb[3] = {0, 0, 0};
   638   bool retval = true;
   639   unsigned int len = str.length();
   640 
   641   std::transform (str.begin (), str.end (), str.begin (), tolower);
   642 
   643   if (str.compare(0, len, "blue", 0, len) == 0)
   644     tmp_rgb[2] = 1;
   645   else if (str.compare(0, len, "black", 0, len) == 0
   646 	   || str.compare(0, len, "k", 0, len) == 0)
   647     tmp_rgb[0] = tmp_rgb[1] = tmp_rgb[2] = 0;
   648   else if (str.compare(0, len, "red", 0, len) == 0)
   649     tmp_rgb[0] = 1;
   650   else if (str.compare(0, len, "green", 0, len) == 0)
   651     tmp_rgb[1] = 1;
   652   else if (str.compare(0, len, "yellow", 0, len) == 0)
   653     tmp_rgb[0] = tmp_rgb[1] = 1;
   654   else if (str.compare(0, len, "magenta", 0, len) == 0)
   655     tmp_rgb[0] = tmp_rgb[2] = 1;
   656   else if (str.compare(0, len, "cyan", 0, len) == 0)
   657     tmp_rgb[1] = tmp_rgb[2] = 1;
   658   else if (str.compare(0, len, "white", 0, len) == 0
   659 	   || str.compare(0, len, "w", 0, len) == 0)
   660     tmp_rgb[0] = tmp_rgb[1] = tmp_rgb[2] = 1;
   661   else	
   662     retval = false;
   663 
   664   if (retval)
   665     {
   666       for (int i = 0; i < 3; i++)
   667 	xrgb(i) = tmp_rgb[i];
   668     }
   669 
   670   return retval;
   671 }
   672 
   673 bool
   674 color_property::do_set (const octave_value& val)
   675 {
   676   if (val.is_string ())
   677     {
   678       std::string s = val.string_value ();
   679 
   680       if (! s.empty ())
   681 	{
   682 	  if (radio_val.contains (s))
   683 	    {
   684 	      if (current_type != radio_t || current_val != s)
   685 		{
   686 		  current_val = s;
   687 		  current_type = radio_t;
   688 		  return true;
   689 		}
   690 	    }
   691           else
   692 	    {
   693 	      color_values col (s);
   694 	      if (! error_state)
   695 		{
   696 		  if (current_type != color_t || col != color_val)
   697 		    {
   698 		      color_val = col;
   699 		      current_type = color_t;
   700 		      return true;
   701 		    }
   702 		}
   703 	      else
   704 		error ("invalid value for color property \"%s\" (value = %s)",
   705 		       get_name ().c_str (), s.c_str ());
   706 	    }	
   707 	}
   708       else
   709 	error ("invalid value for color property \"%s\"",
   710            get_name ().c_str ());
   711     }
   712   else if (val.is_numeric_type ())
   713     {
   714       Matrix m = val.matrix_value ();
   715 
   716       if (m.numel () == 3)
   717 	{
   718 	  color_values col (m (0), m (1), m(2));
   719 	  if (! error_state)
   720 	    {
   721 	      if (current_type != color_t || col != color_val)
   722 		{
   723 		  color_val = col;
   724 		  current_type = color_t;
   725 		  return true;
   726 		}
   727 	    }
   728 	}
   729       else
   730 	error ("invalid value for color property \"%s\"",
   731            get_name ().c_str ());
   732     }
   733   else 
   734     error ("invalid value for color property \"%s\"",
   735            get_name ().c_str ());
   736 
   737   return false;
   738 }
   739 
   740 bool
   741 double_radio_property::do_set (const octave_value& val)
   742 {
   743   if (val.is_string ())
   744     {
   745       std::string s = val.string_value ();
   746 
   747       if (! s.empty () && radio_val.contains (s))
   748 	{
   749 	  if (current_type != radio_t || s != current_val)
   750 	    {
   751 	      current_val = s;
   752 	      current_type = radio_t;
   753 	      return true;
   754 	    }
   755 	}
   756       else
   757 	error ("invalid value for double_radio property \"%s\"",
   758 	       get_name ().c_str ());
   759     }
   760   else if (val.is_scalar_type () && val.is_real_type ())
   761     {
   762       double new_dval = val.double_value ();
   763 
   764       if (current_type != double_t || new_dval != dval)
   765 	{
   766 	  dval = new_dval;
   767 	  current_type = double_t;
   768 	  return true;
   769 	}
   770     }
   771   else 
   772     error ("invalid value for double_radio property \"%s\"",
   773 	   get_name ().c_str ());
   774 
   775   return false;
   776 }
   777 
   778 bool
   779 array_property::validate (const octave_value& v)
   780 {
   781   bool xok = false;
   782 
   783   // FIXME -- should we always support []?
   784   if (v.is_empty () && v.is_numeric_type ())
   785     return true;
   786 
   787   // check value type
   788   if (type_constraints.size () > 0)
   789     {
   790       for (std::list<std::string>::const_iterator it = type_constraints.begin ();
   791            ! xok && it != type_constraints.end (); ++it)
   792         if ((*it) == v.class_name ())
   793           xok = true;
   794     }
   795   else
   796     xok = v.is_numeric_type ();
   797 
   798   if (xok)
   799     {
   800       dim_vector vdims = v.dims ();
   801       int vlen = vdims.length ();
   802 
   803       xok = false;
   804 
   805       // check value size
   806       if (size_constraints.size () > 0)
   807         for (std::list<dim_vector>::const_iterator it = size_constraints.begin ();
   808              ! xok && it != size_constraints.end (); ++it)
   809           {
   810             dim_vector itdims = (*it);
   811 
   812             if (itdims.length () == vlen)
   813               {
   814                 xok = true;
   815 
   816                 for (int i = 0; xok && i < vlen; i++)
   817                   if (itdims(i) >= 0 && itdims(i) != vdims(i))
   818                     xok = false;
   819               }
   820           }
   821       else
   822         return true;
   823     }
   824 
   825   return xok;
   826 }
   827 
   828 bool
   829 array_property::is_equal (const octave_value& v) const
   830 {
   831   if (data.type_name () == v.type_name ())
   832     {
   833       if (data.dims () == v.dims ())
   834 	{
   835 
   836 #define CHECK_ARRAY_EQUAL(T,F,A) \
   837 	    { \
   838 	      if (data.numel () == 1) \
   839 		return data.F ## scalar_value () == \
   840 		  v.F ## scalar_value (); \
   841 	      else  \
   842 		{ \
   843                   /* Keep copy of array_value to allow sparse/bool arrays */ \
   844 		  /* that are converted, to not be deallocated early */ \
   845 		  const A m1 = data.F ## array_value (); \
   846 		  const T* d1 = m1.data (); \
   847 		  const A m2 = v.F ## array_value (); \
   848 		  const T* d2 = m2.data ();\
   849 		  \
   850 		  bool flag = true; \
   851 		  \
   852 		  for (int i = 0; flag && i < data.numel (); i++) \
   853 		    if (d1[i] != d2[i]) \
   854 		      flag = false; \
   855 		  \
   856 		  return flag; \
   857 		} \
   858 	    }
   859 
   860 	  if (data.is_double_type() || data.is_bool_type ())
   861 	    CHECK_ARRAY_EQUAL (double, , NDArray)
   862 	  else if (data.is_single_type ())
   863 	    CHECK_ARRAY_EQUAL (float, float_, FloatNDArray)
   864 	  else if (data.is_int8_type ())
   865 	    CHECK_ARRAY_EQUAL (octave_int8, int8_, int8NDArray)
   866 	  else if (data.is_int16_type ())
   867 	    CHECK_ARRAY_EQUAL (octave_int16, int16_, int16NDArray)
   868 	  else if (data.is_int32_type ())
   869 	    CHECK_ARRAY_EQUAL (octave_int32, int32_, int32NDArray)
   870 	  else if (data.is_int64_type ())
   871 	    CHECK_ARRAY_EQUAL (octave_int64, int64_, int64NDArray)
   872 	  else if (data.is_uint8_type ())
   873 	    CHECK_ARRAY_EQUAL (octave_uint8, uint8_, uint8NDArray)
   874 	  else if (data.is_uint16_type ())
   875 	    CHECK_ARRAY_EQUAL (octave_uint16, uint16_, uint16NDArray)
   876 	  else if (data.is_uint32_type ())
   877 	    CHECK_ARRAY_EQUAL (octave_uint32, uint32_, uint32NDArray)
   878 	  else if (data.is_uint64_type ())
   879 	    CHECK_ARRAY_EQUAL (octave_uint64, uint64_, uint64NDArray)
   880 	}
   881     }
   882 
   883   return false;
   884 }
   885 
   886 void
   887 array_property::get_data_limits (void)
   888 {
   889   xmin = xminp = octave_Inf;
   890   xmax = -octave_Inf;
   891 
   892   if (! data.is_empty ())
   893     {
   894       if (data.is_integer_type ())
   895 	{
   896 	  if (data.is_int8_type ())
   897 	    get_array_limits (data.int8_array_value (), xmin, xmax, xminp);
   898 	  else if (data.is_uint8_type ())
   899 	    get_array_limits (data.uint8_array_value (), xmin, xmax, xminp);
   900 	  else if (data.is_int16_type ())
   901 	    get_array_limits (data.int16_array_value (), xmin, xmax, xminp);
   902 	  else if (data.is_uint16_type ())
   903 	    get_array_limits (data.uint16_array_value (), xmin, xmax, xminp);
   904 	  else if (data.is_int32_type ())
   905 	    get_array_limits (data.int32_array_value (), xmin, xmax, xminp);
   906 	  else if (data.is_uint32_type ())
   907 	    get_array_limits (data.uint32_array_value (), xmin, xmax, xminp);
   908 	  else if (data.is_int64_type ())
   909 	    get_array_limits (data.int64_array_value (), xmin, xmax, xminp);
   910 	  else if (data.is_uint64_type ())
   911 	    get_array_limits (data.uint64_array_value (), xmin, xmax, xminp);
   912 	}
   913       else
   914 	get_array_limits (data.array_value (), xmin, xmax, xminp);
   915     }
   916 }
   917 
   918 bool
   919 handle_property::do_set (const octave_value& v)
   920 {
   921   double dv = v.double_value ();
   922 
   923   if (! error_state)
   924     {
   925       graphics_handle gh = gh_manager::lookup (dv);
   926 
   927       if (xisnan (gh.value ()) || gh.ok ())
   928 	{
   929 	  if (current_val != gh)
   930 	    {
   931 	      current_val = gh;
   932 	      return true;
   933 	    }
   934 	}
   935       else
   936         error ("set: invalid graphics handle (= %g) for property \"%s\"",
   937 	       dv, get_name ().c_str ());
   938     }
   939   else
   940     error ("set: invalid graphics handle for property \"%s\"",
   941 	   get_name ().c_str ());
   942 
   943   return false;
   944 }
   945 
   946 bool
   947 callback_property::validate (const octave_value& v) const
   948 {
   949   // case 1: function handle
   950   // case 2: cell array with first element being a function handle
   951   // case 3: string corresponding to known function name
   952   // case 4: evaluatable string
   953   // case 5: empty matrix
   954 
   955   if (v.is_function_handle ())
   956     return true;
   957   else if (v.is_string ())
   958     // complete validation will be done at execution-time
   959     return true;
   960   else if (v.is_cell () && v.length () > 0
   961            && (v.rows() == 1 || v.columns () == 1)
   962            && v.cell_value ()(0).is_function_handle ())
   963     return true;
   964   else if (v.is_empty ())
   965     return true;
   966 
   967   return false;
   968 }
   969 
   970 void
   971 callback_property::execute (const octave_value& data) const
   972 {
   973   if (callback.is_defined () && ! callback.is_empty ())
   974     gh_manager::execute_callback (get_parent (), callback, data);
   975 }
   976 
   977 // Used to cache dummy graphics objects from which dynamic
   978 // properties can be cloned.
   979 static std::map<caseless_str, graphics_object> dprop_obj_map;
   980 
   981 property
   982 property::create (const std::string& name, const graphics_handle& h,
   983 		  const caseless_str& type, const octave_value_list& args)
   984 {
   985   property retval;
   986 
   987   if (type.compare ("string"))
   988     {
   989       std::string val = (args.length () > 0 ? args(0).string_value () : "");
   990 
   991       if (! error_state)
   992 	retval = property (new string_property (name, h, val));
   993     }
   994   else if (type.compare ("any"))
   995     {
   996       octave_value val =
   997 	  (args.length () > 0 ? args(0) : octave_value (Matrix ()));
   998 
   999       retval = property (new any_property (name, h, val));
  1000     }
  1001   else if (type.compare ("radio"))
  1002     {
  1003       if (args.length () > 0)
  1004 	{
  1005 	  std::string vals = args(0).string_value ();
  1006 
  1007 	  if (! error_state)
  1008 	    {
  1009 	      retval = property (new radio_property (name, h, vals));
  1010 
  1011 	      if (args.length () > 1)
  1012 		retval.set (args(1));
  1013 	    }
  1014 	  else
  1015 	    error ("addproperty: invalid argument for radio property, expected a string value");
  1016 	}
  1017       else
  1018 	error ("addproperty: missing possible values for radio property");
  1019     }
  1020   else if (type.compare ("double"))
  1021     {
  1022       double d = (args.length () > 0 ? args(0).double_value () : 0);
  1023 
  1024       if (! error_state)
  1025 	retval = property (new double_property (name, h, d));
  1026     }
  1027   else if (type.compare ("handle"))
  1028     {
  1029       double hh = (args.length () > 0 ? args(0).double_value () : octave_NaN);
  1030 
  1031       if (! error_state)
  1032 	{
  1033 	  graphics_handle gh (hh);
  1034 
  1035 	  retval = property (new handle_property (name, h, gh));
  1036 	}
  1037     }
  1038   else if (type.compare ("boolean"))
  1039     {
  1040       retval = property (new bool_property (name, h, false));
  1041 
  1042       if (args.length () > 0)
  1043 	retval.set (args(0));
  1044     }
  1045   else if (type.compare ("data"))
  1046     {
  1047       retval = property (new array_property (name, h, Matrix ()));
  1048 
  1049       if (args.length () > 0)
  1050 	{
  1051 	  retval.set (args(0));
  1052 
  1053 	  // FIXME -- additional argument could define constraints,
  1054 	  // but is this really useful?
  1055 	}
  1056     }
  1057   else if (type.compare ("color"))
  1058     {
  1059       color_values cv (0, 0, 0);
  1060       radio_values rv;
  1061 
  1062       if (args.length () > 1)
  1063 	rv = radio_values (args(1).string_value ());
  1064 
  1065       if (! error_state)
  1066 	{
  1067 	  retval = property (new color_property (name, h, cv, rv));
  1068 
  1069 	  if (! error_state)
  1070 	    {
  1071 	      if (args.length () > 0 && ! args(0).is_empty ())
  1072 		retval.set (args(0));
  1073 	      else
  1074 		retval.set (rv.default_value ());
  1075 	    }
  1076 	}
  1077     }
  1078   else
  1079     {
  1080       caseless_str go_name, go_rest;
  1081 
  1082       if (lookup_object_name (type, go_name, go_rest))
  1083 	{
  1084 	  graphics_object go;
  1085 
  1086 	  std::map<caseless_str, graphics_object>::const_iterator it =
  1087 	      dprop_obj_map.find (go_name);
  1088 
  1089 	  if (it == dprop_obj_map.end ())
  1090 	    {
  1091 	      base_graphics_object *bgo =
  1092 		  make_graphics_object_from_type (go_name);
  1093 
  1094 	      if (bgo)
  1095 		{
  1096 		  go = graphics_object (bgo);
  1097 
  1098 		  dprop_obj_map[go_name] = go;
  1099 		}
  1100 	    }
  1101 	  else
  1102 	    go = it->second;
  1103 
  1104 	  if (go.valid_object ())
  1105 	    {
  1106 	      property prop = go.get_properties ().get_property (go_rest);
  1107 
  1108 	      if (! error_state)
  1109 		{
  1110 		  retval = prop.clone ();
  1111 
  1112 		  retval.set_parent (h);
  1113 		  retval.set_name (name);
  1114 
  1115 		  if (args.length () > 0)
  1116 		    retval.set (args(0));
  1117 		}
  1118 	    }
  1119 	  else
  1120 	    error ("addproperty: invalid object type (= %s)",
  1121 		   go_name.c_str ());
  1122 	}
  1123       else
  1124 	error ("addproperty: unsupported type for dynamic property (= %s)",
  1125 	       type.c_str ());
  1126     }
  1127   
  1128   return retval;
  1129 }
  1130 
  1131 // ---------------------------------------------------------------------
  1132 
  1133 void
  1134 property_list::set (const caseless_str& name, const octave_value& val)
  1135 {
  1136   size_t offset = 0;
  1137 
  1138   size_t len = name.length ();
  1139 
  1140   if (len > 4)
  1141     {
  1142       caseless_str pfx = name.substr (0, 4);
  1143 
  1144       if (pfx.compare ("axes") || pfx.compare ("line")
  1145 	  || pfx.compare ("text"))
  1146 	offset = 4;
  1147       else if (len > 5)
  1148 	{
  1149 	  pfx = name.substr (0, 5);
  1150 
  1151 	  if (pfx.compare ("image") || pfx.compare ("patch"))
  1152 	    offset = 5;
  1153 	  else if (len > 6)
  1154 	    {
  1155 	      pfx = name.substr (0, 6);
  1156 
  1157 	      if (pfx.compare ("figure"))
  1158 		offset = 6;
  1159 	      else if (len > 7)
  1160 		{
  1161 		  pfx = name.substr (0, 7);
  1162 
  1163 		  if (pfx.compare ("surface") || pfx.compare ("hggroup"))
  1164 		    offset = 7;
  1165 		}
  1166 	    }
  1167 	}
  1168 
  1169       if (offset > 0)
  1170 	{
  1171 	  // FIXME -- should we validate property names and values here?
  1172 
  1173 	  std::string pname = name.substr (offset);
  1174 
  1175 	  std::transform (pfx.begin (), pfx.end (), pfx.begin (), tolower);
  1176 	  std::transform (pname.begin (), pname.end (), pname.begin (), tolower);
  1177 
  1178 	  bool has_property = false;
  1179 	  if (pfx == "axes")
  1180 	    has_property = axes::properties::has_property (pname);
  1181 	  else if (pfx == "line")
  1182 	    has_property = line::properties::has_property (pname);
  1183 	  else if (pfx == "text")
  1184 	    has_property = text::properties::has_property (pname);
  1185 	  else if (pfx == "image")
  1186 	    has_property = image::properties::has_property (pname);
  1187 	  else if (pfx == "patch")
  1188 	    has_property = patch::properties::has_property (pname);
  1189 	  else if (pfx == "figure")
  1190 	    has_property = figure::properties::has_property (pname);
  1191 	  else if (pfx == "surface")
  1192 	    has_property = surface::properties::has_property (pname);
  1193 	  else if (pfx == "hggroup")
  1194 	    has_property = hggroup::properties::has_property (pname);
  1195 
  1196 	  if (has_property)
  1197 	    {
  1198 	      bool remove = false;
  1199 	      if (val.is_string ())
  1200 		{
  1201 		  caseless_str tval = val.string_value ();
  1202 
  1203 		  remove = tval.compare ("remove");
  1204 		}
  1205 
  1206 	      pval_map_type& pval_map = plist_map[pfx];
  1207 
  1208 	      if (remove)
  1209 		{
  1210 		  pval_map_iterator p = pval_map.find (pname);
  1211 
  1212 		  if (p != pval_map.end ())
  1213 		    pval_map.erase (p);
  1214 		}
  1215 	      else
  1216 		pval_map[pname] = val;
  1217 	    }
  1218 	  else
  1219 	    error ("invalid %s property `%s'", pfx.c_str (), pname.c_str ());
  1220 	}
  1221     }
  1222 
  1223   if (! error_state && offset == 0)
  1224     error ("invalid default property specification");
  1225 }
  1226 
  1227 octave_value
  1228 property_list::lookup (const caseless_str& name) const
  1229 {
  1230   octave_value retval;
  1231 
  1232   size_t offset = 0;
  1233 
  1234   size_t len = name.length ();
  1235 
  1236   if (len > 4)
  1237     {
  1238       caseless_str pfx = name.substr (0, 4);
  1239 
  1240       if (pfx.compare ("axes") || pfx.compare ("line")
  1241 	  || pfx.compare ("text"))
  1242 	offset = 4;
  1243       else if (len > 5)
  1244 	{
  1245 	  pfx = name.substr (0, 5);
  1246 
  1247 	  if (pfx.compare ("image") || pfx.compare ("patch"))
  1248 	    offset = 5;
  1249 	  else if (len > 6)
  1250 	    {
  1251 	      pfx = name.substr (0, 6);
  1252 
  1253 	      if (pfx.compare ("figure"))
  1254 		offset = 6;
  1255 	      else if (len > 7)
  1256 		{
  1257 		  pfx = name.substr (0, 7);
  1258 
  1259 		  if (pfx.compare ("surface") || pfx.compare ("hggroup"))
  1260 		    offset = 7;
  1261 		}
  1262 	    }
  1263 	}
  1264 
  1265       if (offset > 0)
  1266 	{
  1267 	  std::string pname = name.substr (offset);
  1268 
  1269 	  std::transform (pfx.begin (), pfx.end (), pfx.begin (), tolower);
  1270 	  std::transform (pname.begin (), pname.end (), pname.begin (), tolower);
  1271 
  1272 	  plist_map_const_iterator p = find (pfx);
  1273 
  1274 	  if (p != end ())
  1275 	    {
  1276 	      const pval_map_type& pval_map = p->second;
  1277 
  1278 	      pval_map_const_iterator q = pval_map.find (pname);
  1279 
  1280 	      if (q != pval_map.end ())
  1281 		retval = q->second;
  1282 	    }
  1283 	}
  1284     }
  1285 
  1286   return retval;
  1287 }
  1288 
  1289 Octave_map
  1290 property_list::as_struct (const std::string& prefix_arg) const
  1291 {
  1292   Octave_map m;
  1293 
  1294   for (plist_map_const_iterator p = begin (); p != end (); p++)
  1295     {
  1296       std::string prefix = prefix_arg + p->first;
  1297 
  1298       const pval_map_type pval_map = p->second;
  1299 
  1300       for (pval_map_const_iterator q = pval_map.begin ();
  1301 	   q != pval_map.end ();
  1302 	   q++)
  1303 	m.assign (prefix + q->first, q->second);
  1304     }
  1305 
  1306   return m;    
  1307 }
  1308 
  1309 graphics_handle::graphics_handle (const octave_value& a)
  1310   : val (octave_NaN)
  1311 {
  1312   if (a.is_empty ())
  1313     /* do nothing */;
  1314   else
  1315     {
  1316       double tval = a.double_value ();
  1317 
  1318       if (! error_state)
  1319 	val = tval;
  1320       else
  1321 	error ("invalid graphics handle");
  1322     }
  1323 }
  1324 
  1325 void
  1326 graphics_object::set (const octave_value_list& args)
  1327 {
  1328   int nargin = args.length ();
  1329 
  1330   if (nargin == 0)
  1331     rep->defaults ();
  1332   else if (nargin % 2 == 0)
  1333     {
  1334       for (int i = 0; i < nargin; i += 2)
  1335 	{
  1336 	  caseless_str name = args(i).string_value ();
  1337 
  1338 	  if (! error_state)
  1339 	    {
  1340 	      octave_value val = args(i+1);
  1341 
  1342 	      if (val.is_string ())
  1343 		{
  1344 		  caseless_str tval = val.string_value ();
  1345 
  1346 		  if (tval.compare ("default"))
  1347 		    val = get_default (name);
  1348 		  else if (tval.compare ("factory"))
  1349 		    val = get_factory_default (name);
  1350 		}
  1351 
  1352 	      if (error_state)
  1353 		break;
  1354 
  1355 	      rep->set (name, val);
  1356 	    }
  1357 	  else
  1358 	    error ("set: expecting argument %d to be a property name", i);
  1359 	}
  1360     }
  1361   else
  1362     error ("set: invalid number of arguments");
  1363 }
  1364 
  1365 static double
  1366 make_handle_fraction (void)
  1367 {
  1368   static double maxrand = RAND_MAX + 2.0;
  1369 
  1370   return (rand () + 1.0) / maxrand;
  1371 }
  1372 
  1373 graphics_handle
  1374 gh_manager::get_handle (const std::string& go_name)
  1375 {
  1376   graphics_handle retval;
  1377 
  1378   if (go_name == "figure")
  1379     {
  1380       // Figure handles are positive integers corresponding to the
  1381       // figure number.
  1382 
  1383       // We always want the lowest unused figure number.
  1384 
  1385       retval = 1;
  1386 
  1387       while (handle_map.find (retval) != handle_map.end ())
  1388 	retval++;
  1389     }
  1390   else
  1391     {
  1392       // Other graphics handles are negative integers plus some random
  1393       // fractional part.  To avoid running out of integers, we
  1394       // recycle the integer part but tack on a new random part each
  1395       // time.
  1396 
  1397       free_list_iterator p = handle_free_list.begin ();
  1398 
  1399       if (p != handle_free_list.end ())
  1400 	{
  1401 	  retval = *p;
  1402 	  handle_free_list.erase (p);
  1403 	}
  1404       else
  1405 	{
  1406 	  retval = graphics_handle (next_handle);
  1407 
  1408 	  next_handle = ceil (next_handle) - 1.0 - make_handle_fraction ();
  1409 	}
  1410     }
  1411 
  1412   return retval;
  1413 }
  1414 
  1415 void
  1416 gh_manager::do_free (const graphics_handle& h)
  1417 {
  1418   if (h.ok ())
  1419     {
  1420       if (h.value () != 0)
  1421 	{
  1422 	  iterator p = handle_map.find (h);
  1423 
  1424 	  if (p != handle_map.end ())
  1425 	    {
  1426 	      base_properties& bp = p->second.get_properties ();
  1427 	      
  1428 	      bp.set_beingdeleted (true);
  1429 
  1430 	      bp.delete_children ();
  1431 
  1432 	      octave_value val = bp.get_deletefcn ();
  1433 
  1434 	      bp.execute_deletefcn ();
  1435 
  1436 	      // notify backend
  1437 	      graphics_backend backend = p->second.get_backend ();
  1438 	      if (backend)
  1439                 backend.object_destroyed (p->second);
  1440 
  1441 	      // Note: this will be valid only for first explicitly 
  1442 	      // deleted object.  All its children will then have an
  1443 	      // unknown backend.
  1444 
  1445 	      // Graphics handles for non-figure objects are negative
  1446 	      // integers plus some random fractional part.  To avoid
  1447 	      // running out of integers, we recycle the integer part
  1448 	      // but tack on a new random part each time.
  1449 
  1450 	      handle_map.erase (p);
  1451 
  1452 	      if (h.value () < 0)
  1453 		handle_free_list.insert (ceil (h.value ()) - make_handle_fraction ());
  1454 	    }
  1455 	  else
  1456 	    error ("graphics_handle::free: invalid object %g", h.value ());
  1457 	}
  1458       else
  1459 	error ("graphics_handle::free: can't delete root figure");
  1460     }
  1461 }
  1462 
  1463 gh_manager *gh_manager::instance = 0;
  1464 
  1465 static void
  1466 xset (const graphics_handle& h, const caseless_str& name,
  1467       const octave_value& val)
  1468 {
  1469   graphics_object obj = gh_manager::get_object (h);
  1470   obj.set (name, val);
  1471 }
  1472 
  1473 static void
  1474 xset (const graphics_handle& h, const octave_value_list& args)
  1475 {
  1476   if (args.length () > 0)
  1477     {
  1478       graphics_object obj = gh_manager::get_object (h);
  1479       obj.set (args);
  1480     }
  1481 }
  1482 
  1483 
  1484 static octave_value
  1485 xget (const graphics_handle& h, const caseless_str& name)
  1486 {
  1487   graphics_object obj = gh_manager::get_object (h);
  1488   return obj.get (name);
  1489 }
  1490 
  1491 static graphics_handle
  1492 reparent (const octave_value& ov, const std::string& who,
  1493 	  const std::string& property, const graphics_handle& new_parent,
  1494 	  bool adopt = true)
  1495 {
  1496   graphics_handle h = octave_NaN;
  1497 
  1498   double val = ov.double_value ();
  1499 
  1500   if (! error_state)
  1501     {
  1502       h = gh_manager::lookup (val);
  1503 
  1504       if (h.ok ())
  1505 	{
  1506 	  graphics_object obj = gh_manager::get_object (h);
  1507 	  
  1508 	  graphics_handle parent_h = obj.get_parent ();
  1509 
  1510 	  graphics_object parent_obj = gh_manager::get_object (parent_h);
  1511 
  1512 	  parent_obj.remove_child (h);
  1513 
  1514 	  if (adopt)
  1515 	    obj.set ("parent", new_parent.value ());
  1516 	  else
  1517 	    obj.reparent (new_parent);
  1518 	}
  1519       else
  1520 	error ("%s: invalid graphics handle (= %g) for %s",
  1521 	       who.c_str (), val, property.c_str ());
  1522     }
  1523   else
  1524     error ("%s: expecting %s to be a graphics handle",
  1525 	   who.c_str (), property.c_str ());
  1526 
  1527   return h;
  1528 }
  1529 
  1530 // This function is NOT equivalent to the scripting language function gcf.
  1531 graphics_handle
  1532 gcf (void)
  1533 {
  1534   octave_value val = xget (0, "currentfigure");
  1535 
  1536   return val.is_empty () ? octave_NaN : val.double_value ();
  1537 }
  1538 
  1539 // This function is NOT equivalent to the scripting language function gca.
  1540 graphics_handle
  1541 gca (void)
  1542 {
  1543   octave_value val = xget (gcf (), "currentaxes");
  1544 
  1545   return val.is_empty () ? octave_NaN : val.double_value ();
  1546 }
  1547 
  1548 static void
  1549 adopt (const graphics_handle& p, const graphics_handle& h)
  1550 {
  1551   graphics_object parent_obj = gh_manager::get_object (p);
  1552 
  1553   parent_obj.adopt (h);
  1554 }
  1555 
  1556 static bool
  1557 is_handle (const graphics_handle& h)
  1558 {
  1559   return h.ok ();
  1560 }
  1561 
  1562 static bool
  1563 is_handle (double val)
  1564 {
  1565   graphics_handle h = gh_manager::lookup (val);
  1566 
  1567   return h.ok ();
  1568 }
  1569 
  1570 static octave_value
  1571 is_handle (const octave_value& val)
  1572 {
  1573   octave_value retval = false;
  1574 
  1575   if (val.is_real_scalar () && is_handle (val.double_value ()))
  1576     retval = true;
  1577   else if (val.is_real_matrix ())
  1578     {
  1579       if (val.is_string ())
  1580 	retval = boolNDArray (val.dims (), false);
  1581       else
  1582 	{
  1583 	  const NDArray handles = val.array_value ();
  1584 
  1585 	  if (! error_state)
  1586 	    {
  1587 	      boolNDArray result (handles.dims ());
  1588 
  1589 	      for (octave_idx_type i = 0; i < handles.numel (); i++)
  1590 		result.xelem (i) = is_handle (handles (i));
  1591 
  1592 	      retval = result;
  1593 	    }
  1594 	}
  1595     }
  1596 
  1597   return retval;
  1598 }
  1599 
  1600 static bool
  1601 is_figure (double val)
  1602 {
  1603   graphics_object obj = gh_manager::get_object (val);
  1604 
  1605   return obj && obj.isa ("figure");
  1606 }
  1607 
  1608 static void
  1609 xcreatefcn (const graphics_handle& h)
  1610 {
  1611   graphics_object obj = gh_manager::get_object (h);
  1612   obj.get_properties ().execute_createfcn  ();
  1613 }
  1614 
  1615 // ---------------------------------------------------------------------
  1616 
  1617 void
  1618 base_graphics_backend::property_changed (const graphics_handle& h, int id)
  1619 {
  1620   graphics_object go = gh_manager::get_object (h);
  1621 
  1622   property_changed (go, id);
  1623 }
  1624 
  1625 void
  1626 base_graphics_backend::object_created (const graphics_handle& h)
  1627 {
  1628   graphics_object go = gh_manager::get_object (h);
  1629 
  1630   object_created (go);
  1631 }
  1632 
  1633 void
  1634 base_graphics_backend::object_destroyed (const graphics_handle& h)
  1635 {
  1636   graphics_object go = gh_manager::get_object (h);
  1637 
  1638   object_destroyed (go);
  1639 }
  1640 // ---------------------------------------------------------------------
  1641 
  1642 static Matrix
  1643 maybe_set_children (const Matrix& kids, const octave_value& val)
  1644 {
  1645   const Matrix new_kids = val.matrix_value ();
  1646 
  1647   bool ok = true;
  1648 
  1649   if (! error_state)
  1650     {
  1651       if (kids.numel () == new_kids.numel ())
  1652 	{
  1653 	  Matrix t1 = kids;
  1654 	  Matrix t2 = new_kids;
  1655 
  1656 	  t1.sort ();
  1657 	  t2.sort ();
  1658 
  1659 	  if (t1 != t2)
  1660 	    ok = false;
  1661 	}
  1662       else
  1663 	ok = false;
  1664 
  1665       if (! ok)
  1666 	error ("set: new children must be a permutation of existing children");
  1667     }
  1668   else
  1669     {
  1670       ok = false;
  1671       error ("set: expecting children to be array of graphics handles");
  1672     }
  1673 
  1674   return ok ? new_kids : kids;
  1675 }
  1676 
  1677 void
  1678 base_properties::set_from_list (base_graphics_object& obj,
  1679 				property_list& defaults)
  1680 {
  1681   std::string go_name = graphics_object_name ();
  1682 
  1683   property_list::plist_map_const_iterator p = defaults.find (go_name);
  1684 
  1685   if (p != defaults.end ())
  1686     {
  1687       const property_list::pval_map_type pval_map = p->second;
  1688 
  1689       for (property_list::pval_map_const_iterator q = pval_map.begin ();
  1690 	   q != pval_map.end ();
  1691 	   q++)
  1692 	{
  1693 	  std::string pname = q->first;
  1694 
  1695 	  obj.set (pname, q->second);
  1696 
  1697 	  if (error_state)
  1698 	    {
  1699 	      error ("error setting default property %s", pname.c_str ());
  1700 	      break;
  1701 	    }
  1702 	}
  1703     }
  1704 }
  1705 
  1706 octave_value
  1707 base_properties::get_dynamic (const caseless_str& name) const
  1708 {
  1709   octave_value retval;
  1710 
  1711   std::map<caseless_str, property, cmp_caseless_str>::const_iterator it = all_props.find (name);
  1712 
  1713   if (it != all_props.end ())
  1714     retval = it->second.get ();
  1715   else
  1716     error ("get: unknown property \"%s\"", name.c_str ());
  1717 
  1718   return retval;
  1719 }
  1720 
  1721 octave_value
  1722 base_properties::get_dynamic (bool all) const
  1723 {
  1724   Octave_map m;
  1725 
  1726   for (std::map<caseless_str, property, cmp_caseless_str>::const_iterator it = all_props.begin ();
  1727        it != all_props.end (); ++it)
  1728     if (all || ! it->second.is_hidden ())
  1729       m.assign (it->second.get_name (), it->second.get ());
  1730 
  1731   return m;
  1732 }
  1733 
  1734 std::map<std::string, std::set<std::string> > base_properties::all_dynamic_properties;
  1735 
  1736 bool
  1737 base_properties::has_dynamic_property (const std::string& pname,
  1738 				       const std::string& cname)
  1739 {
  1740   // FIXME -- we need to maintain a static map of class names to sets
  1741   // of dynamic property names, then look up the set for the given
  1742   // cname, then see if the set contains the given pname.  Doing that
  1743   // implies changes to set_dynamic, I think.  Where is set_dynamic
  1744   // ever used?
  1745 
  1746   std::set<std::string>& dynprops = all_dynamic_properties[cname];
  1747 
  1748   return dynprops.find (pname) != dynprops.end ();
  1749 }
  1750 
  1751 void
  1752 base_properties::set_dynamic (const caseless_str& pname,
  1753 			      const std::string& cname,
  1754 			      const octave_value& val)
  1755 {
  1756   std::map<caseless_str, property, cmp_caseless_str>::iterator it = all_props.find (pname);
  1757 
  1758   if (it != all_props.end ())
  1759     it->second.set (val);
  1760   else
  1761     error ("set: unknown property \"%s\"", pname.c_str ());
  1762 
  1763   if (! error_state)
  1764     {
  1765       all_dynamic_properties[cname].insert (pname);
  1766 
  1767       mark_modified ();
  1768     }
  1769 }
  1770 
  1771 property
  1772 base_properties::get_property_dynamic (const caseless_str& name)
  1773 {
  1774   std::map<caseless_str, property, cmp_caseless_str>::const_iterator it = all_props.find (name);
  1775 
  1776   if (it == all_props.end ())
  1777     {
  1778       error ("get_property: unknown property \"%s\"", name.c_str ());
  1779       return property ();
  1780     }
  1781   else
  1782     return it->second;
  1783 }
  1784 
  1785 bool
  1786 base_properties::has_property (const caseless_str& name)
  1787 {
  1788   property p;
  1789 
  1790   unwind_protect::begin_frame("base_properties::has_property");
  1791 
  1792   unwind_protect_bool (discard_error_messages);
  1793   unwind_protect_int (error_state);
  1794 
  1795   discard_error_messages = true;
  1796 
  1797   p = get_property (name);
  1798 
  1799   unwind_protect::run_frame ("base_properties::has_property");
  1800 
  1801   return (p.ok ());
  1802 }
  1803 
  1804 void
  1805 base_properties::remove_child (const graphics_handle& h)
  1806 {
  1807   octave_idx_type k = -1;
  1808   octave_idx_type n = children.numel ();
  1809   for (octave_idx_type i = 0; i < n; i++)
  1810     {
  1811       if (h.value () == children(i))
  1812 	{
  1813 	  k = i;
  1814 	  break;
  1815 	}
  1816     }
  1817 
  1818   if (k >= 0)
  1819     {
  1820       Matrix new_kids (n-1, 1);
  1821       octave_idx_type j = 0;
  1822       for (octave_idx_type i = 0; i < n; i++)
  1823 	{
  1824 	  if (i != k)
  1825 	    new_kids(j++) = children(i);
  1826 	}
  1827       children = new_kids;
  1828       mark_modified ();
  1829     }
  1830 }
  1831 
  1832 void
  1833 base_properties::set_parent (const octave_value& val)
  1834 {
  1835   double tmp = val.double_value ();
  1836 
  1837   graphics_handle new_parent = octave_NaN;
  1838 
  1839   if (! error_state)
  1840     {
  1841       new_parent = gh_manager::lookup (tmp);
  1842 
  1843       if (new_parent.ok ())
  1844 	{
  1845 	  graphics_object parent_obj = gh_manager::get_object (get_parent ());
  1846 
  1847 	  parent_obj.remove_child (__myhandle__);
  1848 
  1849 	  parent = new_parent.as_octave_value ();
  1850 
  1851 	  ::adopt (parent.handle_value (), __myhandle__);
  1852 	}
  1853       else
  1854 	error ("set: invalid graphics handle (= %g) for parent", tmp);
  1855     }
  1856   else
  1857     error ("set: expecting parent to be a graphics handle");
  1858 }
  1859 
  1860 void
  1861 base_properties::set_children (const octave_value& val)
  1862 {
  1863   children = maybe_set_children (children, val);
  1864 }
  1865 
  1866 void
  1867 base_properties::mark_modified (void)
  1868 {
  1869   __modified__ = "on";
  1870   graphics_object parent_obj = gh_manager::get_object (get_parent ());
  1871   if (parent_obj)
  1872     parent_obj.mark_modified ();
  1873 }
  1874 
  1875 void
  1876 base_properties::override_defaults (base_graphics_object& obj)
  1877 {
  1878   graphics_object parent_obj = gh_manager::get_object (get_parent ());
  1879 
  1880   if (parent_obj)
  1881     parent_obj.override_defaults (obj);
  1882 }
  1883 
  1884 void
  1885 base_properties::update_axis_limits (const std::string& axis_type) const
  1886 {
  1887   graphics_object obj = gh_manager::get_object (__myhandle__);
  1888 
  1889   if (obj)
  1890     obj.update_axis_limits (axis_type);
  1891 }
  1892 
  1893 void
  1894 base_properties::delete_children (void)
  1895 {
  1896   octave_idx_type n = children.numel ();
  1897 
  1898   // A callback function might have already deleted the child,
  1899   // so check before deleting
  1900   for (octave_idx_type i = 0; i < n; i++)
  1901     {
  1902       graphics_object go = gh_manager::get_object (children(i));
  1903 
  1904       if (go.valid_object ())
  1905 	gh_manager::free (children(i));
  1906     }
  1907 }
  1908 
  1909 graphics_backend
  1910 base_properties::get_backend (void) const
  1911 {
  1912   graphics_object go = gh_manager::get_object (get_parent ());
  1913 
  1914   if (go)
  1915     return go.get_backend ();
  1916   else
  1917     return graphics_backend ();
  1918 }
  1919 
  1920 void
  1921 base_properties::update_boundingbox (void)
  1922 {
  1923   Matrix kids = get_children ();
  1924 
  1925   for (int i = 0; i < kids.numel (); i++)
  1926     {
  1927       graphics_object go = gh_manager::get_object (kids(i));
  1928 
  1929       if (go.valid_object ())
  1930 	go.get_properties ().update_boundingbox ();
  1931     }
  1932 }
  1933 
  1934 void
  1935 base_properties::add_listener (const caseless_str& nm, const octave_value& v,
  1936 			       listener_mode mode)
  1937 {
  1938   property p = get_property (nm);
  1939 
  1940   if (! error_state && p.ok ())
  1941     p.add_listener (v, mode);
  1942 }
  1943 
  1944 void
  1945 base_properties::delete_listener (const caseless_str& nm, 
  1946 				  const octave_value& v, listener_mode mode)
  1947 {
  1948   property p = get_property (nm);
  1949 
  1950   if (! error_state && p.ok ())
  1951     p.delete_listener (v, mode);
  1952 }
  1953 
  1954 // ---------------------------------------------------------------------
  1955 
  1956 class gnuplot_backend : public base_graphics_backend
  1957 {
  1958 public:
  1959   gnuplot_backend (void)
  1960       : base_graphics_backend ("gnuplot") { }
  1961 
  1962   ~gnuplot_backend (void) { }
  1963 
  1964   bool is_valid (void) const { return true; }
  1965 
  1966   void object_destroyed (const graphics_object& go)
  1967     {
  1968       if (go.isa ("figure"))
  1969 	{
  1970 	  const figure::properties& props =
  1971 	      dynamic_cast<const figure::properties&> (go.get_properties ());
  1972 
  1973 	  send_quit (props.get___plot_stream__ ());
  1974 	}
  1975     }
  1976 
  1977   void property_changed (const graphics_object& go, int id)
  1978     {
  1979       if (go.isa ("figure"))
  1980 	{
  1981 	  graphics_object obj (go);
  1982 
  1983 	  figure::properties& props =
  1984 	      dynamic_cast<figure::properties&> (obj.get_properties ());
  1985 
  1986 	  switch (id)
  1987 	    {
  1988 	    case base_properties::VISIBLE:
  1989 	      if (! props.is_visible ())
  1990 		{
  1991 		  send_quit (props.get___plot_stream__ ());
  1992 		  props.set___plot_stream__ (Matrix ());
  1993 		  props.set___enhanced__ (false);
  1994 		}
  1995 	      break;
  1996 	    }
  1997 	}
  1998     }
  1999 
  2000   void redraw_figure (const graphics_object& go) const
  2001     {
  2002       octave_value_list args;
  2003       args(0) = go.get_handle ().as_octave_value ();
  2004       feval ("gnuplot_drawnow", args);
  2005     }
  2006 
  2007   void print_figure (const graphics_object& go, const std::string& term,
  2008 		     const std::string& file, bool mono,
  2009 		     const std::string& debug_file) const
  2010     {
  2011       octave_value_list args;
  2012       if (! debug_file.empty ())
  2013 	args(4) = debug_file;
  2014       args(3) = mono;
  2015       args(2) = file;
  2016       args(1) = term;
  2017       args(0) = go.get_handle ().as_octave_value ();
  2018       feval ("gnuplot_drawnow", args);
  2019     }
  2020 
  2021   Matrix get_canvas_size (const graphics_handle&) const
  2022     {
  2023       Matrix sz (1, 2, 0.0);
  2024       return sz;
  2025     }
  2026 
  2027   double get_screen_resolution (void) const
  2028     { return 72.0; }
  2029 
  2030   Matrix get_screen_size (void) const
  2031     { return Matrix (1, 2, 0.0); }
  2032 
  2033 private:
  2034   void send_quit (const octave_value& pstream) const
  2035     {
  2036       if (! pstream.is_empty())
  2037 	{
  2038 	  octave_value_list args;
  2039 	  Matrix fids = pstream.matrix_value ();
  2040 
  2041 	  if (! error_state)
  2042 	    {
  2043 	      args(1) = "\nquit;\n";
  2044 	      args(0) = octave_value (fids (0));
  2045 	      feval ("fputs", args);
  2046 	      args.resize (1);
  2047 	      feval ("fflush", args);
  2048 	      feval ("pclose", args);
  2049 	      if (fids.numel () > 1)
  2050 		{
  2051 		  args(0) = octave_value (fids (1));
  2052 		  feval ("pclose", args);
  2053 		}
  2054 	    }
  2055 	}
  2056     }
  2057 };
  2058 
  2059 graphics_backend
  2060 graphics_backend::default_backend (void)
  2061 {
  2062   if (available_backends.size () == 0)
  2063     register_backend (new gnuplot_backend ());
  2064 
  2065   return available_backends["gnuplot"];
  2066 }
  2067 
  2068 std::map<std::string, graphics_backend> graphics_backend::available_backends;
  2069 
  2070 // ---------------------------------------------------------------------
  2071 
  2072 void
  2073 base_graphics_object::update_axis_limits (const std::string& axis_type)
  2074 {
  2075   if (valid_object ())
  2076     {
  2077       graphics_object parent_obj = gh_manager::get_object (get_parent ());
  2078 
  2079       if (parent_obj)
  2080 	parent_obj.update_axis_limits (axis_type);
  2081     }
  2082   else
  2083     error ("base_graphics_object::update_axis_limits: invalid graphics object");
  2084 }
  2085 
  2086 void
  2087 base_graphics_object::remove_all_listeners (void)
  2088 {
  2089   Octave_map m = get (true).map_value ();
  2090 
  2091   for (Octave_map::const_iterator pa = m.begin (); pa != m.end (); pa++)
  2092     {
  2093       if (get_properties().has_property (pa->first))
  2094 	{
  2095 	  property p = get_properties ().get_property (pa->first);
  2096 
  2097 	  if (! error_state && p.ok ())
  2098 	    p.delete_listener ();
  2099 	}
  2100     }
  2101 }
  2102 
  2103 // ---------------------------------------------------------------------
  2104 
  2105 #include "graphics-props.cc"
  2106 
  2107 // ---------------------------------------------------------------------
  2108 
  2109 void
  2110 root_figure::properties::set_currentfigure (const octave_value& v)
  2111 {
  2112   graphics_handle val (v);
  2113 
  2114   if (error_state)
  2115     return;
  2116 
  2117   if (xisnan (val.value ()) || is_handle (val))
  2118     {
  2119       currentfigure = val;
  2120 
  2121       gh_manager::push_figure (val);
  2122     }
  2123   else
  2124     gripe_set_invalid ("currentfigure");
  2125 }
  2126 
  2127 void
  2128 root_figure::properties::set_callbackobject (const octave_value& v)
  2129 {
  2130   graphics_handle val (v);
  2131 
  2132   if (error_state)
  2133     return;
  2134 
  2135   if (xisnan (val.value ()))
  2136     {
  2137       if (! cbo_stack.empty ())
  2138 	{
  2139 	  val = cbo_stack.front ();
  2140 
  2141 	  cbo_stack.pop_front ();
  2142 	}
  2143 
  2144       callbackobject = val;
  2145     }
  2146   else if (is_handle (val))
  2147     {
  2148       if (get_callbackobject ().ok ())
  2149 	cbo_stack.push_front (get_callbackobject ());
  2150 
  2151       callbackobject = val;
  2152     }
  2153   else
  2154     gripe_set_invalid ("callbackobject");
  2155 }
  2156 
  2157 void
  2158 root_figure::properties::update_units (void)
  2159 {
  2160   caseless_str xunits = get_units ();
  2161 
  2162   Matrix ss = default_screensize ();
  2163 
  2164   double dpi = get_screenpixelsperinch ();
  2165 
  2166   if (xunits.compare ("inches"))
  2167     {
  2168       ss(0) = 0;
  2169       ss(1) = 0;
  2170       ss(2) /= dpi;
  2171       ss(3) /= dpi;
  2172     }
  2173   else if (xunits.compare ("centimeters"))
  2174     {
  2175       ss(0) = 0;
  2176       ss(1) = 0;
  2177       ss(2) *= 2.54 / dpi;
  2178       ss(3) *= 2.54 / dpi;
  2179     }
  2180   else if (xunits.compare ("normalized"))
  2181     {
  2182       ss = Matrix (1, 4, 1.0);
  2183     }
  2184   else if (xunits.compare ("points"))
  2185     {
  2186       ss(0) = 0;
  2187       ss(1) = 0;
  2188       ss(2) *= 72 / dpi;
  2189       ss(3) *= 72 / dpi;
  2190     }
  2191 
  2192   set_screensize (ss);
  2193 }
  2194 
  2195 void
  2196 root_figure::properties::remove_child (const graphics_handle& gh)
  2197 {
  2198   gh_manager::pop_figure (gh);
  2199 
  2200   graphics_handle cf = gh_manager::current_figure ();
  2201 
  2202   xset (0, "currentfigure", cf.value ());
  2203   
  2204   base_properties::remove_child (gh);
  2205 }
  2206 
  2207 property_list
  2208 root_figure::factory_properties = root_figure::init_factory_properties ();
  2209 
  2210 // ---------------------------------------------------------------------
  2211 
  2212 void
  2213 figure::properties::set_currentaxes (const octave_value& v)
  2214 {
  2215   graphics_handle val (v);
  2216 
  2217   if (error_state)
  2218     return;
  2219 
  2220   if (xisnan (val.value ()) || is_handle (val))
  2221     currentaxes = val;
  2222   else
  2223     gripe_set_invalid ("currentaxes");
  2224 }
  2225 
  2226 void
  2227 figure::properties::remove_child (const graphics_handle& gh)
  2228 {
  2229   base_properties::remove_child (gh);
  2230 
  2231   if (gh == currentaxes.handle_value ())
  2232     {
  2233       graphics_handle new_currentaxes;
  2234 
  2235       for (octave_idx_type i = 0; i < children.numel (); i++)
  2236 	{
  2237 	  graphics_handle kid = children(i);
  2238 
  2239 	  graphics_object go = gh_manager::get_object (kid);
  2240 
  2241 	  if (go.isa ("axes"))
  2242 	    {
  2243 	      new_currentaxes = kid;
  2244 	      break;
  2245 	    }
  2246 	}
  2247 
  2248       currentaxes = new_currentaxes;
  2249     }
  2250 }
  2251 
  2252 void
  2253 figure::properties::set_visible (const octave_value& val)
  2254 {
  2255   std::string s = val.string_value ();
  2256 
  2257   if (! error_state)
  2258     {
  2259       if (s == "on")
  2260 	xset (0, "currentfigure", __myhandle__.value ());
  2261 
  2262       visible = val;
  2263     }
  2264 }
  2265 
  2266 Matrix
  2267 figure::properties::get_boundingbox (bool) const
  2268 {
  2269   Matrix screen_size = xget (0, "screensize").matrix_value ().extract_n (0, 2, 1, 2);
  2270   Matrix pos;
  2271 
  2272   pos = convert_position (get_position ().matrix_value (), get_units (),
  2273 			  "pixels", screen_size);
  2274 
  2275   pos(0)--;
  2276   pos(1)--;
  2277   pos(1) = screen_size(1) - pos(1) - pos(3);
  2278 
  2279   return pos;
  2280 }
  2281 
  2282 void
  2283 figure::properties::set_boundingbox (const Matrix& bb)
  2284 {
  2285   Matrix screen_size = xget (0, "screensize").matrix_value ().extract_n (0, 2, 1, 2);
  2286   Matrix pos = bb;
  2287 
  2288   pos(1) = screen_size(1) - pos(1) - pos(3);
  2289   pos(1)++;
  2290   pos(0)++;
  2291   pos = convert_position (pos, "pixels", get_units (), screen_size);
  2292 
  2293   set_position (pos);
  2294 }
  2295 
  2296 void
  2297 figure::properties::set_position (const octave_value& v)
  2298 {
  2299   if (! error_state)
  2300     {
  2301       Matrix old_bb, new_bb;
  2302 
  2303       old_bb = get_boundingbox ();
  2304       position = v;
  2305       new_bb = get_boundingbox ();
  2306 
  2307       if (old_bb != new_bb)
  2308 	{
  2309 	  if (old_bb(2) != new_bb(2) || old_bb(3) != new_bb(3))
  2310 	    {
  2311 	      execute_resizefcn ();
  2312 	      update_boundingbox ();
  2313 	    }
  2314 	}
  2315 
  2316       mark_modified ();
  2317     }
  2318 }
  2319 
  2320 std::string
  2321 figure::properties::get_title (void) const
  2322 {
  2323   if (is_numbertitle ())
  2324     {
  2325       std::ostringstream os;
  2326       std::string nm = get_name ();
  2327 
  2328       os << "Figure " << __myhandle__.value ();
  2329       if (! nm.empty ())
  2330 	os << ": " << get_name ();
  2331 
  2332       return os.str ();
  2333     }
  2334   else
  2335     return get_name ();
  2336 }
  2337 
  2338 octave_value
  2339 figure::get_default (const caseless_str& name) const
  2340 {
  2341   octave_value retval = default_properties.lookup (name);
  2342 
  2343   if (retval.is_undefined ())
  2344     {
  2345       graphics_handle parent = get_parent ();
  2346       graphics_object parent_obj = gh_manager::get_object (parent);
  2347 
  2348       retval = parent_obj.get_default (name);
  2349     }
  2350 
  2351   return retval;
  2352 }
  2353 
  2354 // ---------------------------------------------------------------------
  2355 
  2356 void
  2357 axes::properties::init (void)
  2358 {
  2359   position.add_constraint (dim_vector (1, 4));
  2360   position.add_constraint (dim_vector (0, 0));
  2361   outerposition.add_constraint (dim_vector (1, 4));
  2362   colororder.add_constraint (dim_vector (-1, 3));
  2363   dataaspectratio.add_constraint (dim_vector (1, 3));
  2364   plotboxaspectratio.add_constraint (dim_vector (1, 3));
  2365   xlim.add_constraint (2);
  2366   ylim.add_constraint (2);
  2367   zlim.add_constraint (2);
  2368   clim.add_constraint (2);
  2369   alim.add_constraint (2);
  2370   xtick.add_constraint (dim_vector (1, -1));
  2371   ytick.add_constraint (dim_vector (1, -1));
  2372   ztick.add_constraint (dim_vector (1, -1));
  2373   Matrix vw (1, 2, 0);
  2374   vw(1) = 90;
  2375   view = vw;
  2376   view.add_constraint (dim_vector (1, 2));
  2377   cameraposition.add_constraint (dim_vector (1, 3));
  2378   Matrix upv (1, 3, 0.0);
  2379   upv(2) = 1.0;
  2380   cameraupvector = upv;
  2381   cameraupvector.add_constraint (dim_vector (1, 3));
  2382   currentpoint.add_constraint (dim_vector (2, 3));
  2383   ticklength.add_constraint (dim_vector (1, 2));
  2384   tightinset.add_constraint (dim_vector (1, 4));
  2385 
  2386   x_zlim.resize (1, 2);
  2387 
  2388   sx = "linear";
  2389   sy = "linear";
  2390   sz = "linear";
  2391 
  2392   xset (xlabel.handle_value (), "handlevisibility", "off");
  2393   xset (ylabel.handle_value (), "handlevisibility", "off");
  2394   xset (zlabel.handle_value (), "handlevisibility", "off");
  2395   xset (title.handle_value (), "handlevisibility", "off");
  2396 
  2397   xset (xlabel.handle_value (), "horizontalalignment", "center");
  2398   xset (ylabel.handle_value (), "horizontalalignment", "center");
  2399   xset (zlabel.handle_value (), "horizontalalignment", "right");
  2400   xset (title.handle_value (), "horizontalalignment", "center");
  2401 
  2402   xset (xlabel.handle_value (), "verticalalignment", "cap");
  2403   xset (ylabel.handle_value (), "verticalalignment", "bottom");
  2404   xset (title.handle_value (), "verticalalignment", "bottom");
  2405 
  2406   xset (ylabel.handle_value (), "rotation", 90.0);
  2407 
  2408   adopt (xlabel.handle_value ());
  2409   adopt (ylabel.handle_value ());
  2410   adopt (zlabel.handle_value ());
  2411   adopt (title.handle_value ());
  2412 }
  2413 
  2414 void 
  2415 axes::properties::sync_positions (void)
  2416 {
  2417 #if 0
  2418   // FIXME -- this should take font metrics into consideration,
  2419   // and also the fact that the colorbox leaves the outerposition
  2420   // alone but alters the position. For now just don't adjust the
  2421   // positions relative to each other.
  2422 
  2423   if (activepositionproperty.is ("outerposition"))
  2424     {
  2425       Matrix outpos = outerposition.get ().matrix_value ();
  2426       Matrix defpos = default_axes_position ();
  2427       Matrix pos(outpos);
  2428       pos(0) = outpos(0) + defpos(0) * outpos(2);
  2429       pos(1) = outpos(1) + defpos(1) * outpos(3);
  2430       pos(2) = outpos(2) * defpos(2);
  2431       pos(3) = outpos(3) * defpos(3);
  2432       position = pos;
  2433     }
  2434   else
  2435     {
  2436       Matrix pos = position.get ().matrix_value ();
  2437       pos(0) -= pos(2)*0.05;
  2438       pos(1) -= pos(3)*0.05;
  2439       pos(2) *= 1.1;
  2440       pos(3) *= 1.1;
  2441       outerposition = pos;
  2442     }
  2443 #endif
  2444 
  2445   update_transform ();
  2446 }
  2447 
  2448 void
  2449 axes::properties::set_text_child (handle_property& hp,
  2450 				  const std::string& who,
  2451 				  const octave_value& v)
  2452 {
  2453   graphics_handle val = ::reparent (v, "set", who, __myhandle__, false);
  2454 
  2455   if (! error_state)
  2456     {
  2457       xset (val, "handlevisibility", "off");
  2458 
  2459       gh_manager::free (hp.handle_value ());
  2460 
  2461       base_properties::remove_child (hp.handle_value ());
  2462 
  2463       hp = val;
  2464 
  2465       adopt (hp.handle_value ());
  2466     }
  2467 }
  2468 
  2469 void
  2470 axes::properties::set_xlabel (const octave_value& v)
  2471 {
  2472   set_text_child (xlabel, "xlabel", v);
  2473 }
  2474 
  2475 void
  2476 axes::properties::set_ylabel (const octave_value& v)
  2477 {
  2478   set_text_child (ylabel, "ylabel", v);
  2479 }
  2480 
  2481 void
  2482 axes::properties::set_zlabel (const octave_value& v)
  2483 {
  2484   set_text_child (zlabel, "zlabel", v);
  2485 }
  2486 
  2487 void
  2488 axes::properties::set_title (const octave_value& v)
  2489 {
  2490   set_text_child (title, "title", v);
  2491 }
  2492 
  2493 void
  2494 axes::properties::set_defaults (base_graphics_object& obj,
  2495 				const std::string& mode)
  2496 {
  2497   box = "on";
  2498   key = "off";
  2499   keybox = "off";
  2500   keyreverse = "off";
  2501   keypos = 1.0;
  2502   colororder = default_colororder ();
  2503   dataaspectratio = Matrix (1, 3, 1.0);
  2504   dataaspectratiomode = "auto";
  2505   layer = "bottom";
  2506 
  2507   Matrix tlim (1, 2, 0.0);
  2508   tlim(1) = 1;
  2509   xlim = tlim;
  2510   ylim = tlim;
  2511   zlim = tlim;
  2512   
  2513   Matrix cl (1, 2, 0);
  2514   cl(1) = 1;
  2515   clim = cl;
  2516   
  2517   xlimmode = "auto";
  2518   ylimmode = "auto";
  2519   zlimmode = "auto";
  2520   climmode = "auto";
  2521 
  2522   xgrid = "off";
  2523   ygrid = "off";
  2524   zgrid = "off";
  2525   xminorgrid = "off";
  2526   yminorgrid = "off";
  2527   zminorgrid = "off";
  2528   xtick = Matrix ();
  2529   ytick = Matrix ();
  2530   ztick = Matrix ();
  2531   xtickmode = "auto";
  2532   ytickmode = "auto";
  2533   ztickmode = "auto";
  2534   xticklabel = "";
  2535   yticklabel = "";
  2536   zticklabel = "";
  2537   xticklabelmode = "auto";
  2538   yticklabelmode = "auto";
  2539   zticklabelmode = "auto";
  2540   color = color_values (1, 1, 1);
  2541   xcolor = color_values ("black");
  2542   ycolor = color_values ("black");
  2543   zcolor = color_values ("black");
  2544   xscale = "linear";
  2545   yscale = "linear";
  2546   zscale = "linear";
  2547   xdir = "normal";
  2548   ydir = "normal";
  2549   zdir = "normal";
  2550   yaxislocation = "left";
  2551   xaxislocation = "bottom";
  2552 
  2553   // Note: camera properties will be set through update_transform
  2554   camerapositionmode = "auto";
  2555   cameratargetmode = "auto";
  2556   cameraupvectormode = "auto";
  2557   cameraviewanglemode = "auto";
  2558   plotboxaspectratio = Matrix (1, 3, 1.0);
  2559   drawmode = "normal";
  2560   gridlinestyle = ":";
  2561   linestyleorder = "-";
  2562   linewidth = 0.5;
  2563   minorgridlinestyle = ":";
  2564   // Note: plotboxaspectratio will be set through update_aspectratiors
  2565   plotboxaspectratiomode = "auto";
  2566   projection = "orthographic";
  2567   tickdir = "in";
  2568   tickdirmode = "auto";
  2569   ticklength = default_axes_ticklength ();
  2570   tightinset = Matrix (1, 4, 0.0);
  2571 
  2572   sx = "linear";
  2573   sy = "linear";
  2574   sz = "linear";
  2575 
  2576   Matrix tview (1, 2, 0.0);
  2577   tview(1) = 90;
  2578   view = tview;
  2579 
  2580   visible = "on";
  2581   nextplot = "replace";
  2582 
  2583   if (mode != "replace")
  2584     {
  2585       fontangle = "normal";
  2586       fontname = OCTAVE_DEFAULT_FONTNAME;
  2587       fontsize = 12;
  2588       fontunits = "points";
  2589       fontweight = "normal";
  2590 
  2591       Matrix touterposition (1, 4, 0.0);
  2592       touterposition(2) = 1;
  2593       touterposition(3) = 1;
  2594       outerposition = touterposition;
  2595 
  2596       position = default_axes_position ();
  2597 
  2598       activepositionproperty = "outerposition";
  2599     }
  2600 
  2601   delete_children ();
  2602 
  2603   children = Matrix ();
  2604 
  2605   xlabel = gh_manager::make_graphics_handle ("text", __myhandle__, false);
  2606   ylabel = gh_manager::make_graphics_handle ("text", __myhandle__, false);
  2607   zlabel = gh_manager::make_graphics_handle ("text", __myhandle__, false);
  2608   title = gh_manager::make_graphics_handle ("text", __myhandle__, false);
  2609 
  2610   xset (xlabel.handle_value (), "handlevisibility", "off");
  2611   xset (ylabel.handle_value (), "handlevisibility", "off");
  2612   xset (zlabel.handle_value (), "handlevisibility", "off");
  2613   xset (title.handle_value (), "handlevisibility", "off");
  2614 
  2615   xset (xlabel.handle_value (), "horizontalalignment", "center");
  2616   xset (ylabel.handle_value (), "horizontalalignment", "center");
  2617   xset (zlabel.handle_value (), "horizontalalignment", "right");
  2618   xset (title.handle_value (), "horizontalalignment", "center");
  2619 
  2620   xset (xlabel.handle_value (), "verticalalignment", "cap");
  2621   xset (ylabel.handle_value (), "verticalalignment", "bottom");
  2622   xset (title.handle_value (), "verticalalignment", "bottom");
  2623 
  2624   xset (ylabel.handle_value (), "rotation", 90.0);
  2625 
  2626   adopt (xlabel.handle_value ());
  2627   adopt (ylabel.handle_value ());
  2628   adopt (zlabel.handle_value ());
  2629   adopt (title.handle_value ());
  2630 
  2631   update_transform ();
  2632 
  2633   override_defaults (obj);
  2634 }
  2635 
  2636 void
  2637 axes::properties::delete_text_child (handle_property& hp)
  2638 {
  2639   graphics_handle h = hp.handle_value ();
  2640 
  2641   if (h.ok ())
  2642     {
  2643       graphics_object go = gh_manager::get_object (h);
  2644 
  2645       if (go.valid_object ())
  2646 	gh_manager::free (h);
  2647 
  2648       base_properties::remove_child (h);
  2649     }
  2650 
  2651   // FIXME -- is it necessary to check whether the axes object is
  2652   // being deleted now?  I think this function is only called when an
  2653   // individual child object is delete and not when the parent axes
  2654   // object is deleted.
  2655 
  2656   if (! is_beingdeleted ())
  2657     {
  2658       hp = gh_manager::make_graphics_handle ("text", __myhandle__, false);
  2659 
  2660       xset (hp.handle_value (), "handlevisibility", "off");
  2661 
  2662       adopt (hp.handle_value ());
  2663     }
  2664 }
  2665 
  2666 void
  2667 axes::properties::remove_child (const graphics_handle& h)
  2668 {
  2669   if (xlabel.handle_value ().ok () && h == xlabel.handle_value ())
  2670     delete_text_child (xlabel);
  2671   else if (ylabel.handle_value ().ok () && h == ylabel.handle_value ())
  2672     delete_text_child (ylabel);
  2673   else if (zlabel.handle_value ().ok () && h == zlabel.handle_value ())
  2674     delete_text_child (zlabel);
  2675   else if (title.handle_value ().ok () && h == title.handle_value ())
  2676     delete_text_child (title);
  2677   else
  2678     base_properties::remove_child (h);
  2679 }
  2680 
  2681 Matrix
  2682 base_properties::get_children (void) const
  2683 {
  2684   Matrix retval = children;
  2685   
  2686   graphics_object go = gh_manager::get_object (0);
  2687 
  2688   root_figure::properties& props =
  2689       dynamic_cast<root_figure::properties&> (go.get_properties ());
  2690 
  2691   if (! props.is_showhiddenhandles ())
  2692     {
  2693       octave_idx_type k = 0;
  2694 
  2695       for (octave_idx_type i = 0; i < children.numel (); i++)
  2696 	{
  2697 	  graphics_handle kid = children (i);
  2698 
  2699 	  if (gh_manager::is_handle_visible (kid))
  2700 	    retval(k++) = children(i);
  2701 	}
  2702 
  2703       retval.resize (k, 1);
  2704     }
  2705 
  2706   return retval;;
  2707 }
  2708 
  2709 inline Matrix
  2710 xform_matrix (void)
  2711 {
  2712   Matrix m (4, 4, 0.0);
  2713   for (int i = 0; i < 4; i++)
  2714     m(i,i) = 1;
  2715   return m;
  2716 }
  2717 
  2718 inline ColumnVector
  2719 xform_vector (void)
  2720 {
  2721   ColumnVector v (4, 0.0);
  2722   v(3) = 1;
  2723   return v;
  2724 }
  2725 
  2726 inline ColumnVector
  2727 xform_vector (double x, double y, double z)
  2728 {
  2729   ColumnVector v (4, 1.0);
  2730   v(0) = x; v(1) = y; v(2) = z;
  2731   return v;
  2732 }
  2733 
  2734 inline ColumnVector
  2735 transform (const Matrix& m, double x, double y, double z)
  2736 {
  2737   return (m * xform_vector (x, y, z));
  2738 }
  2739 
  2740 inline Matrix
  2741 xform_scale (double x, double y, double z)
  2742 {
  2743   Matrix m (4, 4, 0.0);
  2744   m(0,0) = x; m(1,1) = y; m(2,2) = z; m(3,3) = 1;
  2745   return m;
  2746 }
  2747 
  2748 inline Matrix
  2749 xform_translate (double x, double y, double z)
  2750 {
  2751   Matrix m = xform_matrix ();
  2752   m(0,3) = x; m(1,3) = y; m(2,3) = z; m(3,3) = 1;
  2753   return m;
  2754 }
  2755 
  2756 inline void
  2757 scale (Matrix& m, double x, double y, double z)
  2758 {
  2759   m = m * xform_scale (x, y, z);
  2760 }
  2761 
  2762 inline void
  2763 translate (Matrix& m, double x, double y, double z)
  2764 {
  2765   m = m * xform_translate (x, y, z);
  2766 }
  2767 
  2768 inline void
  2769 xform (ColumnVector& v, const Matrix& m)
  2770 {
  2771   v = m*v;
  2772 }
  2773 
  2774 inline void
  2775 scale (ColumnVector& v, double x, double y, double z)
  2776 {
  2777   v(0) *= x;
  2778   v(1) *= y;
  2779   v(2) *= z;
  2780 }
  2781 
  2782 inline void
  2783 translate (ColumnVector& v, double x, double y, double z)
  2784 {
  2785   v(0) += x;
  2786   v(1) += y;
  2787   v(2) += z;
  2788 }
  2789 
  2790 inline void
  2791 normalize (ColumnVector& v)
  2792 {
  2793   double fact = 1.0/sqrt(v(0)*v(0)+v(1)*v(1)+v(2)*v(2));
  2794   scale (v, fact, fact, fact);
  2795 }
  2796 
  2797 inline double
  2798 dot (const ColumnVector& v1, const ColumnVector& v2)
  2799 {
  2800   return (v1(0)*v2(0)+v1(1)*v2(1)+v1(2)*v2(2));
  2801 }
  2802 
  2803 inline double
  2804 norm (const ColumnVector& v)
  2805 {
  2806   return sqrt (dot (v, v));
  2807 }
  2808 
  2809 inline ColumnVector
  2810 cross (const ColumnVector& v1, const ColumnVector& v2)
  2811 {
  2812   ColumnVector r = xform_vector ();
  2813   r(0) = v1(1)*v2(2)-v1(2)*v2(1);
  2814   r(1) = v1(2)*v2(0)-v1(0)*v2(2);
  2815   r(2) = v1(0)*v2(1)-v1(1)*v2(0);
  2816   return r;
  2817 }
  2818 
  2819 inline Matrix
  2820 unit_cube (void)
  2821 {
  2822   static double data[32] = {
  2823       0,0,0,1,
  2824       1,0,0,1,
  2825       0,1,0,1,
  2826       0,0,1,1,
  2827       1,1,0,1,
  2828       1,0,1,1,
  2829       0,1,1,1,
  2830       1,1,1,1};
  2831   Matrix m (4, 8);
  2832   memcpy (m.fortran_vec (), data, sizeof(double)*32);
  2833   return m;
  2834 }
  2835 
  2836 inline ColumnVector
  2837 cam2xform (const Array<double>& m)
  2838 {
  2839   ColumnVector retval (4, 1.0);
  2840   memcpy (retval.fortran_vec (), m.fortran_vec (), sizeof(double)*3);
  2841   return retval;
  2842 }
  2843 
  2844 inline RowVector
  2845 xform2cam (const ColumnVector& v)
  2846 {
  2847   return v.extract_n (0, 3).transpose ();
  2848 }
  2849 
  2850 void
  2851 axes::properties::update_camera (void)
  2852 {
  2853   double xd = (xdir_is ("normal") ? 1 : -1);
  2854   double yd = (ydir_is ("normal") ? 1 : -1);
  2855   double zd = (zdir_is ("normal") ? 1 : -1);
  2856 
  2857   Matrix xlimits = sx.scale (get_xlim ().matrix_value ());
  2858   Matrix ylimits = sy.scale (get_ylim ().matrix_value ());
  2859   Matrix zlimits = sz.scale (get_zlim ().matrix_value ());
  2860 
  2861   double xo = xlimits(xd > 0 ? 0 : 1);
  2862   double yo = ylimits(yd > 0 ? 0 : 1);
  2863   double zo = zlimits(zd > 0 ? 0 : 1);
  2864   
  2865   Matrix pb  = get_plotboxaspectratio ().matrix_value ();
  2866   
  2867   bool autocam = (camerapositionmode_is ("auto")
  2868 		  && cameratargetmode_is ("auto")
  2869 	    	  && cameraupvectormode_is ("auto")
  2870 		  && cameraviewanglemode_is ("auto"));
  2871   bool dowarp = (autocam && dataaspectratiomode_is("auto")
  2872 		 && plotboxaspectratiomode_is ("auto"));
  2873 
  2874   ColumnVector c_eye (xform_vector ());
  2875   ColumnVector c_center (xform_vector ());
  2876   ColumnVector c_upv (xform_vector ());
  2877   
  2878   if (cameratargetmode_is ("auto"))
  2879     {
  2880       c_center(0) = (xlimits(0)+xlimits(1))/2;
  2881       c_center(1) = (ylimits(0)+ylimits(1))/2;
  2882       c_center(2) = (zlimits(0)+zlimits(1))/2;
  2883 
  2884       cameratarget = xform2cam (c_center);
  2885     }
  2886   else
  2887     c_center = cam2xform (get_cameratarget ().matrix_value ());
  2888   
  2889   if (camerapositionmode_is ("auto"))
  2890     {
  2891       Matrix tview = get_view ().matrix_value ();
  2892       double az = tview(0), el = tview(1);
  2893       double d = 5*sqrt(pb(0)*pb(0)+pb(1)*pb(1)+pb(2)*pb(2));
  2894 
  2895       if (el == 90 || el == -90)
  2896 	c_eye(2) = d*signum(el);
  2897       else
  2898 	{
  2899 	  az *= M_PI/180.0;
  2900 	  el *= M_PI/180.0;
  2901 	  c_eye(0) = d*cos(el)*sin(az);
  2902 	  c_eye(1) = -d*cos(el)*cos(az);
  2903 	  c_eye(2) = d*sin(el);
  2904 	}
  2905       c_eye(0) = c_eye(0)*(xlimits(1)-xlimits(0))/(xd*pb(0))+c_center(0);
  2906       c_eye(1) = c_eye(1)*(ylimits(1)-ylimits(0))/(yd*pb(1))+c_center(1);
  2907       c_eye(2) = c_eye(2)*(zlimits(1)-zlimits(0))/(zd*pb(2))+c_center(2);
  2908 
  2909       cameraposition = xform2cam (c_eye);
  2910     }
  2911   else
  2912     c_eye = cam2xform (get_cameraposition ().matrix_value ());
  2913 
  2914   if (cameraupvectormode_is ("auto"))
  2915     {
  2916       Matrix tview = get_view ().matrix_value ();
  2917       double az = tview(0), el = tview(1);
  2918 
  2919       if (el == 90 || el == -90)
  2920 	{
  2921 	  c_upv(0) = -sin(az*M_PI/180.0)*(xlimits(1)-xlimits(0))/pb(0);
  2922 	  c_upv(1) = cos(az*M_PI/180.0)*(ylimits(1)-ylimits(0))/pb(1);
  2923 	}
  2924       else
  2925 	c_upv(2) = 1;
  2926 
  2927       cameraupvector = xform2cam (c_upv);
  2928     }
  2929   else
  2930     c_upv = cam2xform (get_cameraupvector ().matrix_value ());
  2931 
  2932   Matrix x_view = xform_matrix ();
  2933   Matrix x_projection = xform_matrix ();
  2934   Matrix x_viewport = xform_matrix ();
  2935   Matrix x_normrender = xform_matrix ();
  2936   Matrix x_pre = xform_matrix ();
  2937   
  2938   x_render = xform_matrix ();
  2939   x_render_inv = xform_matrix ();
  2940 
  2941   scale (x_pre, pb(0), pb(1), pb(2));
  2942   translate (x_pre, -0.5, -0.5, -0.5);
  2943   scale (x_pre, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
  2944 	 zd/(zlimits(1)-zlimits(0)));
  2945   translate (x_pre, -xo, -yo, -zo);
  2946 
  2947   xform (c_eye, x_pre);
  2948   xform (c_center, x_pre);
  2949   scale (c_upv, pb(0)/(xlimits(1)-xlimits(0)), pb(1)/(ylimits(1)-ylimits(0)), 
  2950 	 pb(2)/(zlimits(1)-zlimits(0)));
  2951   translate (c_center, -c_eye(0), -c_eye(1), -c_eye(2));
  2952 
  2953   ColumnVector F (c_center), f (F), UP (c_upv);
  2954   normalize (f);
  2955   normalize (UP);
  2956 
  2957   if (std::abs (dot (f, UP)) > 1e-15)
  2958     {
  2959       double fa = 1/sqrt(1-f(2)*f(2));
  2960       scale (UP, fa, fa, fa);
  2961     }
  2962 
  2963   ColumnVector s = cross (f, UP);
  2964   ColumnVector u = cross (s, f);
  2965 
  2966   scale (x_view, 1, 1, -1);
  2967   Matrix l = xform_matrix ();
  2968   l(0,0) = s(0); l(0,1) = s(1); l(0,2) = s(2);
  2969   l(1,0) = u(0); l(1,1) = u(1); l(1,2) = u(2);
  2970   l(2,0) = -f(0); l(2,1) = -f(1); l(2,2) = -f(2);
  2971   x_view = x_view * l;
  2972   translate (x_view, -c_eye(0), -c_eye(1), -c_eye(2));
  2973   scale (x_view, pb(0), pb(1), pb(2));
  2974   translate (x_view, -0.5, -0.5, -0.5);
  2975 
  2976   Matrix x_cube = x_view * unit_cube ();
  2977   ColumnVector cmin = x_cube.row_min (), cmax = x_cube.row_max ();
  2978   double xM = cmax(0)-cmin(0);
  2979   double yM = cmax(1)-cmin(1);
  2980 
  2981   Matrix bb = get_boundingbox (true);
  2982 
  2983   double v_angle;
  2984 
  2985   if (cameraviewanglemode_is ("auto"))
  2986     {
  2987       double af;
  2988 
  2989       // FIXME -- was this really needed?  When compared to Matlab, it
  2990       // does not seem to be required. Need investigation with concrete
  2991       // backend to see results visually.
  2992       if (false && dowarp)
  2993         af = 1.0 / (xM > yM ? xM : yM);
  2994       else
  2995         {
  2996           if ((bb(2)/bb(3)) > (xM/yM))
  2997             af = 1.0 / yM;
  2998           else
  2999             af = 1.0 / xM;
  3000         }
  3001       v_angle = 2 * (180.0 / M_PI) * atan (1 / (2 * af * norm (F)));
  3002 
  3003       cameraviewangle = v_angle;
  3004     }
  3005   else
  3006     v_angle = get_cameraviewangle ();
  3007 
  3008   double pf = 1 / (2 * tan ((v_angle / 2) * M_PI / 180.0) * norm (F));
  3009   scale (x_projection, pf, pf, 1);
  3010 
  3011   if (dowarp)
  3012     {
  3013       xM *= pf;
  3014       yM *= pf;
  3015       translate (x_viewport, bb(0)+bb(2)/2, bb(1)+bb(3)/2, 0);
  3016       scale (x_viewport, bb(2)/xM, -bb(3)/yM, 1);
  3017     }
  3018   else
  3019     {
  3020       double pix = 1;
  3021       if (autocam)
  3022 	{
  3023 	  if ((bb(2)/bb(3)) > (xM/yM))
  3024 	    pix = bb(3);
  3025 	  else
  3026 	    pix = bb(2);
  3027 	}
  3028       else
  3029 	pix = (bb(2) < bb(3) ? bb(2) : bb(3));
  3030       translate (x_viewport, bb(0)+bb(2)/2, bb(1)+bb(3)/2, 0);
  3031       scale (x_viewport, pix, -pix, 1);
  3032     }
  3033 
  3034   x_normrender = x_viewport * x_projection * x_view;
  3035 
  3036   x_cube = x_normrender * unit_cube ();
  3037   cmin = x_cube.row_min ();
  3038   cmax = x_cube.row_max ();
  3039   x_zlim.resize (1, 2);
  3040   x_zlim(0) = cmin(2);
  3041   x_zlim(1) = cmax(2);
  3042 
  3043   x_render = x_normrender;
  3044   scale (x_render, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
  3045 	 zd/(zlimits(1)-zlimits(0)));
  3046   translate (x_render, -xo, -yo, -zo);
  3047 
  3048   x_viewtransform = x_view;
  3049   x_projectiontransform = x_projection;
  3050   x_viewporttransform = x_viewport;
  3051   x_normrendertransform = x_normrender;
  3052   x_rendertransform = x_render;
  3053 
  3054   x_render_inv = x_render.inverse ();
  3055 
  3056   // Note: these matrices are a slight modified version of the regular
  3057   // matrices, more suited for OpenGL rendering (x_gl_mat1 => light
  3058   // => x_gl_mat2)
  3059   x_gl_mat1 = x_view;
  3060   scale (x_gl_mat1, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
  3061 	 zd/(zlimits(1)-zlimits(0)));
  3062   translate (x_gl_mat1, -xo, -yo, -zo);
  3063   x_gl_mat2 = x_viewport * x_projection;
  3064 }
  3065 
  3066 void
  3067 axes::properties::update_aspectratios (void)
  3068 {
  3069   Matrix xlimits = get_xlim ().matrix_value ();
  3070   Matrix ylimits = get_ylim ().matrix_value ();
  3071   Matrix zlimits = get_zlim ().matrix_value ();
  3072 
  3073   double dx = (xlimits(1)-xlimits(0));
  3074   double dy = (ylimits(1)-ylimits(0));
  3075   double dz = (zlimits(1)-zlimits(0));
  3076 
  3077   if (dataaspectratiomode_is ("auto"))
  3078     {
  3079       double dmin = xmin (xmin (dx, dy), dz);
  3080       Matrix da (1, 3, 0.0);
  3081 
  3082       da(0) = dx/dmin;
  3083       da(1) = dy/dmin;
  3084       da(2) = dz/dmin;
  3085 
  3086       dataaspectratio = da;
  3087     }
  3088 
  3089   if (plotboxaspectratiomode_is ("auto"))
  3090     {
  3091       if (dataaspectratiomode_is ("auto"))
  3092 	plotboxaspectratio = Matrix (1, 3, 1.0);
  3093       else
  3094 	{
  3095 	  Matrix da = get_dataaspectratio ().matrix_value ();
  3096 	  Matrix pba (1, 3, 0.0);
  3097 
  3098 	  pba(0) = dx/da(0);
  3099 	  pba(1) = dy/da(1);
  3100 	  pba(2) = dz/da(2);
  3101 	}
  3102     }
  3103   
  3104   // FIXME -- if plotboxaspectratiomode is "manual", limits
  3105   // and/or dataaspectratio might be adapted.
  3106 }
  3107 
  3108 // The INTERNAL flag defines whether position or outerposition is used.
  3109 
  3110 Matrix
  3111 axes::properties::get_boundingbox (bool internal) const
  3112 {
  3113   graphics_object obj = gh_manager::get_object (get_parent ());
  3114   Matrix parent_bb = obj.get_properties ().get_boundingbox (true);
  3115   Matrix pos = (internal ?
  3116 		  get_position ().matrix_value ()
  3117 		  : get_outerposition ().matrix_value ());
  3118 
  3119 
  3120   pos = convert_position (pos, get_units (), "pixels",
  3121 			  parent_bb.extract_n (0, 2, 1, 2));
  3122   pos(0)--;
  3123   pos(1)--;
  3124   pos(1) = parent_bb(3) - pos(1) - pos(3);
  3125 
  3126   return pos;
  3127 }
  3128 
  3129 ColumnVector
  3130 graphics_xform::xform_vector (double x, double y, double z)
  3131 {
  3132   return ::xform_vector (x, y, z);
  3133 }
  3134 
  3135 Matrix
  3136 graphics_xform::xform_eye (void)
  3137 {
  3138   return ::xform_matrix ();
  3139 }
  3140 
  3141 ColumnVector
  3142 graphics_xform::transform (double x, double y, double z,
  3143 			   bool use_scale) const
  3144 {
  3145   if (use_scale)
  3146     {
  3147       x = sx.scale (x);
  3148       y = sy.scale (y);
  3149       z = sz.scale (z);
  3150     }
  3151 
  3152   return ::transform (xform, x, y, z);
  3153 }
  3154 
  3155 ColumnVector
  3156 graphics_xform::untransform (double x, double y, double z,
  3157 			     bool use_scale) const
  3158 {
  3159   ColumnVector v = ::transform (xform_inv, x, y, z);
  3160 
  3161   if (use_scale)
  3162     {
  3163       v(0) = sx.unscale (v(0));
  3164       v(1) = sy.unscale (v(1));
  3165       v(2) = sz.unscale (v(2));
  3166     }
  3167 
  3168   return v;
  3169 }
  3170 
  3171 octave_value
  3172 axes::get_default (const caseless_str& name) const
  3173 {
  3174   octave_value retval = default_properties.lookup (name);
  3175 
  3176   if (retval.is_undefined ())
  3177     {
  3178       graphics_handle parent = get_parent ();
  3179       graphics_object parent_obj = gh_manager::get_object (parent);
  3180 
  3181       retval = parent_obj.get_default (name);
  3182     }
  3183 
  3184   return retval;
  3185 }
  3186 
  3187 // FIXME -- remove.
  3188 // FIXME -- maybe this should go into array_property class?
  3189 /*
  3190 static void
  3191 check_limit_vals (double& min_val, double& max_val, double& min_pos,
  3192 		  const array_property& data)
  3193 {
  3194   double val = data.min_val ();
  3195   if (! (xisinf (val) || xisnan (val)) && val < min_val)
  3196     min_val = val;
  3197   val = data.max_val ();
  3198   if (! (xisinf (val) || xisnan (val)) && val > max_val)
  3199     max_val = val;
  3200   val = data.min_pos ();
  3201   if (! (xisinf (val) || xisnan (val)) && val > 0 && val < min_pos)
  3202     min_pos = val;
  3203 }
  3204 */
  3205 
  3206 static void
  3207 check_limit_vals (double& min_val, double& max_val, double& min_pos,
  3208 		  const octave_value& data)
  3209 {
  3210   if (data.is_matrix_type ())
  3211     {
  3212       Matrix m = data.matrix_value ();
  3213 
  3214       if (! error_state && m.numel () == 3)
  3215 	{
  3216 	  double val;
  3217 
  3218 	  val = m(0);
  3219 	  if (! (xisinf (val) || xisnan (val)) && val < min_val)
  3220 	    min_val = val;
  3221 
  3222 	  val = m(1);
  3223 	  if (! (xisinf (val) || xisnan (val)) && val > max_val)
  3224 	    max_val = val;
  3225 
  3226 	  val = m(2);
  3227 	  if (! (xisinf (val) || xisnan (val)) && val > 0 && val < min_pos)
  3228 	    min_pos = val;
  3229 	}
  3230     }
  3231 }
  3232 
  3233 // magform(x) Returns (a, b), where x = a * 10^b, a >= 1., and b is
  3234 // integral.
  3235 
  3236 static void
  3237 magform (double x, double& a, int& b)
  3238 {
  3239   if (x == 0)
  3240     {
  3241       a = 0;
  3242       b = 0;
  3243     }
  3244   else
  3245     {
  3246       double l = std::log10 (std::abs (x));
  3247       double r = std::fmod (l, 1.);
  3248       a = std::pow (10.0, r);
  3249       b = static_cast<int> (l-r);
  3250       if (a < 1)
  3251 	{
  3252 	  a *= 10;
  3253 	  b -= 1;
  3254 	}
  3255 
  3256       if (x < 0)
  3257 	a = -a;
  3258     }
  3259 }
  3260 
  3261 // A translation from Tom Holoryd's python code at
  3262 // http://kurage.nimh.nih.gov/tomh/tics.py
  3263 // FIXME -- add log ticks
  3264 
  3265 double
  3266 axes::properties::calc_tick_sep (double lo, double hi)
  3267 {
  3268   int ticint = 5;
  3269 
  3270   // Reference: Lewart, C. R., "Algorithms SCALE1, SCALE2, and
  3271   // SCALE3 for Determination of Scales on Computer Generated
  3272   // Plots", Communications of the ACM, 10 (1973), 639-640.
  3273   // Also cited as ACM Algorithm 463.
  3274 
  3275   double a;
  3276   int b, x;
  3277 
  3278   magform ((hi-lo)/ticint, a, b);
  3279 
  3280   static const double sqrt_2 = sqrt (2.0);
  3281   static const double sqrt_10 = sqrt (10.0);
  3282   static const double sqrt_50 = sqrt (50.0);
  3283 
  3284   if (a < sqrt_2)
  3285     x = 1;
  3286   else if (a < sqrt_10)
  3287     x = 2;
  3288   else if (a < sqrt_50)
  3289     x = 5;
  3290   else
  3291     x = 10;
  3292 
  3293   return x * std::pow (10., b);
  3294 
  3295 }
  3296 
  3297 // Attempt to make "nice" limits from the actual max and min of the
  3298 // data.  For log plots, we will also use the smallest strictly positive
  3299 // value.
  3300 
  3301 Matrix
  3302 axes::properties::get_axis_limits (double xmin, double xmax,
  3303 				   double min_pos, bool logscale)
  3304 {
  3305   Matrix retval;
  3306 
  3307   double min_val = xmin;
  3308   double max_val = xmax;
  3309 
  3310   if (! (xisinf (min_val) || xisinf (max_val)))
  3311     {
  3312       if (logscale)
  3313 	{
  3314 	  if (xisinf (min_pos))
  3315 	    {
  3316 	      // warning ("axis: logscale with no positive values to plot");
  3317 	      return retval;
  3318 	    }
  3319 
  3320 	  if (min_val <= 0)
  3321 	    {
  3322 	      warning ("axis: omitting nonpositive data in log plot");
  3323 	      min_val = min_pos;
  3324 	    }
  3325 	  // FIXME -- maybe this test should also be relative?
  3326 	  if (std::abs (min_val - max_val) < sqrt (DBL_EPSILON))
  3327 	    {
  3328 	      min_val *= 0.9;
  3329 	      max_val *= 1.1;
  3330 	    }
  3331 	  min_val = pow (10, floor (log10 (min_val)));
  3332 	  max_val = pow (10, ceil (log10 (max_val)));
  3333 	}
  3334       else
  3335 	{
  3336 	  if (min_val == 0 && max_val == 0)
  3337 	    {
  3338 	      min_val = -1;
  3339 	      max_val = 1;
  3340 	    }
  3341 	  // FIXME -- maybe this test should also be relative?
  3342 	  else if (std::abs (min_val - max_val) < sqrt (DBL_EPSILON))
  3343 	    {
  3344 	      min_val -= 0.1 * std::abs (min_val);
  3345 	      max_val += 0.1 * std::abs (max_val);
  3346 	    }
  3347 
  3348 	  double tick_sep = calc_tick_sep (min_val , max_val);
  3349 	  min_val = tick_sep * std::floor (min_val / tick_sep);
  3350 	  max_val = tick_sep * ceil (max_val / tick_sep);
  3351 	}
  3352     }
  3353 
  3354   retval.resize (1, 2);
  3355 
  3356   retval(0) = min_val;
  3357   retval(1) = max_val;
  3358 
  3359   return retval;
  3360 }
  3361 
  3362 void 
  3363 axes::properties::calc_ticks_and_lims (array_property& lims,
  3364 				       array_property& ticks,
  3365 				       bool limmode_is_auto, bool is_logscale)
  3366 {
  3367   // FIXME -- add log ticks and lims
  3368 
  3369   if (lims.get ().is_empty ())
  3370     return;
  3371 
  3372   double lo = (lims.get ().matrix_value ()) (0);
  3373   double hi = (lims.get ().matrix_value ()) (1);
  3374   // FIXME should this be checked for somewhere else? (i.e. set{x,y,z}lim)
  3375   if (hi < lo) 
  3376     {
  3377       double tmp = hi;
  3378       hi = lo;
  3379       lo = tmp;
  3380     }
  3381 
  3382   if (is_logscale)
  3383     {
  3384       // FIXME we should check for negtives here
  3385       hi = std::log10 (hi);
  3386       lo = std::log10 (lo);
  3387     }
  3388 
  3389   double tick_sep = calc_tick_sep (lo , hi);
  3390 
  3391   int i1 = static_cast<int> (std::floor (lo / tick_sep));
  3392   int i2 = static_cast<int> (std::ceil (hi / tick_sep));
  3393 
  3394   if (limmode_is_auto)
  3395     {
  3396       // adjust limits to include min and max tics
  3397       Matrix tmp_lims (1,2);
  3398       tmp_lims(0) = tick_sep * i1;
  3399       tmp_lims(1) = tick_sep * i2;
  3400 
  3401       if (is_logscale) 
  3402 	{
  3403 	  tmp_lims(0) = std::pow (10.,tmp_lims(0));
  3404 	  tmp_lims(1) = std::pow (10.,tmp_lims(1));
  3405 	}
  3406       lims = tmp_lims;
  3407     }
  3408   else
  3409     {
  3410       // adjust min and max tics if they are out of limits
  3411       i1 = static_cast<int> (std::ceil (lo / tick_sep));
  3412       i2 = static_cast<int> (std::floor (hi / tick_sep));
  3413     }
  3414       
  3415   Matrix tmp_ticks (1, i2-i1+1);
  3416   for (int i = 0; i <= i2-i1; i++) 
  3417     {
  3418       tmp_ticks (i) = tick_sep * (i+i1);
  3419       if (is_logscale)
  3420 	tmp_ticks (i) = std::pow (10., tmp_ticks (i));
  3421     }
  3422 
  3423   ticks = tmp_ticks;
  3424 }
  3425 
  3426 static void
  3427 get_children_limits (double& min_val, double& max_val, double& min_pos,
  3428 		     const Matrix& kids, char limit_type)
  3429 {
  3430   octave_idx_type n = kids.numel ();
  3431 
  3432   switch (limit_type)
  3433     {
  3434     case 'x':
  3435       for (octave_idx_type i = 0; i < n; i++)
  3436 	{
  3437 	  graphics_object obj = gh_manager::get_object (kids(i));
  3438 
  3439 	  if (obj.is_xliminclude ())
  3440 	    {
  3441 	      octave_value lim = obj.get_xlim ();
  3442 
  3443 	      check_limit_vals (min_val, max_val, min_pos, lim);
  3444 	    }
  3445 	}
  3446       break;
  3447 
  3448     case 'y':
  3449       for (octave_idx_type i = 0; i < n; i++)
  3450 	{
  3451 	  graphics_object obj = gh_manager::get_object (kids(i));
  3452 
  3453 	  if (obj.is_yliminclude ())
  3454 	    {
  3455 	      octave_value lim = obj.get_ylim ();
  3456 
  3457 	      check_limit_vals (min_val, max_val, min_pos, lim);
  3458 	    }
  3459 	}
  3460       break;
  3461     
  3462     case 'z':
  3463       for (octave_idx_type i = 0; i < n; i++)
  3464 	{
  3465 	  graphics_object obj = gh_manager::get_object (kids(i));
  3466 
  3467 	  if (obj.is_zliminclude ())
  3468 	    {
  3469 	      octave_value lim = obj.get_zlim ();
  3470 
  3471 	      check_limit_vals (min_val, max_val, min_pos, lim);
  3472 	    }
  3473 	}
  3474       break;
  3475     
  3476     case 'c':
  3477       for (octave_idx_type i = 0; i < n; i++)
  3478 	{
  3479 	  graphics_object obj = gh_manager::get_object (kids(i));
  3480 
  3481 	  if (obj.is_climinclude ())
  3482 	    {
  3483 	      octave_value lim = obj.get_clim ();
  3484 
  3485 	      check_limit_vals (min_val, max_val, min_pos, lim);
  3486 	    }
  3487 	}
  3488       break;
  3489     
  3490     case 'a':
  3491       for (octave_idx_type i = 0; i < n; i++)
  3492 	{
  3493 	  graphics_object obj = gh_manager::get_object (kids(i));
  3494 
  3495 	  if (obj.is_aliminclude ())
  3496 	    {
  3497 	      octave_value lim = obj.get_alim ();
  3498 
  3499 	      check_limit_vals (min_val, max_val, min_pos, lim);
  3500 	    }
  3501 	}
  3502       break;
  3503 
  3504     default:
  3505       break;
  3506     }
  3507 }
  3508 
  3509 static bool updating_axis_limits = false;
  3510 
  3511 void
  3512 axes::update_axis_limits (const std::string& axis_type)
  3513 {
  3514   if (updating_axis_limits)
  3515     return;
  3516 
  3517   Matrix kids = xproperties.get_children ();
  3518 
  3519   double min_val = octave_Inf;
  3520   double max_val = -octave_Inf;
  3521   double min_pos = octave_Inf;
  3522 
  3523   char update_type = 0;
  3524 
  3525   Matrix limits;
  3526 
  3527   if (axis_type == "xdata" || axis_type == "xscale"
  3528       || axis_type == "xldata" || axis_type == "xudata"
  3529       || axis_type == "xlimmode" || axis_type == "xliminclude"
  3530       || axis_type == "xlim")
  3531     {
  3532       if (xproperties.xlimmode_is ("auto"))
  3533 	{
  3534 	  get_children_limits (min_val, max_val, min_pos, kids, 'x');
  3535 	  
  3536 	  limits = xproperties.get_axis_limits (min_val, max_val, min_pos,
  3537 						xproperties.xscale_is ("log"));
  3538 
  3539 	  update_type = 'x';
  3540 	}
  3541     }
  3542   else if (axis_type == "ydata" || axis_type == "yscale"
  3543 	   || axis_type == "ldata" || axis_type == "udata"
  3544 	   || axis_type == "ylimmode" || axis_type == "yliminclude"
  3545 	   || axis_type == "ylim")
  3546     {
  3547       if (xproperties.ylimmode_is ("auto"))
  3548 	{
  3549 	  get_children_limits (min_val, max_val, min_pos, kids, 'y');
  3550 
  3551 	  limits = xproperties.get_axis_limits (min_val, max_val, min_pos,
  3552 						xproperties.yscale_is ("log"));
  3553 
  3554 	  update_type = 'y';
  3555 	}
  3556     }
  3557   else if (axis_type == "zdata" || axis_type == "zscale"
  3558 	   || axis_type == "zlimmode" || axis_type == "zliminclude"
  3559 	   || axis_type == "zlim")
  3560     {
  3561       if (xproperties.zlimmode_is ("auto"))
  3562 	{
  3563 	  get_children_limits (min_val, max_val, min_pos, kids, 'z');
  3564 
  3565 	  limits = xproperties.get_axis_limits (min_val, max_val, min_pos,
  3566 						xproperties.zscale_is ("log"));
  3567 
  3568 	  update_type = 'z';
  3569 	}
  3570     }
  3571   else if (axis_type == "cdata" || axis_type == "climmode"
  3572 	   || axis_type == "cdatamapping" || axis_type == "climinclude"
  3573 	   || axis_type == "clim")
  3574     {
  3575       if (xproperties.climmode_is ("auto"))
  3576 	{
  3577 	  get_children_limits (min_val, max_val, min_pos, kids, 'c');
  3578 
  3579 	  if (min_val > max_val)
  3580 	    {
  3581 	      min_val = min_pos = 0;
  3582 	      max_val = 1;
  3583 	    }
  3584 	  else if (min_val == max_val)
  3585 	    max_val = min_val + 1;
  3586 
  3587 	  limits.resize (1, 2);
  3588 
  3589 	  limits(0) = min_val;
  3590 	  limits(1) = max_val;
  3591 
  3592 	  update_type = 'c';
  3593 	}
  3594 
  3595     }
  3596   else if (axis_type == "alphadata" || axis_type == "alimmode"
  3597 	   || axis_type == "alphadatamapping" || axis_type == "aliminclude"
  3598 	   || axis_type == "alim")
  3599     {
  3600       if (xproperties.alimmode_is ("auto"))
  3601 	{
  3602 	  get_children_limits (min_val, max_val, min_pos, kids, 'a');
  3603 
  3604 	  if (min_val > max_val)
  3605 	    {
  3606 	      min_val = min_pos = 0;
  3607 	      max_val = 1;
  3608 	    }
  3609 	  else if (min_val == max_val)
  3610 	    max_val = min_val + 1;
  3611 
  3612 	  limits.resize (1, 2);
  3613 
  3614 	  limits(0) = min_val;
  3615 	  limits(1) = max_val;
  3616 
  3617 	  update_type = 'a';
  3618 	}
  3619 
  3620     }
  3621 
  3622   unwind_protect_bool (updating_axis_limits);
  3623   updating_axis_limits = true;
  3624 
  3625   switch (update_type)
  3626     {
  3627     case 'x':
  3628       xproperties.set_xlim (limits);
  3629       xproperties.set_xlimmode ("auto");
  3630       xproperties.update_xlim ();
  3631       break;
  3632 
  3633     case 'y':
  3634       xproperties.set_ylim (limits);
  3635       xproperties.set_ylimmode ("auto");
  3636       xproperties.update_ylim ();
  3637       break;
  3638 
  3639     case 'z':
  3640       xproperties.set_zlim (limits);
  3641       xproperties.set_zlimmode ("auto");
  3642       xproperties.update_zlim ();
  3643       break;
  3644 
  3645     case 'c':
  3646       xproperties.set_clim (limits);
  3647       xproperties.set_climmode ("auto");
  3648       break;
  3649 
  3650     case 'a':
  3651       xproperties.set_alim (limits);
  3652       xproperties.set_alimmode ("auto");
  3653       break;
  3654 
  3655     default:
  3656       break;
  3657     }
  3658 
  3659   xproperties.update_transform ();
  3660 
  3661   unwind_protect::run ();
  3662 }
  3663 
  3664 void
  3665 axes::properties::zoom (const Matrix& xl, const Matrix& yl)
  3666 {
  3667   zoom_stack.push_front (xlimmode.get ());
  3668   zoom_stack.push_front (xlim.get ());
  3669   zoom_stack.push_front (ylimmode.get ());
  3670   zoom_stack.push_front (ylim.get ());
  3671 
  3672   xlim = xl;
  3673   xlimmode = "manual";
  3674   ylim = yl;
  3675   ylimmode = "manual";
  3676 
  3677   update_transform ();
  3678   update_xlim (false);
  3679   update_ylim (false);
  3680 }
  3681 
  3682 void
  3683 axes::properties::unzoom (void)
  3684 {
  3685   if (zoom_stack.size () >= 4)
  3686     {
  3687       ylim = zoom_stack.front ();
  3688       zoom_stack.pop_front ();
  3689       ylimmode = zoom_stack.front ();
  3690       zoom_stack.pop_front ();
  3691       xlim = zoom_stack.front ();
  3692       zoom_stack.pop_front ();
  3693       xlimmode = zoom_stack.front ();
  3694       zoom_stack.pop_front ();
  3695 
  3696       update_transform ();
  3697       update_xlim (false);
  3698       update_ylim (false);
  3699     }
  3700 }
  3701 
  3702 void
  3703 axes::properties::clear_zoom_stack (void)
  3704 {
  3705   while (zoom_stack.size () > 4)
  3706     zoom_stack.pop_front ();
  3707 
  3708   unzoom ();
  3709 }
  3710 
  3711 // ---------------------------------------------------------------------
  3712 
  3713 Matrix
  3714 line::properties::compute_xlim (void) const
  3715 {
  3716   Matrix m (1, 3);
  3717 
  3718   m(0) = xmin (xdata.min_val (), xmin (xldata.min_val (), xudata.min_val ()));
  3719   m(1) = xmax (xdata.max_val (), xmax (xldata.max_val (), xudata.max_val ()));
  3720   m(2) = xmin (xdata.min_pos (), xmin (xldata.min_pos (), xudata.min_pos ()));
  3721 
  3722   return m;
  3723 }
  3724 
  3725 Matrix
  3726 line::properties::compute_ylim (void) const
  3727 {
  3728   Matrix m (1, 3);
  3729 
  3730   m(0) = xmin (ydata.min_val (), xmin (ldata.min_val (), udata.min_val ()));
  3731   m(1) = xmax (ydata.max_val (), xmax (ldata.max_val (), udata.max_val ()));
  3732   m(2) = xmin (ydata.min_pos (), xmin (ldata.min_pos (), udata.min_pos ()));
  3733 
  3734   return m;
  3735 }
  3736 
  3737 // ---------------------------------------------------------------------
  3738 
  3739 // Note: "text" code is entirely auto-generated
  3740 
  3741 // ---------------------------------------------------------------------
  3742 
  3743 // Note: "image" code is entirely auto-generated
  3744 
  3745 // ---------------------------------------------------------------------
  3746 
  3747 octave_value
  3748 patch::properties::get_color_data (void) const
  3749 {
  3750   return convert_cdata (*this, get_facevertexcdata (),
  3751 			cdatamapping_is ("scaled"), 2);
  3752 }
  3753 
  3754 // ---------------------------------------------------------------------
  3755 
  3756 octave_value
  3757 surface::properties::get_color_data (void) const
  3758 {
  3759   return convert_cdata (*this, get_cdata (), cdatamapping_is ("scaled"), 3);
  3760 }
  3761 
  3762 inline void
  3763 cross_product (double x1, double y1, double z1,
  3764 	       double x2, double y2, double z2,
  3765 	       double& x, double& y, double& z)
  3766 {
  3767   x += (y1 * z2 - z1 * y2);
  3768   y += (z1 * x2 - x1 * z2);
  3769   z += (x1 * y2 - y1 * x2);
  3770 }
  3771 
  3772 void
  3773 surface::properties::update_normals (void)
  3774 {
  3775   if (normalmode_is ("auto"))
  3776     {
  3777       Matrix x = get_xdata ().matrix_value ();
  3778       Matrix y = get_ydata ().matrix_value ();
  3779       Matrix z = get_zdata ().matrix_value ();
  3780 
  3781 
  3782       int p = z.columns (), q = z.rows ();
  3783       int i1 = 0, i2 = 0, i3 = 0;
  3784       int j1 = 0, j2 = 0, j3 = 0;
  3785 
  3786       bool x_mat = (x.rows () == q);
  3787       bool y_mat = (y.columns () == p);
  3788 
  3789       NDArray n (dim_vector (q, p, 3), 0.0);
  3790 
  3791       for (int i = 0; i < p; i++)
  3792 	{
  3793 	  if (y_mat)
  3794 	    {
  3795 	      i1 = i - 1;
  3796 	      i2 = i;
  3797 	      i3 = i + 1;
  3798 	    }
  3799 
  3800 	  for (int j = 0; j < q; j++)
  3801 	    {
  3802 	      if (x_mat)
  3803 		{
  3804 		  j1 = j - 1;
  3805 		  j2 = j;
  3806 		  j3 = j + 1;
  3807 		}
  3808 
  3809 	      double& nx = n(j, i, 0);
  3810 	      double& ny = n(j, i, 1);
  3811 	      double& nz = n(j, i, 2);
  3812 
  3813               if ((j > 0) && (i > 0))
  3814                   // upper left quadrangle
  3815 	          cross_product (x(j1,i-1)-x(j2,i), y(j-1,i1)-y(j,i2), z(j-1,i-1)-z(j,i),
  3816 		                 x(j2,i-1)-x(j1,i), y(j,i1)-y(j-1,i2), z(j,i-1)-z(j-1,i),
  3817 			         nx, ny, nz);
  3818 
  3819               if ((j > 0) && (i < (p -1)))
  3820                   // upper right quadrangle
  3821                   cross_product (x(j1,i+1)-x(j2,i), y(j-1,i3)-y(j,i2), z(j-1,i+1)-z(j,i),
  3822 		                 x(j1,i)-x(j2,i+1), y(j-1,i2)-y(j,i3), z(j-1,i)-z(j,i+1),
  3823 			         nx, ny, nz);
  3824 
  3825               if ((j < (q - 1)) && (i > 0))
  3826                   // lower left quadrangle
  3827                   cross_product (x(j2,i-1)-x(j3,i), y(j,i1)-y(j+1,i2), z(j,i-1)-z(j+1,i),
  3828 		                 x(j3,i-1)-x(j2,i), y(j+1,i1)-y(j,i2), z(j+1,i-1)-z(j,i),
  3829 			         nx, ny, nz);
  3830 
  3831               if ((j < (q - 1)) && (i < (p -1)))
  3832                   // lower right quadrangle
  3833 	          cross_product (x(j3,i)-x(j2,i+1), y(j+1,i2)-y(j,i3), z(j+1,i)-z(j,i+1),
  3834                                  x(j3,i+1)-x(j2,i), y(j+1,i3)-y(j,i2), z(j+1,i+1)-z(j,i),
  3835 			         nx, ny, nz);
  3836 
  3837               double d = - std::max(std::max(fabs(nx), fabs(ny)), fabs(nz));
  3838 
  3839 	      nx /= d;
  3840 	      ny /= d;
  3841 	      nz /= d;
  3842 	    }
  3843 	}
  3844       vertexnormals = n;
  3845     }
  3846 }
  3847 
  3848 // ---------------------------------------------------------------------
  3849 
  3850 void
  3851 hggroup::update_axis_limits (const std::string& axis_type)
  3852 {
  3853   Matrix kids = xproperties.get_children ();
  3854 
  3855   double min_val = octave_Inf;
  3856   double max_val = -octave_Inf;
  3857   double min_pos = octave_Inf;
  3858 
  3859   char update_type = 0;
  3860 
  3861   if (axis_type == "xlim" || axis_type == "xliminclude")
  3862     {
  3863       get_children_limits (min_val, max_val, min_pos, kids, 'x');
  3864       
  3865       update_type = 'x';
  3866     }
  3867   else if (axis_type == "ylim" || axis_type == "yliminclude")
  3868     {
  3869       get_children_limits (min_val, max_val, min_pos, kids, 'y');
  3870 
  3871       update_type = 'y';
  3872     }
  3873   else if (axis_type == "zlim" || axis_type == "zliminclude")
  3874     {
  3875       get_children_limits (min_val, max_val, min_pos, kids, 'z');
  3876 
  3877       update_type = 'z';
  3878     }
  3879   else if (axis_type == "clim" || axis_type == "climinclude")
  3880     {
  3881       get_children_limits (min_val, max_val, min_pos, kids, 'c');
  3882 
  3883       update_type = 'c';
  3884 
  3885     }
  3886   else if (axis_type == "alim" || axis_type == "aliminclude")
  3887     {
  3888       get_children_limits (min_val, max_val, min_pos, kids, 'a');
  3889 
  3890       update_type = 'a';
  3891     }
  3892 
  3893   Matrix limits (1, 3, 0.0);
  3894 
  3895   limits(0) = min_val;
  3896   limits(1) = max_val;
  3897   limits(2) = min_pos;
  3898 
  3899   switch (update_type)
  3900     {
  3901     case 'x':
  3902       xproperties.set_xlim (limits);
  3903       break;
  3904 
  3905     case 'y':
  3906       xproperties.set_ylim (limits);
  3907       break;
  3908 
  3909     case 'z':
  3910       xproperties.set_zlim (limits);
  3911       break;
  3912 
  3913     case 'c':
  3914       xproperties.set_clim (limits);
  3915       break;
  3916 
  3917     case 'a':
  3918       xproperties.set_alim (limits);
  3919       break;
  3920 
  3921     default:
  3922       break;
  3923     }
  3924 
  3925   base_graphics_object::update_axis_limits (axis_type);
  3926 }
  3927 
  3928 // ---------------------------------------------------------------------
  3929 
  3930 octave_value
  3931 base_graphics_object::get_default (const caseless_str& name) const
  3932 {
  3933   graphics_handle parent = get_parent ();
  3934   graphics_object parent_obj = gh_manager::get_object (parent);
  3935 
  3936   return parent_obj.get_default (type () + name);
  3937 }
  3938 
  3939 octave_value
  3940 base_graphics_object::get_factory_default (const caseless_str& name) const
  3941 {
  3942   graphics_object parent_obj = gh_manager::get_object (0);
  3943 
  3944   return parent_obj.get_factory_default (type () + name);
  3945 }
  3946 
  3947 // We use a random value for the handle to avoid issues with plots and
  3948 // scalar values for the first argument.
  3949 gh_manager::gh_manager (void)
  3950   : handle_map (), handle_free_list (),
  3951     next_handle (-1.0 - (rand () + 1.0) / (RAND_MAX + 2.0))
  3952 {
  3953   handle_map[0] = graphics_object (new root_figure ());
  3954 
  3955   // Make sure the default backend is registered.
  3956   graphics_backend::default_backend ();
  3957 }
  3958 
  3959 graphics_handle
  3960 gh_manager::do_make_graphics_handle (const std::string& go_name,
  3961 				     const graphics_handle& p, bool do_createfcn)
  3962 {
  3963   graphics_handle h = get_handle (go_name);
  3964 
  3965   base_graphics_object *go = 0;
  3966 
  3967   go = make_graphics_object_from_type (go_name, h, p);
  3968   
  3969   if (go)
  3970     {
  3971       graphics_object obj (go);
  3972 
  3973       handle_map[h] = obj;
  3974       if (do_createfcn)
  3975         go->get_properties ().execute_createfcn ();
  3976 
  3977       // notify backend
  3978       graphics_backend backend = go->get_backend ();
  3979       if (backend)
  3980         backend.object_created (obj);
  3981     }
  3982   else
  3983     error ("gh_manager::do_make_graphics_handle: invalid object type `%s'",
  3984 	   go_name.c_str ());
  3985 
  3986   return h;
  3987 }
  3988 
  3989 graphics_handle
  3990 gh_manager::do_make_figure_handle (double val)
  3991 {
  3992   graphics_handle h = val;
  3993 
  3994   base_graphics_object* go = new figure (h, 0);
  3995   graphics_object obj (go);
  3996 
  3997   handle_map[h] = obj;
  3998 
  3999   // notify backend
  4000   graphics_backend backend = go->get_backend ();
  4001   if (backend)
  4002     backend.object_created (obj);
  4003   
  4004   return h;
  4005 }
  4006 
  4007 void
  4008 gh_manager::do_push_figure (const graphics_handle& h)
  4009 {
  4010   do_pop_figure (h);
  4011 
  4012   figure_list.push_front (h);
  4013 }
  4014 
  4015 void
  4016 gh_manager::do_pop_figure (const graphics_handle& h)
  4017 {
  4018   for (figure_list_iterator p = figure_list.begin ();
  4019        p != figure_list.end ();
  4020        p++)
  4021     {
  4022       if (*p == h)
  4023 	{
  4024 	  figure_list.erase (p);
  4025 	  break;
  4026 	}
  4027     }
  4028 }
  4029 
  4030 class
  4031 callback_event : public base_graphics_event
  4032 {
  4033 public:
  4034   callback_event (const graphics_handle& h, const std::string& name,
  4035 		  const octave_value& data = Matrix ())
  4036       : base_graphics_event (), handle (h), callback_name (name),
  4037         callback_data (data) { }
  4038 
  4039   void execute (void)
  4040     {
  4041       gh_manager::execute_callback (handle, callback_name, callback_data);
  4042     }
  4043 
  4044 private:
  4045   callback_event (void)
  4046       : base_graphics_event () { }
  4047 
  4048 private:
  4049   graphics_handle handle;
  4050   std::string callback_name;
  4051   octave_value callback_data;
  4052 };
  4053 
  4054 class
  4055 function_event : public base_graphics_event
  4056 {
  4057 public:
  4058   function_event (graphics_event::event_fcn fcn, void* data = 0)
  4059       : base_graphics_event (), function (fcn),
  4060         function_data (data) { }
  4061 
  4062   void execute (void)
  4063     {
  4064       function (function_data);
  4065     }
  4066 
  4067 private:
  4068   function_event (void)
  4069       : base_graphics_event () { }
  4070 
  4071 private:
  4072   graphics_event::event_fcn function;
  4073   void* function_data;
  4074 };
  4075 
  4076 class
  4077 set_event : public base_graphics_event
  4078 {
  4079 public:
  4080   set_event (const graphics_handle& h, const std::string& name,
  4081 	     const octave_value& value)
  4082       : base_graphics_event (), handle (h), property_name (name),
  4083         property_value (value) { }
  4084 
  4085   void execute (void)
  4086     {
  4087       gh_manager::autolock guard;
  4088 
  4089       xset (handle, property_name, property_value);
  4090     }
  4091 
  4092 private:
  4093   set_event (void)
  4094       : base_graphics_event () { }
  4095 
  4096 private:
  4097   graphics_handle handle;
  4098   std::string property_name;
  4099   octave_value property_value;
  4100 };
  4101 
  4102 graphics_event
  4103 graphics_event::create_callback_event (const graphics_handle& h,
  4104 				       const std::string& name,
  4105 				       const octave_value& data)
  4106 {
  4107   graphics_event e;
  4108 
  4109   e.rep = new callback_event (h, name, data);
  4110 
  4111   return e;
  4112 }
  4113 
  4114 graphics_event
  4115 graphics_event::create_function_event (graphics_event::event_fcn fcn,
  4116 				       void *data)
  4117 {
  4118   graphics_event e;
  4119 
  4120   e.rep = new function_event (fcn, data);
  4121 
  4122   return e;
  4123 }
  4124 
  4125 graphics_event
  4126 graphics_event::create_set_event (const graphics_handle& h,
  4127 				  const std::string& name,
  4128 				  const octave_value& data)
  4129 {
  4130   graphics_event e;
  4131 
  4132   e.rep = new set_event (h, name, data);
  4133 
  4134   return e;
  4135 }
  4136 
  4137 static void
  4138 xset_gcbo (const graphics_handle& h)
  4139 {
  4140   graphics_object go = gh_manager::get_object (0);
  4141   root_figure::properties& props =
  4142       dynamic_cast<root_figure::properties&> (go.get_properties ());
  4143 
  4144   props.set_callbackobject (h.as_octave_value ());
  4145 }
  4146 
  4147 void
  4148 gh_manager::do_restore_gcbo (void)
  4149 {
  4150   gh_manager::autolock guard;
  4151 
  4152   callback_objects.pop_front ();
  4153 
  4154   xset_gcbo (callback_objects.empty ()
  4155 	     ? graphics_handle ()
  4156 	     : callback_objects.front ().get_handle ());
  4157 }
  4158 
  4159 void
  4160 gh_manager::do_execute_callback (const graphics_handle& h,
  4161 				 const octave_value& cb_arg,
  4162 				 const octave_value& data)
  4163 {
  4164   octave_value_list args;
  4165   octave_function *fcn = 0;
  4166 
  4167   args(0) = h.as_octave_value ();
  4168   if (data.is_defined ())
  4169     args(1) = data;
  4170   else
  4171     args(1) = Matrix ();
  4172 
  4173   unwind_protect::begin_frame ("execute_callback");
  4174   unwind_protect::add (gh_manager::restore_gcbo);
  4175 
  4176   if (true)
  4177     {
  4178       gh_manager::autolock guard;
  4179   
  4180       callback_objects.push_front (get_object (h));
  4181       xset_gcbo (h);
  4182     }
  4183 
  4184   BEGIN_INTERRUPT_WITH_EXCEPTIONS;
  4185 
  4186   // Copy CB because "function_value" method is non-const.
  4187 
  4188   octave_value cb = cb_arg;
  4189 
  4190   if (cb.is_function_handle ())
  4191     fcn = cb.function_value ();
  4192   else if (cb.is_string ())
  4193     {
  4194       int status;
  4195       std::string s = cb.string_value ();
  4196 
  4197       eval_string (s, false, status);
  4198     }
  4199   else if (cb.is_cell () && cb.length () > 0
  4200            && (cb.rows () == 1 || cb.columns () == 1)
  4201            && cb.cell_value ()(0).is_function_handle ())
  4202     {
  4203       Cell c = cb.cell_value ();
  4204 
  4205       fcn = c(0).function_value ();
  4206       if (! error_state)
  4207         {
  4208           for (int i = 1; i < c.length () ; i++)
  4209             args(1+i) = c(i);
  4210         }
  4211     }
  4212   else
  4213     {
  4214       std::string nm = cb.class_name ();
  4215       error ("trying to execute non-executable object (class = %s)",
  4216 	     nm.c_str ());
  4217     }
  4218 
  4219   if (fcn && ! error_state)
  4220     feval (fcn, args);
  4221   
  4222   END_INTERRUPT_WITH_EXCEPTIONS;
  4223 
  4224   unwind_protect::run_frame ("execute_callback");
  4225 }
  4226 
  4227 void
  4228 gh_manager::do_post_event (const graphics_event& e)
  4229 {
  4230   event_queue.push_back (e);
  4231 
  4232   command_editor::add_event_hook (gh_manager::process_events);
  4233 }
  4234 
  4235 void
  4236 gh_manager::do_post_callback (const graphics_handle& h, const std::string name,
  4237 			      const octave_value& data)
  4238 {
  4239   gh_manager::autolock guard;
  4240 
  4241   graphics_object go = get_object (h);
  4242 
  4243   if (go.valid_object ())
  4244     {
  4245       if (callback_objects.empty ())
  4246 	do_post_event (graphics_event::create_callback_event (h, name, data));
  4247       else
  4248 	{
  4249 	  const graphics_object& current = callback_objects.front ();
  4250 
  4251 	  if (current.get_properties ().is_interruptible ())
  4252 	    do_post_event (graphics_event::create_callback_event (h, name, data));
  4253 	  else
  4254 	    {
  4255 	      caseless_str busy_action (go.get_properties ().get_busyaction ());
  4256 
  4257 	      if (busy_action.compare ("queue"))
  4258 		do_post_event (graphics_event::create_callback_event (h, name, data));
  4259 	      else
  4260 		{
  4261 		  caseless_str cname (name);
  4262 
  4263 		  if (cname.compare ("deletefcn")
  4264 		      || cname.compare ("createfcn")
  4265 		      || (go.isa ("figure")
  4266 			  && (cname.compare ("closerequestfcn")
  4267 			      || cname.compare ("resizefcn"))))
  4268 		    do_post_event (graphics_event::create_callback_event (h, name, data));
  4269 		}
  4270 	    }
  4271 	}
  4272     }
  4273 }
  4274 
  4275 void
  4276 gh_manager::do_post_function (graphics_event::event_fcn fcn, void* fcn_data)
  4277 {
  4278   gh_manager::autolock guard;
  4279 
  4280   do_post_event (graphics_event::create_function_event (fcn, fcn_data));
  4281 }
  4282 
  4283 void
  4284 gh_manager::do_post_set (const graphics_handle& h, const std::string name,
  4285 			 const octave_value& value)
  4286 {
  4287   gh_manager::autolock guard;
  4288 
  4289   do_post_event (graphics_event::create_set_event (h, name, value));
  4290 }
  4291 
  4292 int
  4293 gh_manager::do_process_events (bool force)
  4294 {
  4295   graphics_event e;
  4296 
  4297   do
  4298     {
  4299       e = graphics_event ();
  4300 
  4301       gh_manager::lock ();
  4302 
  4303       if (! event_queue.empty ())
  4304 	{
  4305 	  if (callback_objects.empty () || force)
  4306 	    {
  4307 	      e = event_queue.front ();
  4308 	      
  4309 	      event_queue.pop_front ();
  4310 	    }
  4311 	  else
  4312 	    {
  4313 	      const graphics_object& go = callback_objects.front ();
  4314 
  4315 	      if (go.get_properties ().is_interruptible ())
  4316 		{
  4317 		  e = event_queue.front ();
  4318 
  4319 		  event_queue.pop_front ();
  4320 		}
  4321 	    }
  4322 	}
  4323 
  4324       gh_manager::unlock ();
  4325 
  4326       if (e.ok ())
  4327 	e.execute ();
  4328     }
  4329   while (e.ok ());
  4330 
  4331   gh_manager::lock ();
  4332 
  4333   if (event_queue.empty ())
  4334     command_editor::remove_event_hook (gh_manager::process_events);
  4335 
  4336   gh_manager::unlock ();
  4337 
  4338   return 0;
  4339 }
  4340 
  4341 property_list::plist_map_type
  4342 root_figure::init_factory_properties (void)
  4343 {
  4344   property_list::plist_map_type plist_map;
  4345 
  4346   plist_map["figure"] = figure::properties::factory_defaults ();
  4347   plist_map["axes"] = axes::properties::factory_defaults ();
  4348   plist_map["line"] = line::properties::factory_defaults ();
  4349   plist_map["text"] = text::properties::factory_defaults ();
  4350   plist_map["image"] = image::properties::factory_defaults ();
  4351   plist_map["patch"] = patch::properties::factory_defaults ();
  4352   plist_map["surface"] = surface::properties::factory_defaults ();
  4353   plist_map["hggroup"] = hggroup::properties::factory_defaults ();
  4354 
  4355   return plist_map;
  4356 }
  4357 
  4358 // ---------------------------------------------------------------------
  4359 
  4360 DEFUN (ishandle, args, ,
  4361   "-*- texinfo -*-\n\
  4362 @deftypefn {Built-in Function} {} ishandle (@var{h})\n\
  4363 Return true if @var{h} is a graphics handle and false otherwise.\n\
  4364 @end deftypefn")
  4365 {
  4366   gh_manager::autolock guard;
  4367 
  4368   octave_value retval;
  4369 
  4370   if (args.length () == 1)
  4371     retval = is_handle (args(0));
  4372   else
  4373     print_usage ();
  4374 
  4375   return retval;
  4376 }
  4377 
  4378 DEFUN (set, args, ,
  4379   "-*- texinfo -*-\n\
  4380 @deftypefn {Built-in Function} {} set (@var{h}, @var{p}, @var{v}, @dots{})\n\
  4381 Set the named property value or vector @var{p} to the value @var{v}\n\
  4382 for the graphics handle @var{h}.\n\
  4383 @end deftypefn")
  4384 {
  4385   gh_manager::autolock guard;
  4386 
  4387   octave_value retval;
  4388 
  4389   int nargin = args.length ();
  4390 
  4391   if (nargin > 0)
  4392     {
  4393       ColumnVector hcv (args(0).vector_value ());
  4394 
  4395       if (! error_state)
  4396         {
  4397 	  bool request_drawnow = false;
  4398 
  4399           for (octave_idx_type n = 0; n < hcv.length (); n++) 
  4400             {
  4401               graphics_object obj = gh_manager::get_object (hcv(n));
  4402 
  4403               if (obj)
  4404                 {
  4405                   obj.set (args.splice (0, 1));
  4406 
  4407                   request_drawnow = true;
  4408                 }
  4409               else
  4410 		{
  4411 		  error ("set: invalid handle (= %g)", hcv(n));
  4412 		  break;
  4413 		}
  4414             }
  4415 
  4416 	  if (! error_state && request_drawnow)
  4417 	    Vdrawnow_requested = true;
  4418         }
  4419       else
  4420         error ("set: expecting graphics handle as first argument");
  4421     }
  4422   else
  4423     print_usage ();
  4424 
  4425   return retval;
  4426 }
  4427 
  4428 DEFUN (get, args, ,
  4429   "-*- texinfo -*-\n\
  4430 @deftypefn {Built-in Function} {} get (@var{h}, @var{p})\n\
  4431 Return the named property @var{p} from the graphics handle @var{h}.\n\
  4432 If @var{p} is omitted, return the complete property list for @var{h}.\n\
  4433 If @var{h} is a vector, return a cell array including the property\n\
  4434 values or lists respectively.\n\
  4435 @end deftypefn")
  4436 {
  4437   gh_manager::autolock guard;
  4438 
  4439   octave_value retval;
  4440 
  4441   Cell vals;
  4442 
  4443   int nargin = args.length ();
  4444 
  4445   if (nargin == 1 || nargin == 2)
  4446     {
  4447       ColumnVector hcv (args(0).vector_value ());
  4448 
  4449       if (! error_state)
  4450         {
  4451 	  octave_idx_type len = hcv.length ();
  4452 
  4453 	  vals.resize (dim_vector (len, 1));
  4454 
  4455           for (octave_idx_type n = 0; n < len; n++)
  4456             {
  4457               graphics_object obj = gh_manager::get_object (hcv(n));
  4458 
  4459               if (obj)
  4460                 {
  4461                   if (nargin == 1)
  4462                     vals(n) = obj.get ();
  4463                   else
  4464                     {
  4465                       caseless_str property = args(1).string_value ();
  4466 
  4467                       if (! error_state)
  4468                         vals(n) = obj.get (property);
  4469                       else
  4470 			{
  4471 			  error ("get: expecting property name as second argument");
  4472 			  break;
  4473 			}
  4474                     }
  4475                 }
  4476               else
  4477 		{
  4478 		  error ("get: invalid handle (= %g)", hcv(n));
  4479 		  break;
  4480 		}
  4481             }
  4482         }
  4483       else
  4484         error ("get: expecting graphics handle as first argument");
  4485     }
  4486   else
  4487     print_usage ();
  4488 
  4489   if (! error_state)
  4490     {
  4491       octave_idx_type len = vals.numel ();
  4492 
  4493       if (len > 1)
  4494 	retval = vals;
  4495       else if (len == 1)
  4496 	retval = vals(0);
  4497     }
  4498 
  4499   return retval;
  4500 }
  4501 
  4502 // Return all properties from the graphics handle @var{h}.
  4503 // If @var{h} is a vector, return a cell array including the
  4504 // property values or lists respectively.
  4505 
  4506 DEFUN (__get__, args, ,
  4507   "-*- texinfo -*-\n\
  4508 @deftypefn {Built-in Function} {} __get__ (@var{h})\n\
  4509 Undocumented internal function.\n\
  4510 @end deftypefn")
  4511 {
  4512   gh_manager::autolock guard;
  4513 
  4514   octave_value retval;
  4515 
  4516   Cell vals;
  4517 
  4518   int nargin = args.length ();
  4519 
  4520   if (nargin == 1)
  4521     {
  4522       ColumnVector hcv (args(0).vector_value ());
  4523 
  4524       if (! error_state)
  4525         {
  4526           octave_idx_type len = hcv.length ();
  4527 
  4528           vals.resize (dim_vector (len, 1));
  4529 
  4530           for (octave_idx_type n = 0; n < len; n++)
  4531             {
  4532               graphics_object obj = gh_manager::get_object (hcv(n));
  4533 
  4534               if (obj)
  4535                 vals(n) = obj.get (true);
  4536               else
  4537                 {
  4538                   error ("get: invalid handle (= %g)", hcv(n));
  4539                   break;
  4540                 }
  4541             }
  4542         }
  4543       else
  4544         error ("get: expecting graphics handle as first argument");
  4545     }
  4546   else
  4547     print_usage ();
  4548 
  4549   if (! error_state)
  4550     {
  4551       octave_idx_type len = vals.numel ();
  4552 
  4553       if (len > 1)
  4554         retval = vals;
  4555       else if (len == 1)
  4556         retval = vals(0);
  4557     }
  4558 
  4559   return retval;
  4560 }
  4561 
  4562 static octave_value
  4563 make_graphics_object (const std::string& go_name,
  4564 		      const octave_value_list& args)
  4565 {
  4566   octave_value retval;
  4567 
  4568   double val = octave_NaN;
  4569 
  4570   octave_value_list xargs = args.splice (0, 1);
  4571 
  4572   caseless_str p ("parent");
  4573 
  4574   for (int i = 0; i < xargs.length (); i++)
  4575     if (xargs(i).is_string ()
  4576 	&& p.compare (xargs(i).string_value ()))
  4577       {
  4578 	if (i < (xargs.length () - 1))
  4579 	  {
  4580 	    val = xargs(i+1).double_value ();
  4581 
  4582 	    if (! error_state)
  4583 	      {
  4584 		xargs = xargs.splice (i, 2);
  4585 		break;
  4586 	      }
  4587 	  }
  4588 	else
  4589 	  error ("__go_%s__: missing value for parent property",
  4590 		 go_name.c_str ());
  4591       }
  4592 
  4593   if (! error_state && xisnan (val))
  4594     val = args(0).double_value ();
  4595 
  4596   if (! error_state)
  4597     {
  4598       graphics_handle parent = gh_manager::lookup (val);
  4599 
  4600       if (parent.ok ())
  4601 	{
  4602 	  graphics_handle h
  4603 	    = gh_manager::make_graphics_handle (go_name, parent, false);
  4604 
  4605 	  if (! error_state)
  4606 	    {
  4607 	      adopt (parent, h);
  4608 
  4609 	      xset (h, xargs);
  4610 	      xcreatefcn (h);
  4611 
  4612 	      retval = h.value ();
  4613 
  4614 	      if (! error_state)
  4615 		Vdrawnow_requested = true;
  4616 	    }
  4617 	  else
  4618 	    error ("__go%s__: unable to create graphics handle",
  4619 		   go_name.c_str ());
  4620 	}
  4621       else
  4622 	error ("__go_%s__: invalid parent", go_name.c_str ());
  4623     }
  4624   else
  4625     error ("__go_%s__: invalid parent", go_name.c_str ());
  4626 
  4627   return retval;
  4628 }
  4629 
  4630 DEFUN (__go_figure__, args, ,
  4631    "-*- texinfo -*-\n\
  4632 @deftypefn {Built-in Function} {} __go_figure__ (@var{fignum})\n\
  4633 Undocumented internal function.\n\
  4634 @end deftypefn")
  4635 {
  4636   gh_manager::autolock guard;
  4637 
  4638   octave_value retval;
  4639 
  4640   if (args.length () > 0)
  4641     {
  4642       double val = args(0).double_value ();
  4643 
  4644       if (! error_state)
  4645 	{
  4646 	  if (is_figure (val))
  4647 	    {
  4648 	      graphics_handle h = gh_manager::lookup (val);
  4649 
  4650 	      xset (h, args.splice (0, 1));
  4651 
  4652 	      retval = h.value ();
  4653 	    }
  4654 	  else
  4655 	    {
  4656 	      graphics_handle h = octave_NaN;
  4657 
  4658 	      if (xisnan (val))
  4659 		h = gh_manager::make_graphics_handle ("figure", 0, false);
  4660 	      else if (val > 0 && D_NINT (val) == val)
  4661 		h = gh_manager::make_figure_handle (val);
  4662 	      else
  4663 		error ("__go_figure__: invalid figure number");
  4664 
  4665 	      if (! error_state && h.ok ())
  4666 		{
  4667 		  adopt (0, h);
  4668 
  4669 		  xset (h, args.splice (0, 1));
  4670 		  xcreatefcn (h);
  4671 
  4672 		  retval = h.value ();
  4673 		}
  4674 	      else
  4675 		error ("__go_figure__: failed to create figure handle");
  4676 	    }
  4677 	}
  4678       else
  4679 	error ("__go_figure__: expecting figure number to be double value");
  4680     }
  4681   else
  4682     print_usage ();
  4683 
  4684   return retval;
  4685 }
  4686 
  4687 #define GO_BODY(TYPE) \
  4688   gh_manager::autolock guard; \
  4689  \
  4690   octave_value retval; \
  4691  \
  4692   if (args.length () > 0) \
  4693     retval = make_graphics_object (#TYPE, args); \
  4694   else \
  4695     print_usage (); \
  4696  \
  4697   return retval
  4698 
  4699 DEFUN (__go_axes__, args, ,
  4700   "-*- texinfo -*-\n\
  4701 @deftypefn {Built-in Function} {} __go_axes__ (@var{parent})\n\
  4702 Undocumented internal function.\n\
  4703 @end deftypefn")
  4704 {
  4705   GO_BODY (axes);
  4706 }
  4707 
  4708 DEFUN (__go_line__, args, ,
  4709   "-*- texinfo -*-\n\
  4710 @deftypefn {Built-in Function} {} __go_line__ (@var{parent})\n\
  4711 Undocumented internal function.\n\
  4712 @end deftypefn")
  4713 {
  4714   GO_BODY (line);
  4715 }
  4716 
  4717 DEFUN (__go_text__, args, ,
  4718   "-*- texinfo -*-\n\
  4719 @deftypefn {Built-in Function} {} __go_text__ (@var{parent})\n\
  4720 Undocumented internal function.\n\
  4721 @end deftypefn")
  4722 {
  4723   GO_BODY (text);
  4724 }
  4725 
  4726 DEFUN (__go_image__, args, ,
  4727   "-*- texinfo -*-\n\
  4728 @deftypefn {Built-in Function} {} __go_image__ (@var{parent})\n\
  4729 Undocumented internal function.\n\
  4730 @end deftypefn")
  4731 {
  4732   GO_BODY (image);
  4733 }
  4734 
  4735 DEFUN (__go_surface__, args, ,
  4736   "-*- texinfo -*-\n\
  4737 @deftypefn {Built-in Function} {} __go_surface__ (@var{parent})\n\
  4738 Undocumented internal function.\n\
  4739 @end deftypefn")
  4740 {
  4741   GO_BODY (surface);
  4742 }
  4743 
  4744 DEFUN (__go_patch__, args, ,
  4745   "-*- texinfo -*-\n\
  4746 @deftypefn {Built-in Function} {} __go_patch__ (@var{parent})\n\
  4747 Undocumented internal function.\n\
  4748 @end deftypefn")
  4749 {
  4750   GO_BODY (patch);
  4751 }
  4752 
  4753 DEFUN (__go_hggroup__, args, ,
  4754   "-*- texinfo -*-\n\
  4755 @deftypefn {Built-in Function} {} __go_hggroup__ (@var{parent})\n\
  4756 Undocumented internal function.\n\
  4757 @end deftypefn")
  4758 {
  4759   GO_BODY (hggroup);
  4760 }
  4761 
  4762 DEFUN (__go_delete__, args, ,
  4763   "-*- texinfo -*-\n\
  4764 @deftypefn {Built-in Function} {} __go_delete__ (@var{h})\n\
  4765 Undocumented internal function.\n\
  4766 @end deftypefn")
  4767 {
  4768   gh_manager::autolock guard;
  4769 
  4770   octave_value_list retval;
  4771 
  4772   if (args.length () == 1)
  4773     {
  4774       graphics_handle h = octave_NaN;
  4775 
  4776       const NDArray vals = args (0).array_value ();
  4777 
  4778       if (! error_state)
  4779 	{
  4780 	  // Check is all the handles to delete are valid first
  4781 	  // as callbacks might delete one of the handles we
  4782 	  // later want to delete
  4783 	  for (octave_idx_type i = 0; i < vals.numel (); i++)
  4784 	    {
  4785 	      h = gh_manager::lookup (vals.elem (i));
  4786 
  4787 	      if (! h.ok ())
  4788 		{
  4789 		  error ("delete: invalid graphics object (= %g)", 
  4790 			 vals.elem (i));
  4791 		  break;
  4792 		}
  4793 	    }
  4794 
  4795 	  if (! error_state)
  4796 	    {
  4797 	      for (octave_idx_type i = 0; i < vals.numel (); i++)
  4798 		{
  4799 		  h = gh_manager::lookup (vals.elem (i));
  4800 
  4801 		  if (h.ok ())
  4802 		    {
  4803 		      graphics_object obj = gh_manager::get_object (h);
  4804 
  4805 		      // Don't do recursive deleting, due to callbacks
  4806 		      if (! obj.get_properties ().is_beingdeleted ())
  4807 			{
  4808 			  graphics_handle parent_h = obj.get_parent ();
  4809 
  4810 			  graphics_object parent_obj = 
  4811 			    gh_manager::get_object (parent_h);
  4812 
  4813 			  // NOTE: free the handle before removing it from its
  4814 			  //       parent's children, such that the object's 
  4815 			  //       state is correct when the deletefcn callback
  4816 			  //       is executed
  4817 
  4818 			  gh_manager::free (h);
  4819 
  4820 			  // A callback function might have already deleted 
  4821 			  // the parent
  4822 			  if (parent_obj.valid_object ())
  4823 			    parent_obj.remove_child (h);
  4824 
  4825 			  Vdrawnow_requested = true;
  4826 			}
  4827 		    }
  4828 		}
  4829 	    }
  4830 	}
  4831       else
  4832 	error ("delete: invalid graphics object");
  4833     }
  4834   else
  4835     print_usage ();
  4836 
  4837   return retval;
  4838 }
  4839 
  4840 DEFUN (__go_axes_init__, args, ,
  4841   "-*- texinfo -*-\n\
  4842 @deftypefn {Built-in Function} {} __go_axes_init__ (@var{h}, @var{mode})\n\
  4843 Undocumented internal function.\n\
  4844 @end deftypefn")
  4845 {
  4846   gh_manager::autolock guard;
  4847 
  4848   octave_value retval;
  4849 
  4850   int nargin = args.length ();
  4851 
  4852   std::string mode = "";
  4853 
  4854   if (nargin == 2)
  4855     {
  4856       mode = args(1).string_value ();
  4857 
  4858       if (error_state)
  4859 	return retval;
  4860     }
  4861 
  4862   if (nargin == 1 || nargin == 2)
  4863     {
  4864       graphics_handle h = octave_NaN;
  4865 
  4866       double val = args(0).double_value ();
  4867 
  4868       if (! error_state)
  4869 	{
  4870 	  h = gh_manager::lookup (val);
  4871 
  4872 	  if (h.ok ())
  4873 	    {
  4874 	      graphics_object obj = gh_manager::get_object (h);
  4875 
  4876 	      obj.set_defaults (mode);
  4877 
  4878 	      h = gh_manager::lookup (val);
  4879 	      if (! h.ok ())
  4880 		error ("__go_axes_init__: axis deleted during initialization (= %g)", val);
  4881 	    }
  4882 	  else
  4883 	    error ("__go_axes_init__: invalid graphics object (= %g)", val);
  4884 	}
  4885       else
  4886 	error ("__go_axes_init__: invalid graphics object");
  4887     }
  4888   else
  4889     print_usage ();
  4890 
  4891   return retval;
  4892 }
  4893 
  4894 DEFUN (__go_handles__, , ,
  4895    "-*- texinfo -*-\n\
  4896 @deftypefn {Built-in Function} {} __go_handles__ ()\n\
  4897 Undocumented internal function.\n\
  4898 @end deftypefn")
  4899 {
  4900   gh_manager::autolock guard;
  4901 
  4902   return octave_value (gh_manager::handle_list ());
  4903 }
  4904 
  4905 DEFUN (__go_figure_handles__, , ,
  4906    "-*- texinfo -*-\n\
  4907 @deftypefn {Built-in Function} {} __go_figure_handles__ ()\n\
  4908 Undocumented internal function.\n\
  4909 @end deftypefn")
  4910 {
  4911   gh_manager::autolock guard;
  4912 
  4913   return octave_value (gh_manager::figure_handle_list ());
  4914 }
  4915 
  4916 DEFUN (__go_execute_callback__, args, ,
  4917    "-*- texinfo -*-\n\
  4918 @deftypefn {Built-in Function} {} __go_execute_callback__ (@var{h}, @var{name})\n\
  4919 @deftypefnx {Built-in Function} {} __go_execute_callback__ (@var{h}, @var{name}, @var{param})\n\
  4920 Undocumented internal function.\n\
  4921 @end deftypefn")
  4922 {
  4923   octave_value retval;
  4924 
  4925   int nargin = args.length ();
  4926 
  4927   if (nargin == 2 || nargin == 3)
  4928     {
  4929       double val = args(0).double_value ();
  4930 
  4931       if (! error_state)
  4932 	{
  4933 	  graphics_handle h = gh_manager::lookup (val);
  4934 
  4935 	  if (h.ok ())
  4936 	    {
  4937 	      std::string name = args(1).string_value ();
  4938 
  4939 	      if (! error_state)
  4940 		{
  4941 		  if (nargin == 2)
  4942 		    gh_manager::execute_callback (h, name);
  4943 		  else
  4944 		    gh_manager::execute_callback (h, name, args(2));
  4945 		}
  4946 	      else
  4947 		error ("__go_execute_callback__: invalid callback name");
  4948 	    }
  4949 	  else
  4950 	    error ("__go_execute_callback__: invalid graphics object (= %g)",
  4951 		   val);
  4952 	}
  4953       else
  4954 	error ("__go_execute_callback__: invalid graphics object");
  4955     }
  4956   else
  4957     print_usage ();
  4958 
  4959   return retval;
  4960 }
  4961 
  4962 DEFUN (available_backends, , ,
  4963    "-*- texinfo -*-\n\
  4964 @deftypefn {Built-in Function} {} available_backends ()\n\
  4965 Return a cell array of registered graphics backends.\n\
  4966 @end deftypefn")
  4967 {
  4968   gh_manager::autolock guard;
  4969 
  4970   return octave_value (graphics_backend::available_backends_list ());
  4971 }
  4972 
  4973 static void
  4974 clear_drawnow_request (void *)
  4975 {
  4976   Vdrawnow_requested = false;
  4977 }
  4978 
  4979 DEFUN (drawnow, args, ,
  4980    "-*- texinfo -*-\n\
  4981 @deftypefn  {Built-in Function} {} drawnow ()\n\
  4982 @deftypefnx {Built-in Function} {} drawnow (\"expose\")\n\
  4983 @deftypefnx {Built-in Function} {} drawnow (@var{term}, @var{file}, @var{mono}, @var{debug_file})\n\
  4984 Update figure windows and their children.  The event queue is flushed and\n\
  4985 any callbacks generated are executed.  With the optional argument\n\
  4986 @code{\"expose\"}, only graphic objects are updated and no other events or\n\
  4987 callbacks are processed.\n\
  4988 The third calling form of @code{drawnow} is for debugging and is\n\
  4989 undocumented.\n\
  4990 @end deftypefn")
  4991 {
  4992   static int drawnow_executing = 0;
  4993   static bool __go_close_all_registered__ = false;
  4994 
  4995   octave_value retval;
  4996 
  4997   gh_manager::lock ();
  4998 
  4999   unwind_protect::begin_frame ("Fdrawnow");
  5000   unwind_protect::add (clear_drawnow_request);
  5001 
  5002   unwind_protect_int (drawnow_executing);
  5003 
  5004   if (++drawnow_executing <= 1)
  5005     {
  5006       if (! __go_close_all_registered__)
  5007 	{
  5008 	  octave_add_atexit_function ("__go_close_all__");
  5009 
  5010 	  __go_close_all_registered__ = true;
  5011 	}
  5012 
  5013       if (args.length () == 0 || args.length () == 1)
  5014 	{
  5015 	  Matrix hlist = gh_manager::figure_handle_list ();
  5016 
  5017 	  for (int i = 0; ! error_state && i < hlist.length (); i++)
  5018 	    {
  5019 	      graphics_handle h = gh_manager::lookup (hlist(i));
  5020 
  5021 	      if (h.ok () && h != 0)
  5022 		{
  5023 		  graphics_object go = gh_manager::get_object (h);
  5024 		  figure::properties& fprops = dynamic_cast <figure::properties&> (go.get_properties ());
  5025 
  5026 		  if (fprops.is_modified ())
  5027 		    {
  5028 		      if (fprops.is_visible ())
  5029 			{
  5030 			  gh_manager::unlock ();
  5031 
  5032 			  fprops.get_backend ().redraw_figure (go);
  5033 
  5034 			  gh_manager::lock ();
  5035 			}
  5036 
  5037 		      fprops.set_modified (false);
  5038 		    }
  5039 		}
  5040 	    }
  5041 
  5042 	  bool do_events = true;
  5043 
  5044 	  if (args.length () == 1)
  5045 	    {
  5046 	      caseless_str val (args(0).string_value ());
  5047 
  5048 	      if (! error_state && val.compare ("expose"))
  5049 		do_events = false;
  5050 	      else
  5051 		{
  5052 		  error ("drawnow: invalid argument, expected `expose' as argument");
  5053 		  return retval;
  5054 		}
  5055 	    }
  5056 
  5057 	  if (do_events)
  5058 	    {
  5059 	      gh_manager::unlock ();
  5060 
  5061 	      gh_manager::process_events ();
  5062 
  5063 	      gh_manager::lock ();
  5064 	    }
  5065 	}
  5066       else if (args.length () >= 2 && args.length () <= 4)
  5067 	{
  5068 	  std::string term, file, debug_file;
  5069 	  bool mono;
  5070 
  5071 	  term = args(0).string_value ();
  5072 
  5073 	  if (! error_state)
  5074 	    {
  5075 	      file = args(1).string_value ();
  5076 
  5077 	      if (! error_state)
  5078 		{
  5079 		  size_t pos = file.find_last_of (file_ops::dir_sep_chars ());
  5080 
  5081 		  if (pos != std::string::npos)
  5082 		    {
  5083 		      std::string dirname = file.substr (0, pos+1);
  5084 
  5085 		      file_stat fs (dirname);
  5086 
  5087 		      if (! (fs && fs.is_dir ()))
  5088 			{
  5089 			  error ("drawnow: nonexistent directory `%s'",
  5090 				 dirname.c_str ());
  5091 
  5092 			  return retval;
  5093 			}
  5094 		    }
  5095 
  5096 		  mono = (args.length () >= 3 ? args(2).bool_value () : false);
  5097 
  5098 		  if (! error_state)
  5099 		    {
  5100 		      debug_file = (args.length () > 3 ? args(3).string_value ()
  5101 				    : "");
  5102 
  5103 		      if (! error_state)
  5104 			{
  5105 			  graphics_handle h = gcf ();
  5106 
  5107 			  if (h.ok ())
  5108 			    {
  5109 			      graphics_object go = gh_manager::get_object (h);
  5110 
  5111 			      gh_manager::unlock ();
  5112 
  5113 			      go.get_backend ()
  5114 				.print_figure (go, term, file, mono, debug_file);
  5115 
  5116 			      gh_manager::lock ();
  5117 			    }
  5118 			  else
  5119 			    error ("drawnow: nothing to draw");
  5120 			}
  5121 		      else
  5122 			error ("drawnow: invalid debug_file, expected a string value");
  5123 		    }
  5124 		  else
  5125 		    error ("drawnow: invalid colormode, expected a boolean value");
  5126 		}
  5127 	      else
  5128 		error ("drawnow: invalid file, expected a string value");
  5129 	    }
  5130 	  else
  5131 	    error ("drawnow: invalid terminal, expected a string value");
  5132 	}
  5133       else
  5134 	print_usage ();
  5135     }
  5136 
  5137   unwind_protect::run_frame ("Fdrawnow");
  5138 
  5139   gh_manager::unlock ();
  5140 
  5141   return retval;
  5142 }
  5143 
  5144 DEFUN (addlistener, args, ,
  5145    "-*- texinfo -*-\n\
  5146 @deftypefn {Built-in Function} {} addlistener (@var{h}, @var{prop}, @var{fcn})\n\
  5147 Register @var{fcn} as listener for the property @var{prop} of the graphics\n\
  5148 object @var{h}.  Property listeners are executed (in order of registration)\n\
  5149 when the property is set.  The new value is already available when the\n\
  5150 listeners are executed.\n\
  5151 \n\
  5152 @var{prop} must be a string naming a valid property in @var{h}.\n\
  5153 \n\
  5154 @var{fcn} can be a function handle, a string or a cell array whose first\n\
  5155 element is a function handle.  If @var{fcn} is a function handle, the\n\
  5156 corresponding function should accept at least 2 arguments, that will be\n\
  5157 set to the object handle and the empty matrix respectively.  If @var{fcn}\n\
  5158 is a string, it must be any valid octave expression.  If @var{fcn} is a cell\n\
  5159 array, the first element must be a function handle with the same signature\n\
  5160 as described above.  The next elements of the cell array are passed\n\
  5161 as additional arguments to the function.\n\
  5162 \n\
  5163 Example:\n\
  5164 \n\
  5165 @example\n\
  5166 @group\n\
  5167 function my_listener (h, dummy, p1)\n\
  5168   fprintf (\"my_listener called with p1=%s\\n\", p1);\n\
  5169 endfunction\n\
  5170 \n\
  5171 addlistener (gcf, \"position\", @{@@my_listener, \"my string\"@})\n\
  5172 @end group\n\
  5173 @end example\n\
  5174 \n\
  5175 @end deftypefn")
  5176 {
  5177   gh_manager::autolock guard;
  5178 
  5179   octave_value retval;
  5180 
  5181   if (args.length () == 3)
  5182     {
  5183       double h = args(0).double_value ();
  5184 
  5185       if (! error_state)
  5186 	{
  5187 	  std::string pname = args(1).string_value ();
  5188 
  5189 	  if (! error_state)
  5190 	    {
  5191 	      graphics_handle gh = gh_manager::lookup (h);
  5192 
  5193 	      if (gh.ok ())
  5194 		{
  5195 		  graphics_object go = gh_manager::get_object (gh);
  5196 
  5197 		  go.add_property_listener (pname, args(2), POSTSET);
  5198 		}
  5199 	      else
  5200 		error ("addlistener: invalid graphics object (= %g)",
  5201 		       h);
  5202 	    }
  5203 	  else
  5204 	    error ("addlistener: invalid property name, expected a string value");
  5205 	}
  5206       else
  5207 	error ("addlistener: invalid handle");
  5208     }
  5209   else
  5210     print_usage ();
  5211 
  5212   return retval;
  5213 }
  5214 
  5215 DEFUN (dellistener, args, ,
  5216    "-*- texinfo -*-\n\
  5217 @deftypefn {Built-in Function} {} dellistener (@var{h}, @var{prop}, @var{fcn})\n\
  5218 Remove the registration of @var{fcn} as a listener for the property\n\
  5219 @var{prop} of the graphics object @var{h}.  The function @var{fcn} must\n\
  5220 be the same variable (not just the same value), as was passed to the\n\
  5221 original call to @code{addlistener}.\n\
  5222 \n\
  5223 If @var{fcn} is not defined then all listener functions of @var{prop}\n\
  5224 are removed.\n\
  5225 \n\
  5226 Example:\n\
  5227 \n\
  5228 @example\n\
  5229 @group\n\
  5230 function my_listener (h, dummy, p1)\n\
  5231   fprintf (\"my_listener called with p1=%s\\n\", p1);\n\
  5232 endfunction\n\
  5233 \n\
  5234 c = @{@@my_listener, \"my string\"@};\n\
  5235 addlistener (gcf, \"position\", c);\n\
  5236 dellistener (gcf, \"position\", c);\n\
  5237 @end group\n\
  5238 @end example\n\
  5239 \n\
  5240 @end deftypefn")
  5241 {
  5242   gh_manager::autolock guard;
  5243 
  5244   octave_value retval;
  5245 
  5246   if (args.length () == 3 || args.length () == 2)
  5247     {
  5248       double h = args(0).double_value ();
  5249 
  5250       if (! error_state)
  5251 	{
  5252 	  std::string pname = args(1).string_value ();
  5253 
  5254 	  if (! error_state)
  5255 	    {
  5256 	      graphics_handle gh = gh_manager::lookup (h);
  5257 
  5258 	      if (gh.ok ())
  5259 		{
  5260 		  graphics_object go = gh_manager::get_object (gh);
  5261 
  5262 		  if (args.length () == 2)
  5263 		    go.delete_property_listener (pname, octave_value (), POSTSET);
  5264 		  else
  5265 		    go.delete_property_listener (pname, args(2), POSTSET);
  5266 		}
  5267 	      else
  5268 		error ("dellistener: invalid graphics object (= %g)",
  5269 		       h);
  5270 	    }
  5271 	  else
  5272 	    error ("dellistener: invalid property name, expected a string value");
  5273 	}
  5274       else
  5275 	error ("dellistener: invalid handle");
  5276     }
  5277   else
  5278     print_usage ();
  5279 
  5280   return retval;
  5281 }
  5282 
  5283 DEFUN (addproperty, args, ,
  5284   "-*- texinfo -*-\n\
  5285 @deftypefn {Built-in Function} {} addproperty (@var{name}, @var{h}, @var{type}, [@var{arg}, @dots{}])\n\
  5286 Create a new property named @var{name} in graphics object @var{h}.\n\
  5287 @var{type} determines the type of the property to create.  @var{args}\n\
  5288 usually contains the default value of the property, but additional\n\
  5289 arguments might be given, depending on the type of the property.\n\
  5290 \n\
  5291 The supported property types are:\n\
  5292 \n\
  5293 @table @code\n\
  5294 @item string\n\
  5295 A string property.  @var{arg} contains the default string value.\n\
  5296 @item any\n\
  5297 An un-typed property.  This kind of property can hold any octave\n\
  5298 value.  @var{args} contains the default value.\n\
  5299 @item radio\n\
  5300 A string property with a limited set of accepted values.  The first\n\
  5301 argument must be a string with all accepted values separated by\n\
  5302 a vertical bar ('|').  The default value can be marked by enclosing\n\
  5303 it with a '@{' '@}' pair.  The default value may also be given as\n\
  5304 an optional second string argument.\n\
  5305 @item boolean\n\
  5306 A boolean property.  This property type is equivalent to a radio\n\
  5307 property with \"on|off\" as accepted values.  @var{arg} contains\n\
  5308 the default property value.\n\
  5309 @item double\n\
  5310 A scalar double property.  @var{arg} contains the default value.\n\
  5311 @item handle\n\
  5312 A handle property.  This kind of property holds the handle of a\n\
  5313 graphics object.  @var{arg} contains the default handle value.\n\
  5314 When no default value is given, the property is initialized to\n\
  5315 the empty matrix.\n\
  5316 @item data\n\
  5317 A data (matrix) property.  @var{arg} contains the default data\n\
  5318 value.  When no default value is given, the data is initialized to\n\
  5319 the empty matrix.\n\
  5320 @item color\n\
  5321 A color property.  @var{arg} contains the default color value.\n\
  5322 When no default color is given, the property is set to black.\n\
  5323 An optional second string argument may be given to specify an\n\
  5324 additional set of accepted string values (like a radio property).\n\
  5325 @end table\n\
  5326 \n\
  5327 @var{type} may also be the concatenation of a core object type and\n\
  5328 a valid property name for that object type.  The property created\n\
  5329 then has the same characteristics as the referenced property (type,\n\
  5330 possible values, hidden state@dots{}).  This allows to clone an existing\n\
  5331 property into the graphics object @var{h}.\n\
  5332 \n\
  5333 Examples:\n\
  5334 \n\
  5335 @example\n\
  5336 @group\n\
  5337 addproperty (\"my_property\", gcf, \"string\", \"a string value\");\n\
  5338 addproperty (\"my_radio\", gcf, \"radio\", \"val_1|val_2|@{val_3@}\");\n\
  5339 addproperty (\"my_style\", gcf, \"linelinestyle\", \"--\");\n\
  5340 @end group\n\
  5341 @end example\n\
  5342 \n\
  5343 @end deftypefn")
  5344 {
  5345   gh_manager::autolock guard;
  5346 
  5347   octave_value retval;
  5348 
  5349   if (args.length () >= 3)
  5350     {
  5351       std::string name = args(0).string_value ();
  5352 
  5353       if (! error_state)
  5354 	{
  5355 	  double h = args(1).double_value ();
  5356 
  5357 	  if (! error_state)
  5358 	    {
  5359 	      graphics_handle gh = gh_manager::lookup (h);
  5360 
  5361 	      if (gh.ok ())
  5362 		{
  5363 		  graphics_object go = gh_manager::get_object (gh);
  5364 
  5365 		  std::string type = args(2).string_value ();
  5366 
  5367 		  if (! error_state)
  5368 		    {
  5369 		      if (! go.get_properties ().has_property (name))
  5370 			{
  5371 			  property p = property::create (name, gh, type,
  5372 							 args.splice (0, 3));
  5373 
  5374 			  if (! error_state)
  5375 			    go.get_properties ().insert_property (name, p);
  5376 			}
  5377 		      else
  5378 			error ("addproperty: a `%s' property already exists in the graphics object",
  5379 			       name.c_str ());
  5380 		    }
  5381 		  else
  5382 		    error ("addproperty: invalid property type, expected a string value");
  5383 		}
  5384 	      else
  5385 		error ("addproperty: invalid graphics object (= %g)", h);
  5386 	    }
  5387 	  else
  5388 	    error ("addproperty: invalid handle value");
  5389 	}
  5390       else
  5391 	error ("addproperty: invalid property name, expected a string value");
  5392     }
  5393   else
  5394     print_usage ();
  5395 
  5396   return retval;
  5397 }
  5398 
  5399 octave_value
  5400 get_property_from_handle (double handle, const std::string& property,
  5401 			  const std::string& func)
  5402 {
  5403   gh_manager::autolock guard;
  5404 
  5405   graphics_object obj = gh_manager::get_object (handle);
  5406   octave_value retval;
  5407 
  5408   if (obj)
  5409     {
  5410       caseless_str p = std::string (property);
  5411       retval = obj.get (p);
  5412     }
  5413   else
  5414     error ("%s: invalid handle (= %g)", func.c_str(), handle);
  5415 
  5416   return retval;
  5417 }
  5418 
  5419 bool
  5420 set_property_in_handle (double handle, const std::string& property,
  5421 			const octave_value& arg, const std::string& func)
  5422 {
  5423   gh_manager::autolock guard;
  5424 
  5425   graphics_object obj = gh_manager::get_object (handle);
  5426   int ret = false;
  5427 
  5428   if (obj)
  5429     {
  5430       caseless_str p = std::string (property);
  5431       obj.set (p, arg);
  5432       if (!error_state)
  5433 	ret = true;
  5434     }
  5435   else
  5436     error ("%s: invalid handle (= %g)", func.c_str(), handle);
  5437 
  5438   return ret;
  5439 }
  5440 
  5441 /*
  5442 ;;; Local Variables: ***
  5443 ;;; mode: C++ ***
  5444 ;;; End: ***
  5445 */