graphics.cc

Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (C) 2007-2012 John W. Eaton
00004 
00005 This file is part of Octave.
00006 
00007 Octave is free software; you can redistribute it and/or modify it
00008 under the terms of the GNU General Public License as published by the
00009 Free Software Foundation; either version 3 of the License, or (at your
00010 option) any later version.
00011 
00012 Octave is distributed in the hope that it will be useful, but WITHOUT
00013 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00014 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00015 for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Octave; see the file COPYING.  If not, see
00019 <http://www.gnu.org/licenses/>.
00020 
00021 */
00022 
00023 #ifdef HAVE_CONFIG_H
00024 #include <config.h>
00025 #endif
00026 
00027 #include <cctype>
00028 #include <cfloat>
00029 #include <cstdlib>
00030 #include <ctime>
00031 
00032 #include <algorithm>
00033 #include <list>
00034 #include <map>
00035 #include <set>
00036 #include <string>
00037 #include <sstream>
00038 
00039 #include "cmd-edit.h"
00040 #include "file-ops.h"
00041 #include "file-stat.h"
00042 #include "oct-locbuf.h"
00043 #include "singleton-cleanup.h"
00044 
00045 #include "cutils.h"
00046 #include "defun.h"
00047 #include "display.h"
00048 #include "error.h"
00049 #include "graphics.h"
00050 #include "input.h"
00051 #include "ov.h"
00052 #include "oct-obj.h"
00053 #include "oct-map.h"
00054 #include "ov-fcn-handle.h"
00055 #include "pager.h"
00056 #include "parse.h"
00057 #include "toplev.h"
00058 #include "txt-eng-ft.h"
00059 #include "unwind-prot.h"
00060 
00061 // forward declarations
00062 static octave_value xget (const graphics_handle& h, const caseless_str& name);
00063 
00064 static void
00065 gripe_set_invalid (const std::string& pname)
00066 {
00067   error ("set: invalid value for %s property", pname.c_str ());
00068 }
00069 
00070 // Check to see that PNAME matches just one of PNAMES uniquely.
00071 // Return the full name of the match, or an empty caseless_str object
00072 // if there is no match, or the match is ambiguous.
00073 
00074 static caseless_str
00075 validate_property_name (const std::string& who, const std::string& what,
00076                         const std::set<std::string>& pnames,
00077                         const caseless_str& pname)
00078 {
00079   size_t len = pname.length ();
00080   std::set<std::string> matches;
00081 
00082   for (std::set<std::string>::const_iterator p = pnames.begin ();
00083        p != pnames.end (); p++)
00084     {
00085       if (pname.compare (*p, len))
00086         {
00087           if (len == p->length ())
00088             {
00089               // Exact match.
00090               return pname;
00091             }
00092 
00093           matches.insert (*p);
00094         }
00095     }
00096 
00097   size_t num_matches = matches.size ();
00098 
00099   if (num_matches == 0)
00100     {
00101       error ("%s: unknown %s property %s",
00102              who.c_str (), what.c_str (), pname.c_str ());
00103     }
00104   else if (num_matches > 1)
00105     {
00106       string_vector sv (matches);
00107 
00108       std::ostringstream os;
00109 
00110       sv.list_in_columns (os);
00111 
00112       std::string match_list = os.str ();
00113 
00114       error ("%s: ambiguous %s property name %s; possible matches:\n\n%s",
00115              who.c_str (), what.c_str (), pname.c_str (), match_list.c_str ());
00116     }
00117   else if (num_matches == 1)
00118     {
00119       // Exact match was handled above.
00120 
00121       std::string possible_match = *(matches.begin ());
00122 
00123       warning_with_id ("Octave:abbreviated-property-match",
00124                        "%s: allowing %s to match %s property %s",
00125                        who.c_str (), pname.c_str (), what.c_str (),
00126                        possible_match.c_str ());
00127 
00128       return possible_match;
00129     }
00130 
00131   return caseless_str ();
00132 }
00133 
00134 static Matrix
00135 jet_colormap (void)
00136 {
00137   Matrix cmap (64, 3, 0.0);
00138 
00139   for (octave_idx_type i = 0; i < 64; i++)
00140     {
00141       // This is the jet colormap.  It would be nice to be able
00142       // to feval the jet function but since there is a static
00143       // property object that includes a colormap_property
00144       // object, we need to initialize this before main is even
00145       // called, so calling an interpreted function is not
00146       // possible.
00147 
00148       double x = i / 63.0;
00149 
00150       if (x >= 3.0/8.0 && x < 5.0/8.0)
00151         cmap(i,0) = 4.0 * x - 3.0/2.0;
00152       else if (x >= 5.0/8.0 && x < 7.0/8.0)
00153         cmap(i,0) = 1.0;
00154       else if (x >= 7.0/8.0)
00155         cmap(i,0) = -4.0 * x + 9.0/2.0;
00156 
00157       if (x >= 1.0/8.0 && x < 3.0/8.0)
00158         cmap(i,1) = 4.0 * x - 1.0/2.0;
00159       else if (x >= 3.0/8.0 && x < 5.0/8.0)
00160         cmap(i,1) = 1.0;
00161       else if (x >= 5.0/8.0 && x < 7.0/8.0)
00162         cmap(i,1) = -4.0 * x + 7.0/2.0;
00163 
00164       if (x < 1.0/8.0)
00165         cmap(i,2) = 4.0 * x + 1.0/2.0;
00166       else if (x >= 1.0/8.0 && x < 3.0/8.0)
00167         cmap(i,2) = 1.0;
00168       else if (x >= 3.0/8.0 && x < 5.0/8.0)
00169         cmap(i,2) = -4.0 * x + 5.0/2.0;
00170     }
00171 
00172   return cmap;
00173 }
00174 
00175 static double
00176 default_screendepth (void)
00177 {
00178   return display_info::depth ();
00179 }
00180 
00181 static Matrix
00182 default_screensize (void)
00183 {
00184   Matrix retval (1, 4, 1.0);
00185 
00186   retval(2) = display_info::width ();
00187   retval(3) = display_info::height ();
00188 
00189   return retval;
00190 }
00191 
00192 static double
00193 default_screenpixelsperinch (void)
00194 {
00195   return (display_info::x_dpi () + display_info::y_dpi ()) / 2;
00196 }
00197 
00198 static Matrix
00199 default_colororder (void)
00200 {
00201   Matrix retval (7, 3, 0.0);
00202 
00203   retval(0,2) = 1.0;
00204 
00205   retval(1,1) = 0.5;
00206 
00207   retval(2,0) = 1.0;
00208 
00209   retval(3,1) = 0.75;
00210   retval(3,2) = 0.75;
00211 
00212   retval(4,0) = 0.75;
00213   retval(4,2) = 0.75;
00214 
00215   retval(5,0) = 0.75;
00216   retval(5,1) = 0.75;
00217 
00218   retval(6,0) = 0.25;
00219   retval(6,1) = 0.25;
00220   retval(6,2) = 0.25;
00221 
00222   return retval;
00223 }
00224 
00225 static Matrix
00226 default_lim (bool logscale = false)
00227 {
00228   Matrix m (1, 2, 0);
00229 
00230   if (logscale)
00231     {
00232       m(0) = 0.1;
00233       m(1) = 1.0;
00234     }
00235   else
00236     m(1) = 1;
00237 
00238   return m;
00239 }
00240 
00241 static Matrix
00242 default_data (void)
00243 {
00244   Matrix retval (1, 2);
00245 
00246   retval(0) = 0;
00247   retval(1) = 1;
00248 
00249   return retval;
00250 }
00251 
00252 static Matrix
00253 default_axes_position (void)
00254 {
00255   Matrix m (1, 4, 0.0);
00256   m(0) = 0.13;
00257   m(1) = 0.11;
00258   m(2) = 0.775;
00259   m(3) = 0.815;
00260   return m;
00261 }
00262 
00263 static Matrix
00264 default_axes_outerposition (void)
00265 {
00266   Matrix m (1, 4, 0.0);
00267   m(2) = m(3) = 1.0;
00268   return m;
00269 }
00270 
00271 static Matrix
00272 default_axes_tick (void)
00273 {
00274   Matrix m (1, 6, 0.0);
00275   m(0) = 0.0;
00276   m(1) = 0.2;
00277   m(2) = 0.4;
00278   m(3) = 0.6;
00279   m(4) = 0.8;
00280   m(5) = 1.0;
00281   return m;
00282 }
00283 
00284 static Matrix
00285 default_axes_ticklength (void)
00286 {
00287   Matrix m (1, 2, 0.0);
00288   m(0) = 0.01;
00289   m(1) = 0.025;
00290   return m;
00291 }
00292 
00293 static Matrix
00294 default_figure_position (void)
00295 {
00296   Matrix m (1, 4, 0.0);
00297   m(0) = 300;
00298   m(1) = 200;
00299   m(2) = 560;
00300   m(3) = 420;
00301   return m;
00302 }
00303 
00304 static Matrix
00305 default_figure_papersize (void)
00306 {
00307   Matrix m (1, 2, 0.0);
00308   m(0) = 8.5;
00309   m(1) = 11.0;
00310   return m;
00311 }
00312 
00313 static Matrix
00314 default_figure_paperposition (void)
00315 {
00316   Matrix m (1, 4, 0.0);
00317   m(0) = 0.25;
00318   m(1) = 2.50;
00319   m(2) = 8.00;
00320   m(3) = 6.00;
00321   return m;
00322 }
00323 
00324 static Matrix
00325 default_control_position (void)
00326 {
00327   Matrix retval (1, 4, 0.0);
00328 
00329   retval(0) = 0;
00330   retval(1) = 0;
00331   retval(2) = 80;
00332   retval(3) = 30;
00333 
00334   return retval;
00335 }
00336 
00337 static Matrix
00338 default_control_sliderstep (void)
00339 {
00340   Matrix retval (1, 2, 0.0);
00341 
00342   retval(0) = 0.01;
00343   retval(1) = 0.1;
00344 
00345   return retval;
00346 }
00347 
00348 static Matrix
00349 default_panel_position (void)
00350 {
00351   Matrix retval (1, 4, 0.0);
00352 
00353   retval(0) = 0;
00354   retval(1) = 0;
00355   retval(2) = 0.5;
00356   retval(3) = 0.5;
00357 
00358   return retval;
00359 }
00360 
00361 static double
00362 convert_font_size (double font_size, const caseless_str& from_units,
00363                    const caseless_str& to_units, double parent_height = 0)
00364 {
00365   // Simple case where from_units == to_units
00366 
00367   if (from_units.compare (to_units))
00368     return font_size;
00369 
00370   // Converts the given fontsize using the following transformation:
00371   // <old_font_size> => points => <new_font_size>
00372 
00373   double points_size = 0;
00374   double res = 0;
00375 
00376   if (from_units.compare ("points"))
00377     points_size = font_size;
00378   else
00379     {
00380       res = xget (0, "screenpixelsperinch").double_value ();
00381 
00382       if (from_units.compare ("pixels"))
00383         points_size = font_size * 72.0 / res;
00384       else if (from_units.compare ("inches"))
00385         points_size = font_size * 72.0;
00386       else if (from_units.compare ("centimeters"))
00387         points_size = font_size * 72.0 / 2.54;
00388       else if (from_units.compare ("normalized"))
00389         points_size = font_size * parent_height * 72.0 / res;
00390     }
00391 
00392   double new_font_size = 0;
00393 
00394   if (to_units.compare ("points"))
00395     new_font_size = points_size;
00396   else
00397     {
00398       if (res <= 0)
00399         res = xget (0, "screenpixelsperinch").double_value ();
00400 
00401       if (to_units.compare ("pixels"))
00402         new_font_size = points_size * res / 72.0;
00403       else if (to_units.compare ("inches"))
00404         new_font_size = points_size / 72.0;
00405       else if (to_units.compare ("centimeters"))
00406         new_font_size = points_size * 2.54 / 72.0;
00407       else if (to_units.compare ("normalized"))
00408         {
00409           // Avoid setting font size to (0/0) = NaN
00410 
00411           if (parent_height > 0)
00412             new_font_size = points_size * res / (parent_height * 72.0);
00413         }
00414     }
00415 
00416   return new_font_size;
00417 }
00418 
00419 static Matrix
00420 convert_position (const Matrix& pos, const caseless_str& from_units,
00421                   const caseless_str& to_units, const Matrix& parent_dim)
00422 {
00423   Matrix retval (1, pos.numel ());
00424   double res = 0;
00425   bool is_rectangle = (pos.numel () == 4);
00426   bool is_2d = (pos.numel () == 2);
00427 
00428   if (from_units.compare ("pixels"))
00429     retval = pos;
00430   else if (from_units.compare ("normalized"))
00431     {
00432       retval(0) = pos(0) * parent_dim(0) + 1;
00433       retval(1) = pos(1) * parent_dim(1) + 1;
00434       if (is_rectangle)
00435         {
00436           retval(2) = pos(2) * parent_dim(0);
00437           retval(3) = pos(3) * parent_dim(1);
00438         }
00439       else if (! is_2d)
00440         retval(2) = 0;
00441     }
00442   else if (from_units.compare ("characters"))
00443     {
00444       if (res <= 0)
00445         res = xget (0, "screenpixelsperinch").double_value ();
00446 
00447       double f = 0.0;
00448 
00449       // FIXME -- this assumes the system font is Helvetica 10pt
00450       //          (for which "x" requires 6x12 pixels at 74.951 pixels/inch)
00451       f = 12.0 * res / 74.951;
00452 
00453       if (f > 0)
00454         {
00455           retval(0) = 0.5 * pos(0) * f;
00456           retval(1) = pos(1) * f;
00457           if (is_rectangle)
00458             {
00459               retval(2) = 0.5 * pos(2) * f;
00460               retval(3) = pos(3) * f;
00461             }
00462           else if (! is_2d)
00463             retval(2) = 0;
00464         }
00465     }
00466   else
00467     {
00468       if (res <= 0)
00469         res = xget (0, "screenpixelsperinch").double_value ();
00470 
00471       double f = 0.0;
00472 
00473       if (from_units.compare ("points"))
00474         f = res / 72.0;
00475       else if (from_units.compare ("inches"))
00476         f = res;
00477       else if (from_units.compare ("centimeters"))
00478         f = res / 2.54;
00479 
00480       if (f > 0)
00481         {
00482           retval(0) = pos(0) * f + 1;
00483           retval(1) = pos(1) * f + 1;
00484           if (is_rectangle)
00485             {
00486               retval(2) = pos(2) * f;
00487               retval(3) = pos(3) * f;
00488             }
00489           else if (! is_2d)
00490             retval(2) = 0;
00491         }
00492     }
00493 
00494   if (! to_units.compare ("pixels"))
00495     {
00496       if (to_units.compare ("normalized"))
00497         {
00498           retval(0) = (retval(0) - 1) / parent_dim(0);
00499           retval(1) = (retval(1) - 1) / parent_dim(1);
00500           if (is_rectangle)
00501             {
00502               retval(2) /= parent_dim(0);
00503               retval(3) /= parent_dim(1);
00504             }
00505           else if (! is_2d)
00506             retval(2) = 0;
00507         }
00508       else if (to_units.compare ("characters"))
00509         {
00510           if (res <= 0)
00511             res = xget (0, "screenpixelsperinch").double_value ();
00512 
00513           double f = 0.0;
00514 
00515           f = 12.0 * res / 74.951;
00516 
00517           if (f > 0)
00518             {
00519               retval(0) = 2 * retval(0) / f;
00520               retval(1) = retval(1) / f;
00521               if (is_rectangle)
00522                 {
00523                   retval(2) = 2 * retval(2) / f;
00524                   retval(3) = retval(3) / f;
00525                 }
00526               else if (! is_2d)
00527                 retval(2) = 0;
00528             }
00529         }
00530       else
00531         {
00532           if (res <= 0)
00533             res = xget (0, "screenpixelsperinch").double_value ();
00534 
00535           double f = 0.0;
00536 
00537           if (to_units.compare ("points"))
00538             f = res / 72.0;
00539           else if (to_units.compare ("inches"))
00540             f = res;
00541           else if (to_units.compare ("centimeters"))
00542             f = res / 2.54;
00543 
00544           if (f > 0)
00545             {
00546               retval(0) = (retval(0) - 1) / f;
00547               retval(1) = (retval(1) - 1) / f;
00548               if (is_rectangle)
00549                 {
00550                   retval(2) /= f;
00551                   retval(3) /= f;
00552                 }
00553               else if (! is_2d)
00554                 retval(2) = 0;
00555             }
00556         }
00557     }
00558   else if (! is_rectangle && ! is_2d)
00559     retval(2) = 0;
00560 
00561   return retval;
00562 }
00563 
00564 static Matrix
00565 convert_text_position (const Matrix& pos, const text::properties& props,
00566                        const caseless_str& from_units,
00567                        const caseless_str& to_units)
00568 {
00569   graphics_object go = gh_manager::get_object (props.get___myhandle__ ());
00570   graphics_object ax = go.get_ancestor ("axes");
00571 
00572   Matrix retval;
00573 
00574   if (ax.valid_object ())
00575     {
00576       const axes::properties& ax_props =
00577           dynamic_cast<const axes::properties&> (ax.get_properties ());
00578       graphics_xform ax_xform = ax_props.get_transform ();
00579       bool is_rectangle = (pos.numel () == 4);
00580       Matrix ax_bbox = ax_props.get_boundingbox (true),
00581              ax_size = ax_bbox.extract_n (0, 2, 1, 2);
00582 
00583       if (from_units.compare ("data"))
00584         {
00585           if (is_rectangle)
00586             {
00587               ColumnVector v1 = ax_xform.transform (pos(0), pos(1), 0),
00588                            v2 = ax_xform.transform (pos(0) + pos(2),
00589                                                     pos(1) + pos(3), 0);
00590 
00591               retval.resize (1, 4);
00592 
00593               retval(0) = v1(0) - ax_bbox(0) + 1;
00594               retval(1) = ax_bbox(1) + ax_bbox(3) - v1(1) + 1;
00595               retval(2) = v2(0) - v1(0);
00596               retval(3) = v1(1) - v2(1);
00597             }
00598           else
00599             {
00600               ColumnVector v = ax_xform.transform (pos(0), pos(1), pos(2));
00601 
00602               retval.resize (1, 3);
00603 
00604               retval(0) = v(0) - ax_bbox(0) + 1;
00605               retval(1) = ax_bbox(1) + ax_bbox(3) - v(1) + 1;
00606               retval(2) = 0;
00607             }
00608         }
00609       else
00610         retval = convert_position (pos, from_units, "pixels", ax_size);
00611 
00612       if (! to_units.compare ("pixels"))
00613         {
00614           if (to_units.compare ("data"))
00615             {
00616               if (is_rectangle)
00617                 {
00618                   ColumnVector v1 = ax_xform.untransform (retval(0) + ax_bbox(0) - 1,
00619                                                           ax_bbox(1) + ax_bbox(3)  - retval(1) + 1),
00620                                v2 = ax_xform.untransform (retval(0) + retval(2) + ax_bbox(0) - 1,
00621                                                           ax_bbox(1) + ax_bbox(3)  - (retval(1) + retval(3)) + 1);
00622 
00623                   retval.resize (1, 4);
00624 
00625                   retval(0) = v1(0);
00626                   retval(1) = v1(1);
00627                   retval(2) = v2(0) - v1(0);
00628                   retval(3) = v2(1) - v1(1);
00629                 }
00630               else
00631                 {
00632                   ColumnVector v = ax_xform.untransform (retval(0) + ax_bbox(0) - 1,
00633                                                          ax_bbox(1) + ax_bbox(3)  - retval(1) + 1);
00634 
00635                   retval.resize (1, 3);
00636 
00637                   retval(0) = v(0);
00638                   retval(1) = v(1);
00639                   retval(2) = v(2);
00640                 }
00641             }
00642           else
00643             retval = convert_position (retval, "pixels", to_units, ax_size);
00644         }
00645     }
00646 
00647   return retval;
00648 }
00649 
00650 // This function always returns the screensize in pixels
00651 static Matrix
00652 screen_size_pixels (void)
00653 {
00654   graphics_object obj = gh_manager::get_object (0);
00655   Matrix sz = obj.get ("screensize").matrix_value ();
00656   return convert_position (sz, obj.get ("units").string_value (), "pixels", sz.extract_n (0, 2, 1, 2)).extract_n (0, 2, 1, 2);
00657 }
00658 
00659 static void
00660 convert_cdata_2 (bool is_scaled, double clim_0, double clim_1,
00661                  const double *cmapv, double x, octave_idx_type lda,
00662                  octave_idx_type nc, octave_idx_type i, double *av)
00663 {
00664   if (is_scaled)
00665     x = xround ((nc - 1) * (x - clim_0) / (clim_1 - clim_0));
00666   else
00667     x = xround (x - 1);
00668 
00669   if (xisnan (x))
00670     {
00671       av[i]       = x;
00672       av[i+lda]   = x;
00673       av[i+2*lda] = x;
00674     }
00675   else
00676     {
00677       if (x < 0)
00678         x = 0;
00679       else if (x >= nc)
00680         x = (nc - 1);
00681 
00682       octave_idx_type idx = static_cast<octave_idx_type> (x);
00683 
00684       av[i]       = cmapv[idx];
00685       av[i+lda]   = cmapv[idx+nc];
00686       av[i+2*lda] = cmapv[idx+2*nc];
00687     }
00688 }
00689 
00690 template <class T>
00691 void
00692 convert_cdata_1 (bool is_scaled, double clim_0, double clim_1,
00693                  const double *cmapv, const T *cv, octave_idx_type lda,
00694                  octave_idx_type nc, double *av)
00695 {
00696   for (octave_idx_type i = 0; i < lda; i++)
00697     convert_cdata_2 (is_scaled, clim_0, clim_1, cmapv, cv[i], lda, nc, i, av);
00698 }
00699 
00700 static octave_value
00701 convert_cdata (const base_properties& props, const octave_value& cdata,
00702                bool is_scaled, int cdim)
00703 {
00704   dim_vector dv (cdata.dims ());
00705 
00706   if (dv.length () == cdim && dv(cdim-1) == 3)
00707     return cdata;
00708 
00709   Matrix cmap (1, 3, 0.0);
00710   Matrix clim (1, 2, 0.0);
00711 
00712   graphics_object go = gh_manager::get_object (props.get___myhandle__ ());
00713   graphics_object fig = go.get_ancestor ("figure");
00714 
00715   if (fig.valid_object ())
00716     {
00717       Matrix _cmap = fig.get (caseless_str ("colormap")).matrix_value ();
00718 
00719       if (! error_state)
00720         cmap = _cmap;
00721     }
00722 
00723   if (is_scaled)
00724     {
00725       graphics_object ax = go.get_ancestor ("axes");
00726 
00727       if (ax.valid_object ())
00728         {
00729           Matrix _clim = ax.get (caseless_str ("clim")).matrix_value ();
00730 
00731           if (! error_state)
00732             clim = _clim;
00733         }
00734     }
00735 
00736   dv.resize (cdim);
00737   dv(cdim-1) = 3;
00738 
00739   NDArray a (dv);
00740 
00741   octave_idx_type lda = a.numel () / static_cast<octave_idx_type> (3);
00742   octave_idx_type nc = cmap.rows ();
00743 
00744   double *av = a.fortran_vec ();
00745   const double *cmapv = cmap.data ();
00746 
00747   double clim_0 = clim(0);
00748   double clim_1 = clim(1);
00749 
00750 #define CONVERT_CDATA_1(ARRAY_T, VAL_FN) \
00751   do \
00752     { \
00753       ARRAY_T tmp = cdata. VAL_FN ## array_value (); \
00754  \
00755       convert_cdata_1 (is_scaled, clim_0, clim_1, cmapv, \
00756                        tmp.data (), lda, nc, av); \
00757     } \
00758   while (0)
00759 
00760   if (cdata.is_uint8_type ())
00761     CONVERT_CDATA_1 (uint8NDArray, uint8_);
00762   else if (cdata.is_single_type ())
00763     CONVERT_CDATA_1 (FloatNDArray, float_);
00764   else if (cdata.is_double_type ())
00765     CONVERT_CDATA_1 (NDArray, );
00766   else
00767     error ("unsupported type for cdata (= %s)", cdata.type_name ().c_str ());
00768 
00769 #undef CONVERT_CDATA_1
00770 
00771   return octave_value (a);
00772 }
00773 
00774 template<class T>
00775 static void
00776 get_array_limits (const Array<T>& m, double& emin, double& emax,
00777                   double& eminp, double& emaxp)
00778 {
00779   const T *data = m.data ();
00780   octave_idx_type n = m.numel ();
00781 
00782   for (octave_idx_type i = 0; i < n; i++)
00783     {
00784       double e = double (data[i]);
00785 
00786       // Don't need to test for NaN here as NaN>x and NaN<x is always false
00787       if (! xisinf (e))
00788         {
00789           if (e < emin)
00790             emin = e;
00791 
00792           if (e > emax)
00793             emax = e;
00794 
00795           if (e > 0 && e < eminp)
00796             eminp = e;
00797 
00798           if (e < 0 && e > emaxp)
00799             emaxp = e;
00800         }
00801     }
00802 }
00803 
00804 static bool
00805 lookup_object_name (const caseless_str& name, caseless_str& go_name,
00806                     caseless_str& rest)
00807 {
00808   int len = name.length ();
00809   int offset = 0;
00810   bool result = false;
00811 
00812   if (len >= 4)
00813     {
00814       caseless_str pfx = name.substr (0, 4);
00815 
00816       if (pfx.compare ("axes") || pfx.compare ("line")
00817           || pfx.compare ("text"))
00818         offset = 4;
00819       else if (len >= 5)
00820         {
00821           pfx = name.substr (0, 5);
00822 
00823           if (pfx.compare ("image") || pfx.compare ("patch"))
00824             offset = 5;
00825           else if (len >= 6)
00826             {
00827               pfx = name.substr (0, 6);
00828 
00829               if (pfx.compare ("figure") || pfx.compare ("uimenu"))
00830                 offset = 6;
00831               else if (len >= 7)
00832                 {
00833                   pfx = name.substr (0, 7);
00834 
00835                   if (pfx.compare ("surface") || pfx.compare ("hggroup")
00836                       || pfx.compare ("uipanel"))
00837                     offset = 7;
00838                   else if (len >= 9)
00839                     {
00840                       pfx = name.substr (0, 9);
00841 
00842                       if (pfx.compare ("uicontrol")
00843                           || pfx.compare ("uitoolbar"))
00844                         offset = 9;
00845                       else if (len >= 10)
00846                         {
00847                           pfx = name.substr (0, 10);
00848 
00849                           if (pfx.compare ("uipushtool"))
00850                             offset = 10;
00851                           else if (len >= 12)
00852                             {
00853                               pfx = name.substr (0, 12);
00854 
00855                               if (pfx.compare ("uitoggletool"))
00856                                 offset = 12;
00857                               else if (len >= 13)
00858                                 {
00859                                   pfx = name.substr (0, 13);
00860 
00861                                   if (pfx.compare ("uicontextmenu"))
00862                                     offset = 13;
00863                                 }
00864                             }
00865                         }
00866                     }
00867                 }
00868             }
00869         }
00870 
00871       if (offset > 0)
00872         {
00873           go_name = pfx;
00874           rest = name.substr (offset);
00875           result = true;
00876         }
00877     }
00878 
00879   return result;
00880 }
00881 
00882 static base_graphics_object*
00883 make_graphics_object_from_type (const caseless_str& type,
00884                                 const graphics_handle& h = graphics_handle (),
00885                                 const graphics_handle& p = graphics_handle ())
00886 {
00887   base_graphics_object *go = 0;
00888 
00889   if (type.compare ("figure"))
00890     go = new figure (h, p);
00891   else if (type.compare ("axes"))
00892     go = new axes (h, p);
00893   else if (type.compare ("line"))
00894     go = new line (h, p);
00895   else if (type.compare ("text"))
00896     go = new text (h, p);
00897   else if (type.compare ("image"))
00898     go = new image (h, p);
00899   else if (type.compare ("patch"))
00900     go = new patch (h, p);
00901   else if (type.compare ("surface"))
00902     go = new surface (h, p);
00903   else if (type.compare ("hggroup"))
00904     go = new hggroup (h, p);
00905   else if (type.compare ("uimenu"))
00906     go = new uimenu (h, p);
00907   else if (type.compare ("uicontrol"))
00908     go = new uicontrol (h, p);
00909   else if (type.compare ("uipanel"))
00910     go = new uipanel (h, p);
00911   else if (type.compare ("uicontextmenu"))
00912     go = new uicontextmenu (h, p);
00913   else if (type.compare ("uitoolbar"))
00914     go = new uitoolbar (h, p);
00915   else if (type.compare ("uipushtool"))
00916     go = new uipushtool (h, p);
00917   else if (type.compare ("uitoggletool"))
00918     go = new uitoggletool (h, p);
00919   return go;
00920 }
00921 
00922 // ---------------------------------------------------------------------
00923 
00924 bool
00925 base_property::set (const octave_value& v, bool do_run, bool do_notify_toolkit)
00926 {
00927   if (do_set (v))
00928     {
00929 
00930       // Notify graphics toolkit.
00931       if (id >= 0 && do_notify_toolkit)
00932         {
00933           graphics_object go = gh_manager::get_object (parent);
00934           if (go)
00935             go.update (id);
00936         }
00937 
00938       // run listeners
00939       if (do_run && ! error_state)
00940         run_listeners (POSTSET);
00941 
00942       return true;
00943     }
00944 
00945   return false;
00946 }
00947 
00948 
00949 void
00950 base_property::run_listeners (listener_mode mode)
00951 {
00952   const octave_value_list& l = listeners[mode];
00953 
00954   for (int i = 0; i < l.length (); i++)
00955     {
00956       gh_manager::execute_listener (parent, l(i));
00957 
00958       if (error_state)
00959         break;
00960     }
00961 }
00962 
00963 radio_values::radio_values (const std::string& opt_string)
00964   : default_val (), possible_vals ()
00965 {
00966   size_t beg = 0;
00967   size_t len = opt_string.length ();
00968   bool done = len == 0;
00969 
00970   while (! done)
00971     {
00972       size_t end = opt_string.find ('|', beg);
00973 
00974       if (end == std::string::npos)
00975         {
00976           end = len;
00977           done = true;
00978         }
00979 
00980       std::string t = opt_string.substr (beg, end-beg);
00981 
00982       // Might want more error checking here...
00983       if (t[0] == '{')
00984         {
00985           t = t.substr (1, t.length () - 2);
00986           default_val = t;
00987         }
00988       else if (beg == 0) // ensure default value
00989         default_val = t;
00990 
00991       possible_vals.insert (t);
00992 
00993       beg = end + 1;
00994     }
00995 }
00996 
00997 std::string
00998 radio_values::values_as_string (void) const
00999 {
01000   std::string retval;
01001   for (std::set<caseless_str>::const_iterator it = possible_vals.begin ();
01002        it != possible_vals.end (); it++)
01003     {
01004       if (retval == "")
01005         {
01006           if (*it == default_value ())
01007             retval = "{" + *it + "}";
01008           else
01009             retval = *it;
01010         }
01011       else
01012         {
01013           if (*it == default_value ())
01014             retval += " | {" + *it + "}";
01015           else
01016             retval += " | " + *it;
01017         }
01018     }
01019   if (retval != "")
01020     retval = "[ " + retval + " ]";
01021   return retval;
01022 }
01023 
01024 Cell
01025 radio_values::values_as_cell (void) const
01026 {
01027   octave_idx_type i = 0;
01028   Cell retval (nelem (), 1);
01029   for (std::set<caseless_str>::const_iterator it = possible_vals.begin ();
01030        it != possible_vals.end (); it++)
01031     retval(i++) = std::string (*it);
01032   return retval;
01033 }
01034 
01035 bool
01036 color_values::str2rgb (std::string str)
01037 {
01038   double tmp_rgb[3] = {0, 0, 0};
01039   bool retval = true;
01040   unsigned int len = str.length();
01041 
01042   std::transform (str.begin (), str.end (), str.begin (), tolower);
01043 
01044   if (str.compare(0, len, "blue", 0, len) == 0)
01045     tmp_rgb[2] = 1;
01046   else if (str.compare(0, len, "black", 0, len) == 0
01047            || str.compare(0, len, "k", 0, len) == 0)
01048     tmp_rgb[0] = tmp_rgb[1] = tmp_rgb[2] = 0;
01049   else if (str.compare(0, len, "red", 0, len) == 0)
01050     tmp_rgb[0] = 1;
01051   else if (str.compare(0, len, "green", 0, len) == 0)
01052     tmp_rgb[1] = 1;
01053   else if (str.compare(0, len, "yellow", 0, len) == 0)
01054     tmp_rgb[0] = tmp_rgb[1] = 1;
01055   else if (str.compare(0, len, "magenta", 0, len) == 0)
01056     tmp_rgb[0] = tmp_rgb[2] = 1;
01057   else if (str.compare(0, len, "cyan", 0, len) == 0)
01058     tmp_rgb[1] = tmp_rgb[2] = 1;
01059   else if (str.compare(0, len, "white", 0, len) == 0
01060            || str.compare(0, len, "w", 0, len) == 0)
01061     tmp_rgb[0] = tmp_rgb[1] = tmp_rgb[2] = 1;
01062   else
01063     retval = false;
01064 
01065   if (retval)
01066     {
01067       for (int i = 0; i < 3; i++)
01068         xrgb(i) = tmp_rgb[i];
01069     }
01070 
01071   return retval;
01072 }
01073 
01074 bool
01075 color_property::do_set (const octave_value& val)
01076 {
01077   if (val.is_string ())
01078     {
01079       std::string s = val.string_value ();
01080 
01081       if (! s.empty ())
01082         {
01083           std::string match;
01084 
01085           if (radio_val.contains (s, match))
01086             {
01087               if (current_type != radio_t || match != current_val)
01088                 {
01089                   if (s.length () != match.length ())
01090                     warning_with_id ("Octave:abbreviated-property-match",
01091                                      "%s: allowing %s to match %s value %s",
01092                                      "set", s.c_str (), get_name ().c_str (),
01093                                      match.c_str ());
01094                   current_val = match;
01095                   current_type = radio_t;
01096                   return true;
01097                 }
01098             }
01099           else
01100             {
01101               color_values col (s);
01102               if (! error_state)
01103                 {
01104                   if (current_type != color_t || col != color_val)
01105                     {
01106                       color_val = col;
01107                       current_type = color_t;
01108                       return true;
01109                     }
01110                 }
01111               else
01112                 error ("invalid value for color property \"%s\" (value = %s)",
01113                        get_name ().c_str (), s.c_str ());
01114             }
01115         }
01116       else
01117         error ("invalid value for color property \"%s\"",
01118            get_name ().c_str ());
01119     }
01120   else if (val.is_numeric_type ())
01121     {
01122       Matrix m = val.matrix_value ();
01123 
01124       if (m.numel () == 3)
01125         {
01126           color_values col (m (0), m (1), m(2));
01127           if (! error_state)
01128             {
01129               if (current_type != color_t || col != color_val)
01130                 {
01131                   color_val = col;
01132                   current_type = color_t;
01133                   return true;
01134                 }
01135             }
01136         }
01137       else
01138         error ("invalid value for color property \"%s\"",
01139            get_name ().c_str ());
01140     }
01141   else
01142     error ("invalid value for color property \"%s\"",
01143            get_name ().c_str ());
01144 
01145   return false;
01146 }
01147 
01148 bool
01149 double_radio_property::do_set (const octave_value& val)
01150 {
01151   if (val.is_string ())
01152     {
01153       std::string s = val.string_value ();
01154       std::string match;
01155 
01156       if (! s.empty () && radio_val.contains (s, match))
01157         {
01158           if (current_type != radio_t || match != current_val)
01159             {
01160               if (s.length () != match.length ())
01161                 warning_with_id ("Octave:abbreviated-property-match",
01162                                  "%s: allowing %s to match %s value %s",
01163                                  "set", s.c_str (), get_name ().c_str (),
01164                                  match.c_str ());
01165               current_val = match;
01166               current_type = radio_t;
01167               return true;
01168             }
01169         }
01170       else
01171         error ("invalid value for double_radio property \"%s\"",
01172                get_name ().c_str ());
01173     }
01174   else if (val.is_scalar_type () && val.is_real_type ())
01175     {
01176       double new_dval = val.double_value ();
01177 
01178       if (current_type != double_t || new_dval != dval)
01179         {
01180           dval = new_dval;
01181           current_type = double_t;
01182           return true;
01183         }
01184     }
01185   else
01186     error ("invalid value for double_radio property \"%s\"",
01187            get_name ().c_str ());
01188 
01189   return false;
01190 }
01191 
01192 bool
01193 array_property::validate (const octave_value& v)
01194 {
01195   bool xok = false;
01196 
01197   // FIXME -- should we always support []?
01198   if (v.is_empty () && v.is_numeric_type ())
01199     return true;
01200 
01201   // check value type
01202   if (type_constraints.size () > 0)
01203     {
01204       for (std::list<std::string>::const_iterator it = type_constraints.begin ();
01205            ! xok && it != type_constraints.end (); ++it)
01206         if ((*it) == v.class_name ())
01207           xok = true;
01208     }
01209   else
01210     xok = v.is_numeric_type ();
01211 
01212   if (xok)
01213     {
01214       dim_vector vdims = v.dims ();
01215       int vlen = vdims.length ();
01216 
01217       xok = false;
01218 
01219       // check value size
01220       if (size_constraints.size () > 0)
01221         for (std::list<dim_vector>::const_iterator it = size_constraints.begin ();
01222              ! xok && it != size_constraints.end (); ++it)
01223           {
01224             dim_vector itdims = (*it);
01225 
01226             if (itdims.length () == vlen)
01227               {
01228                 xok = true;
01229 
01230                 for (int i = 0; xok && i < vlen; i++)
01231                   if (itdims(i) >= 0 && itdims(i) != vdims(i))
01232                     xok = false;
01233               }
01234           }
01235       else
01236         return true;
01237     }
01238 
01239   return xok;
01240 }
01241 
01242 bool
01243 array_property::is_equal (const octave_value& v) const
01244 {
01245   if (data.type_name () == v.type_name ())
01246     {
01247       if (data.dims () == v.dims ())
01248         {
01249 
01250 #define CHECK_ARRAY_EQUAL(T,F,A) \
01251             { \
01252               if (data.numel () == 1) \
01253                 return data.F ## scalar_value () == \
01254                   v.F ## scalar_value (); \
01255               else  \
01256                 { \
01257                   /* Keep copy of array_value to allow sparse/bool arrays */ \
01258                   /* that are converted, to not be deallocated early */ \
01259                   const A m1 = data.F ## array_value (); \
01260                   const T* d1 = m1.data (); \
01261                   const A m2 = v.F ## array_value (); \
01262                   const T* d2 = m2.data ();\
01263                   \
01264                   bool flag = true; \
01265                   \
01266                   for (int i = 0; flag && i < data.numel (); i++) \
01267                     if (d1[i] != d2[i]) \
01268                       flag = false; \
01269                   \
01270                   return flag; \
01271                 } \
01272             }
01273 
01274           if (data.is_double_type() || data.is_bool_type ())
01275             CHECK_ARRAY_EQUAL (double, , NDArray)
01276           else if (data.is_single_type ())
01277             CHECK_ARRAY_EQUAL (float, float_, FloatNDArray)
01278           else if (data.is_int8_type ())
01279             CHECK_ARRAY_EQUAL (octave_int8, int8_, int8NDArray)
01280           else if (data.is_int16_type ())
01281             CHECK_ARRAY_EQUAL (octave_int16, int16_, int16NDArray)
01282           else if (data.is_int32_type ())
01283             CHECK_ARRAY_EQUAL (octave_int32, int32_, int32NDArray)
01284           else if (data.is_int64_type ())
01285             CHECK_ARRAY_EQUAL (octave_int64, int64_, int64NDArray)
01286           else if (data.is_uint8_type ())
01287             CHECK_ARRAY_EQUAL (octave_uint8, uint8_, uint8NDArray)
01288           else if (data.is_uint16_type ())
01289             CHECK_ARRAY_EQUAL (octave_uint16, uint16_, uint16NDArray)
01290           else if (data.is_uint32_type ())
01291             CHECK_ARRAY_EQUAL (octave_uint32, uint32_, uint32NDArray)
01292           else if (data.is_uint64_type ())
01293             CHECK_ARRAY_EQUAL (octave_uint64, uint64_, uint64NDArray)
01294         }
01295     }
01296 
01297   return false;
01298 }
01299 
01300 void
01301 array_property::get_data_limits (void)
01302 {
01303   xmin = xminp = octave_Inf;
01304   xmax = xmaxp = -octave_Inf;
01305 
01306   if (! data.is_empty ())
01307     {
01308       if (data.is_integer_type ())
01309         {
01310           if (data.is_int8_type ())
01311             get_array_limits (data.int8_array_value (), xmin, xmax, xminp, xmaxp);
01312           else if (data.is_uint8_type ())
01313             get_array_limits (data.uint8_array_value (), xmin, xmax, xminp, xmaxp);
01314           else if (data.is_int16_type ())
01315             get_array_limits (data.int16_array_value (), xmin, xmax, xminp, xmaxp);
01316           else if (data.is_uint16_type ())
01317             get_array_limits (data.uint16_array_value (), xmin, xmax, xminp, xmaxp);
01318           else if (data.is_int32_type ())
01319             get_array_limits (data.int32_array_value (), xmin, xmax, xminp, xmaxp);
01320           else if (data.is_uint32_type ())
01321             get_array_limits (data.uint32_array_value (), xmin, xmax, xminp, xmaxp);
01322           else if (data.is_int64_type ())
01323             get_array_limits (data.int64_array_value (), xmin, xmax, xminp, xmaxp);
01324           else if (data.is_uint64_type ())
01325             get_array_limits (data.uint64_array_value (), xmin, xmax, xminp, xmaxp);
01326         }
01327       else
01328         get_array_limits (data.array_value (), xmin, xmax, xminp, xmaxp);
01329     }
01330 }
01331 
01332 bool
01333 handle_property::do_set (const octave_value& v)
01334 {
01335   double dv = v.double_value ();
01336 
01337   if (! error_state)
01338     {
01339       graphics_handle gh = gh_manager::lookup (dv);
01340 
01341       if (xisnan (gh.value ()) || gh.ok ())
01342         {
01343           if (current_val != gh)
01344             {
01345               current_val = gh;
01346               return true;
01347             }
01348         }
01349       else
01350         error ("set: invalid graphics handle (= %g) for property \"%s\"",
01351                dv, get_name ().c_str ());
01352     }
01353   else
01354     error ("set: invalid graphics handle for property \"%s\"",
01355            get_name ().c_str ());
01356 
01357   return false;
01358 }
01359 
01360 Matrix
01361 children_property::do_get_children (bool return_hidden) const
01362 {
01363   Matrix retval (children_list.size (), 1);
01364   octave_idx_type k = 0;
01365 
01366   graphics_object go = gh_manager::get_object (0);
01367 
01368   root_figure::properties& props =
01369     dynamic_cast<root_figure::properties&> (go.get_properties ());
01370 
01371   if (! props.is_showhiddenhandles ())
01372     {
01373       for (const_children_list_iterator p = children_list.begin ();
01374            p != children_list.end (); p++)
01375         {
01376           graphics_handle kid = *p;
01377 
01378           if (gh_manager::is_handle_visible (kid))
01379             {
01380               if (! return_hidden)
01381                 retval(k++) = *p;
01382             }
01383           else if (return_hidden)
01384             retval(k++) = *p;
01385         }
01386 
01387       retval.resize (k, 1);
01388     }
01389   else
01390     {
01391       for (const_children_list_iterator p = children_list.begin ();
01392            p != children_list.end (); p++)
01393         retval(k++) = *p;
01394     }
01395 
01396   return retval;
01397 }
01398 
01399 void
01400 children_property::do_delete_children (bool clear)
01401 {
01402   for (children_list_iterator p = children_list.begin ();
01403        p != children_list.end (); p++)
01404     {
01405       graphics_object go = gh_manager::get_object (*p);
01406 
01407       if (go.valid_object ())
01408         gh_manager::free (*p);
01409 
01410     }
01411 
01412   if (clear)
01413     children_list.clear ();
01414 }
01415 
01416 bool
01417 callback_property::validate (const octave_value& v) const
01418 {
01419   // case 1: function handle
01420   // case 2: cell array with first element being a function handle
01421   // case 3: string corresponding to known function name
01422   // case 4: evaluatable string
01423   // case 5: empty matrix
01424 
01425   if (v.is_function_handle ())
01426     return true;
01427   else if (v.is_string ())
01428     // complete validation will be done at execution-time
01429     return true;
01430   else if (v.is_cell () && v.length () > 0
01431            && (v.rows() == 1 || v.columns () == 1)
01432            && v.cell_value ()(0).is_function_handle ())
01433     return true;
01434   else if (v.is_empty ())
01435     return true;
01436 
01437   return false;
01438 }
01439 
01440 // If TRUE, we are executing any callback function, or the functions it
01441 // calls.  Used to determine handle visibility inside callback
01442 // functions.
01443 static bool executing_callback = false;
01444 
01445 void
01446 callback_property::execute (const octave_value& data) const
01447 {
01448   unwind_protect frame;
01449 
01450   // We are executing the callback function associated with this
01451   // callback property.  When set to true, we avoid recursive calls to
01452   // callback routines.
01453   frame.protect_var (executing);
01454 
01455   // We are executing a callback function, so allow handles that have
01456   // their handlevisibility property set to "callback" to be visible.
01457   frame.protect_var (executing_callback);
01458 
01459   if (! executing)
01460     {
01461       executing = true;
01462       executing_callback = true;
01463 
01464       if (callback.is_defined () && ! callback.is_empty ())
01465         gh_manager::execute_callback (get_parent (), callback, data);
01466     }
01467 }
01468 
01469 // Used to cache dummy graphics objects from which dynamic
01470 // properties can be cloned.
01471 static std::map<caseless_str, graphics_object> dprop_obj_map;
01472 
01473 property
01474 property::create (const std::string& name, const graphics_handle& h,
01475                   const caseless_str& type, const octave_value_list& args)
01476 {
01477   property retval;
01478 
01479   if (type.compare ("string"))
01480     {
01481       std::string val = (args.length () > 0 ? args(0).string_value () : "");
01482 
01483       if (! error_state)
01484         retval = property (new string_property (name, h, val));
01485     }
01486   else if (type.compare ("any"))
01487     {
01488       octave_value val =
01489           (args.length () > 0 ? args(0) : octave_value (Matrix ()));
01490 
01491       retval = property (new any_property (name, h, val));
01492     }
01493   else if (type.compare ("radio"))
01494     {
01495       if (args.length () > 0)
01496         {
01497           std::string vals = args(0).string_value ();
01498 
01499           if (! error_state)
01500             {
01501               retval = property (new radio_property (name, h, vals));
01502 
01503               if (args.length () > 1)
01504                 retval.set (args(1));
01505             }
01506           else
01507             error ("addproperty: invalid argument for radio property, expected a string value");
01508         }
01509       else
01510         error ("addproperty: missing possible values for radio property");
01511     }
01512   else if (type.compare ("double"))
01513     {
01514       double d = (args.length () > 0 ? args(0).double_value () : 0);
01515 
01516       if (! error_state)
01517         retval = property (new double_property (name, h, d));
01518     }
01519   else if (type.compare ("handle"))
01520     {
01521       double hh = (args.length () > 0 ? args(0).double_value () : octave_NaN);
01522 
01523       if (! error_state)
01524         {
01525           graphics_handle gh (hh);
01526 
01527           retval = property (new handle_property (name, h, gh));
01528         }
01529     }
01530   else if (type.compare ("boolean"))
01531     {
01532       retval = property (new bool_property (name, h, false));
01533 
01534       if (args.length () > 0)
01535         retval.set (args(0));
01536     }
01537   else if (type.compare ("data"))
01538     {
01539       retval = property (new array_property (name, h, Matrix ()));
01540 
01541       if (args.length () > 0)
01542         {
01543           retval.set (args(0));
01544 
01545           // FIXME -- additional argument could define constraints,
01546           // but is this really useful?
01547         }
01548     }
01549   else if (type.compare ("color"))
01550     {
01551       color_values cv (0, 0, 0);
01552       radio_values rv;
01553 
01554       if (args.length () > 1)
01555         rv = radio_values (args(1).string_value ());
01556 
01557       if (! error_state)
01558         {
01559           retval = property (new color_property (name, h, cv, rv));
01560 
01561           if (! error_state)
01562             {
01563               if (args.length () > 0 && ! args(0).is_empty ())
01564                 retval.set (args(0));
01565               else
01566                 retval.set (rv.default_value ());
01567             }
01568         }
01569     }
01570   else
01571     {
01572       caseless_str go_name, go_rest;
01573 
01574       if (lookup_object_name (type, go_name, go_rest))
01575         {
01576           graphics_object go;
01577 
01578           std::map<caseless_str, graphics_object>::const_iterator it =
01579               dprop_obj_map.find (go_name);
01580 
01581           if (it == dprop_obj_map.end ())
01582             {
01583               base_graphics_object *bgo =
01584                   make_graphics_object_from_type (go_name);
01585 
01586               if (bgo)
01587                 {
01588                   go = graphics_object (bgo);
01589 
01590                   dprop_obj_map[go_name] = go;
01591                 }
01592             }
01593           else
01594             go = it->second;
01595 
01596           if (go.valid_object ())
01597             {
01598               property prop = go.get_properties ().get_property (go_rest);
01599 
01600               if (! error_state)
01601                 {
01602                   retval = prop.clone ();
01603 
01604                   retval.set_parent (h);
01605                   retval.set_name (name);
01606 
01607                   if (args.length () > 0)
01608                     retval.set (args(0));
01609                 }
01610             }
01611           else
01612             error ("addproperty: invalid object type (= %s)",
01613                    go_name.c_str ());
01614         }
01615       else
01616         error ("addproperty: unsupported type for dynamic property (= %s)",
01617                type.c_str ());
01618     }
01619 
01620   return retval;
01621 }
01622 
01623 static void
01624 finalize_r (const graphics_handle& h)
01625 {
01626   graphics_object go = gh_manager::get_object (h);
01627 
01628   if (go)
01629     {
01630       Matrix children = go.get_properties ().get_all_children ();
01631 
01632       for (int k = 0; k < children.numel (); k++)
01633         finalize_r (children(k));
01634 
01635       go.finalize ();
01636     }
01637 }
01638 
01639 static void
01640 initialize_r (const graphics_handle& h)
01641 {
01642   graphics_object go = gh_manager::get_object (h);
01643 
01644   if (go)
01645     {
01646       Matrix children = go.get_properties ().get_all_children ();
01647 
01648       go.initialize ();
01649 
01650       for (int k = 0; k < children.numel (); k++)
01651         initialize_r (children(k));
01652     }
01653 }
01654 
01655 void
01656 figure::properties::set_toolkit (const graphics_toolkit& b)
01657 {
01658   if (toolkit)
01659     finalize_r (get___myhandle__ ());
01660 
01661   toolkit = b;
01662   __graphics_toolkit__ = b.get_name ();
01663   __plot_stream__ = Matrix ();
01664 
01665   if (toolkit)
01666     initialize_r (get___myhandle__ ());
01667 
01668   mark_modified ();
01669 }
01670 
01671 // ---------------------------------------------------------------------
01672 
01673 void
01674 property_list::set (const caseless_str& name, const octave_value& val)
01675 {
01676   size_t offset = 0;
01677 
01678   size_t len = name.length ();
01679 
01680   if (len > 4)
01681     {
01682       caseless_str pfx = name.substr (0, 4);
01683 
01684       if (pfx.compare ("axes") || pfx.compare ("line")
01685           || pfx.compare ("text"))
01686         offset = 4;
01687       else if (len > 5)
01688         {
01689           pfx = name.substr (0, 5);
01690 
01691           if (pfx.compare ("image") || pfx.compare ("patch"))
01692             offset = 5;
01693           else if (len > 6)
01694             {
01695               pfx = name.substr (0, 6);
01696 
01697               if (pfx.compare ("figure") || pfx.compare ("uimenu"))
01698                 offset = 6;
01699               else if (len > 7)
01700                 {
01701                   pfx = name.substr (0, 7);
01702 
01703                   if (pfx.compare ("surface") || pfx.compare ("hggroup")
01704                       || pfx.compare ("uipanel"))
01705                     offset = 7;
01706                   else if (len > 9)
01707                     {
01708                       pfx = name.substr (0, 9);
01709 
01710                       if (pfx.compare ("uicontrol")
01711                           || pfx.compare ("uitoolbar"))
01712                         offset = 9;
01713                       else if (len > 10)
01714                         {
01715                           pfx = name.substr (0, 10);
01716 
01717                           if (pfx.compare ("uipushtool"))
01718                             offset = 10;
01719                           else if (len > 12)
01720                             {
01721                               pfx = name.substr (0, 12);
01722 
01723                               if (pfx.compare ("uitoogletool"))
01724                                 offset = 12;
01725                               else if (len > 13)
01726                                 {
01727                                   pfx = name.substr (0, 13);
01728 
01729                                   if (pfx.compare ("uicontextmenu"))
01730                                     offset = 13;
01731                                 }
01732                             }
01733                         }
01734                     }
01735                 }
01736             }
01737         }
01738 
01739       if (offset > 0)
01740         {
01741           // FIXME -- should we validate property names and values here?
01742 
01743           std::string pname = name.substr (offset);
01744 
01745           std::transform (pfx.begin (), pfx.end (), pfx.begin (), tolower);
01746           std::transform (pname.begin (), pname.end (), pname.begin (), tolower);
01747 
01748           bool has_property = false;
01749           if (pfx == "axes")
01750             has_property = axes::properties::has_core_property (pname);
01751           else if (pfx == "line")
01752             has_property = line::properties::has_core_property (pname);
01753           else if (pfx == "text")
01754             has_property = text::properties::has_core_property (pname);
01755           else if (pfx == "image")
01756             has_property = image::properties::has_core_property (pname);
01757           else if (pfx == "patch")
01758             has_property = patch::properties::has_core_property (pname);
01759           else if (pfx == "figure")
01760             has_property = figure::properties::has_core_property (pname);
01761           else if (pfx == "surface")
01762             has_property = surface::properties::has_core_property (pname);
01763           else if (pfx == "hggroup")
01764             has_property = hggroup::properties::has_core_property (pname);
01765           else if (pfx == "uimenu")
01766             has_property = uimenu::properties::has_core_property (pname);
01767           else if (pfx == "uicontrol")
01768             has_property = uicontrol::properties::has_core_property (pname);
01769           else if (pfx == "uipanel")
01770             has_property = uipanel::properties::has_core_property (pname);
01771           else if (pfx == "uicontextmenu")
01772             has_property = uicontextmenu::properties::has_core_property (pname);
01773           else if (pfx == "uitoolbar")
01774             has_property = uitoolbar::properties::has_core_property (pname);
01775           else if (pfx == "uipushtool")
01776             has_property = uipushtool::properties::has_core_property (pname);
01777 
01778           if (has_property)
01779             {
01780               bool remove = false;
01781               if (val.is_string ())
01782                 {
01783                   caseless_str tval = val.string_value ();
01784 
01785                   remove = tval.compare ("remove");
01786                 }
01787 
01788               pval_map_type& pval_map = plist_map[pfx];
01789 
01790               if (remove)
01791                 {
01792                   pval_map_iterator p = pval_map.find (pname);
01793 
01794                   if (p != pval_map.end ())
01795                     pval_map.erase (p);
01796                 }
01797               else
01798                 pval_map[pname] = val;
01799             }
01800           else
01801             error ("invalid %s property '%s'", pfx.c_str (), pname.c_str ());
01802         }
01803     }
01804 
01805   if (! error_state && offset == 0)
01806     error ("invalid default property specification");
01807 }
01808 
01809 octave_value
01810 property_list::lookup (const caseless_str& name) const
01811 {
01812   octave_value retval;
01813 
01814   size_t offset = 0;
01815 
01816   size_t len = name.length ();
01817 
01818   if (len > 4)
01819     {
01820       caseless_str pfx = name.substr (0, 4);
01821 
01822       if (pfx.compare ("axes") || pfx.compare ("line")
01823           || pfx.compare ("text"))
01824         offset = 4;
01825       else if (len > 5)
01826         {
01827           pfx = name.substr (0, 5);
01828 
01829           if (pfx.compare ("image") || pfx.compare ("patch"))
01830             offset = 5;
01831           else if (len > 6)
01832             {
01833               pfx = name.substr (0, 6);
01834 
01835               if (pfx.compare ("figure") || pfx.compare ("uimenu"))
01836                 offset = 6;
01837               else if (len > 7)
01838                 {
01839                   pfx = name.substr (0, 7);
01840 
01841                   if (pfx.compare ("surface") || pfx.compare ("hggroup")
01842                       || pfx.compare ("uipanel"))
01843                     offset = 7;
01844                   else if (len > 9)
01845                     {
01846                       pfx = name.substr (0, 9);
01847 
01848                       if (pfx.compare ("uicontrol")
01849                           || pfx.compare ("uitoolbar"))
01850                         offset = 9;
01851                       else if (len > 10)
01852                         {
01853                           pfx = name.substr (0, 10);
01854 
01855                           if (pfx.compare ("uipushtool"))
01856                             offset = 10;
01857                           else if (len > 12)
01858                             {
01859                               pfx = name.substr (0, 12);
01860 
01861                               if (pfx.compare ("uitoggletool"))
01862                                 offset = 12;
01863                               else if (len > 13)
01864                                 {
01865                                   pfx = name.substr (0, 13);
01866 
01867                                   if (pfx.compare ("uicontextmenu"))
01868                                     offset = 13;
01869                                 }
01870                             }
01871                         }
01872                     }
01873                 }
01874             }
01875         }
01876 
01877       if (offset > 0)
01878         {
01879           std::string pname = name.substr (offset);
01880 
01881           std::transform (pfx.begin (), pfx.end (), pfx.begin (), tolower);
01882           std::transform (pname.begin (), pname.end (), pname.begin (), tolower);
01883 
01884           plist_map_const_iterator p = find (pfx);
01885 
01886           if (p != end ())
01887             {
01888               const pval_map_type& pval_map = p->second;
01889 
01890               pval_map_const_iterator q = pval_map.find (pname);
01891 
01892               if (q != pval_map.end ())
01893                 retval = q->second;
01894             }
01895         }
01896     }
01897 
01898   return retval;
01899 }
01900 
01901 octave_scalar_map
01902 property_list::as_struct (const std::string& prefix_arg) const
01903 {
01904   octave_scalar_map m;
01905 
01906   for (plist_map_const_iterator p = begin (); p != end (); p++)
01907     {
01908       std::string prefix = prefix_arg + p->first;
01909 
01910       const pval_map_type pval_map = p->second;
01911 
01912       for (pval_map_const_iterator q = pval_map.begin ();
01913            q != pval_map.end ();
01914            q++)
01915         m.assign (prefix + q->first, q->second);
01916     }
01917 
01918   return m;
01919 }
01920 
01921 graphics_handle::graphics_handle (const octave_value& a)
01922   : val (octave_NaN)
01923 {
01924   if (a.is_empty ())
01925     /* do nothing */;
01926   else
01927     {
01928       double tval = a.double_value ();
01929 
01930       if (! error_state)
01931         val = tval;
01932       else
01933         error ("invalid graphics handle");
01934     }
01935 }
01936 
01937 // Set properties given as a cs-list of name, value pairs.
01938 
01939 void
01940 graphics_object::set (const octave_value_list& args)
01941 {
01942   int nargin = args.length ();
01943 
01944   if (nargin == 0)
01945     error ("graphics_object::set: Nothing to set");
01946   else if (nargin % 2 == 0)
01947     {
01948       for (int i = 0; i < nargin; i += 2)
01949         {
01950           caseless_str name = args(i).string_value ();
01951 
01952           if (! error_state)
01953             {
01954               octave_value val = args(i+1);
01955 
01956               set_value_or_default (name, val);
01957 
01958               if (error_state)
01959                 break;
01960             }
01961           else
01962             error ("set: expecting argument %d to be a property name", i);
01963         }
01964     }
01965   else
01966     error ("set: invalid number of arguments");
01967 }
01968 
01969 /*
01970 %!# test set with name, value pairs
01971 %!test
01972 %!  set(gcf, "visible", "off");
01973 %!  h = plot (1:10, 10:-1:1);
01974 %!  set (h, "linewidth", 10, "marker", "x");
01975 %!  assert (get (h, "linewidth"), 10);
01976 %!  assert (get (h, "marker"), "x");
01977 */
01978 
01979 // Set properties given in two cell arrays containing names and values.
01980 void
01981 graphics_object::set (const Array<std::string>& names,
01982                       const Cell& values, octave_idx_type row)
01983 {
01984   if (names.numel () != values.columns ())
01985     {
01986       error("set: number of names must match number of value columns (%d != %d)",
01987             names.numel (), values.columns ());
01988     }
01989 
01990   octave_idx_type k = names.columns ();
01991 
01992   for (octave_idx_type column = 0; column < k; column++)
01993     {
01994       caseless_str name = names(column);
01995       octave_value val  = values(row, column);
01996 
01997       set_value_or_default (name, val);
01998 
01999       if (error_state)
02000         break;
02001     }
02002 }
02003 
02004 /*
02005 %!# test set with cell array arguments
02006 %!test
02007 %!  set (gcf, "visible", "off");
02008 %!  h = plot (1:10, 10:-1:1);
02009 %!  set (h, {"linewidth", "marker"}, {10, "x"});
02010 %!  assert (get(h, "linewidth"), 10);
02011 %!  assert (get(h, "marker"), "x");
02012 
02013 %!# test set with multiple handles and cell array arguments
02014 %!test
02015 %!  set (gcf, "visible", "off");
02016 %!  h = plot (1:10, 10:-1:1, 1:10, 1:10);
02017 %!  set (h, {"linewidth", "marker"}, {10, "x"; 5, "o"});
02018 %!  assert (get (h, "linewidth"), {10; 5});
02019 %!  assert (get (h, "marker"), {"x"; "o"});
02020 %!  set (h, {"linewidth", "marker"}, {10, "x"});
02021 %!  assert (get (h, "linewidth"), {10; 10});
02022 %!  assert (get (h, "marker"), {"x"; "x"});
02023 
02024 %!error <set: number of graphics handles must match number of value rows>
02025 %!  set (gcf, "visible", "off");
02026 %!  h = plot (1:10, 10:-1:1, 1:10, 1:10);
02027 %!  set (h, {"linewidth", "marker"}, {10, "x"; 5, "o"; 7, "."});
02028 
02029 %!error <set: number of names must match number of value columns>
02030 %!  set (gcf, "visible", "off");
02031 %!  h = plot (1:10, 10:-1:1, 1:10, 1:10);
02032 %!  set (h, {"linewidth"}, {10, "x"; 5, "o"});
02033 */
02034 
02035 // Set properties given in a struct array
02036 void
02037 graphics_object::set (const octave_map& m)
02038 {
02039   for (octave_map::const_iterator p = m.begin ();
02040        p != m.end (); p++)
02041     {
02042       caseless_str name  = m.key (p);
02043 
02044       octave_value val = octave_value (m.contents (p).elem (m.numel () - 1));
02045 
02046       set_value_or_default (name, val);
02047 
02048       if (error_state)
02049         break;
02050     }
02051 }
02052 
02053 /*
02054 %!# test set with struct arguments
02055 %!test
02056 %!  set (gcf, "visible", "off");
02057 %!  h = plot (1:10, 10:-1:1);
02058 %!  set (h, struct ("linewidth", 10, "marker", "x"));
02059 %!  assert (get (h, "linewidth"), 10);
02060 %!  assert (get (h, "marker"), "x");
02061 %!  h = plot (1:10, 10:-1:1, 1:10, 1:10);
02062 %!  set (h, struct ("linewidth", {5, 10}));
02063 %!  assert (get(h, "linewidth"), {10; 10});
02064 */
02065 
02066 // Set a property to a value or to its (factory) default value.
02067 
02068 void
02069 graphics_object::set_value_or_default (const caseless_str& name,
02070                                        const octave_value& val)
02071 {
02072   if (val.is_string ())
02073     {
02074       caseless_str tval = val.string_value ();
02075 
02076       octave_value default_val;
02077 
02078       if (tval.compare ("default"))
02079         {
02080           default_val = get_default (name);
02081 
02082           if (error_state)
02083             return;
02084 
02085           rep->set (name, default_val);
02086         }
02087       else if (tval.compare ("factory"))
02088         {
02089           default_val = get_factory_default (name);
02090 
02091           if (error_state)
02092             return;
02093 
02094           rep->set (name, default_val);
02095         }
02096       else
02097         rep->set (name, val);
02098     }
02099   else
02100     rep->set (name, val);
02101 }
02102 
02103 /*
02104 %!# test setting of default values
02105 %!test
02106 %!  set (gcf, "visible", "off");
02107 %!  h = plot (1:10, 10:-1:1);
02108 %!  set (0, "defaultlinelinewidth", 20);
02109 %!  set (h, "linewidth", "default");
02110 %!  assert (get (h, "linewidth"), 20);
02111 %!  set (h, "linewidth", "factory");
02112 %!  assert (get (h, "linewidth"), 0.5);
02113 */
02114 
02115 static double
02116 make_handle_fraction (void)
02117 {
02118   static double maxrand = RAND_MAX + 2.0;
02119 
02120   return (rand () + 1.0) / maxrand;
02121 }
02122 
02123 graphics_handle
02124 gh_manager::do_get_handle (bool integer_figure_handle)
02125 {
02126   graphics_handle retval;
02127 
02128   if (integer_figure_handle)
02129     {
02130       // Figure handles are positive integers corresponding to the
02131       // figure number.
02132 
02133       // We always want the lowest unused figure number.
02134 
02135       retval = 1;
02136 
02137       while (handle_map.find (retval) != handle_map.end ())
02138         retval++;
02139     }
02140   else
02141     {
02142       // Other graphics handles are negative integers plus some random
02143       // fractional part.  To avoid running out of integers, we
02144       // recycle the integer part but tack on a new random part each
02145       // time.
02146 
02147       free_list_iterator p = handle_free_list.begin ();
02148 
02149       if (p != handle_free_list.end ())
02150         {
02151           retval = *p;
02152           handle_free_list.erase (p);
02153         }
02154       else
02155         {
02156           retval = graphics_handle (next_handle);
02157 
02158           next_handle = std::ceil (next_handle) - 1.0 - make_handle_fraction ();
02159         }
02160     }
02161 
02162   return retval;
02163 }
02164 
02165 void
02166 gh_manager::do_free (const graphics_handle& h)
02167 {
02168   if (h.ok ())
02169     {
02170       if (h.value () != 0)
02171         {
02172           iterator p = handle_map.find (h);
02173 
02174           if (p != handle_map.end ())
02175             {
02176               base_properties& bp = p->second.get_properties ();
02177 
02178               bp.set_beingdeleted (true);
02179 
02180               bp.delete_children ();
02181 
02182               octave_value val = bp.get_deletefcn ();
02183 
02184               bp.execute_deletefcn ();
02185 
02186               // Notify graphics toolkit.
02187               p->second.finalize ();
02188 
02189               // Note: this will be valid only for first explicitly
02190               // deleted object.  All its children will then have an
02191               // unknown graphics toolkit.
02192 
02193               // Graphics handles for non-figure objects are negative
02194               // integers plus some random fractional part.  To avoid
02195               // running out of integers, we recycle the integer part
02196               // but tack on a new random part each time.
02197 
02198               handle_map.erase (p);
02199 
02200               if (h.value () < 0)
02201                 handle_free_list.insert (std::ceil (h.value ()) - make_handle_fraction ());
02202             }
02203           else
02204             error ("graphics_handle::free: invalid object %g", h.value ());
02205         }
02206       else
02207         error ("graphics_handle::free: can't delete root figure");
02208     }
02209 }
02210 
02211 void
02212 gh_manager::do_renumber_figure (const graphics_handle& old_gh,
02213                                 const graphics_handle& new_gh)
02214 {
02215   iterator p = handle_map.find (old_gh);
02216 
02217   if (p != handle_map.end ())
02218     {
02219       graphics_object go = p->second;
02220 
02221       handle_map.erase (p);
02222 
02223       handle_map[new_gh] = go;
02224 
02225       if (old_gh.value () < 0)
02226         handle_free_list.insert (std::ceil (old_gh.value ())
02227                                  - make_handle_fraction ());
02228     }
02229   else
02230     error ("graphics_handle::free: invalid object %g", old_gh.value ());
02231 
02232   for (figure_list_iterator q = figure_list.begin ();
02233        q != figure_list.end (); q++)
02234     {
02235       if (*q == old_gh)
02236         {
02237           *q = new_gh;
02238           break;
02239         }
02240     }
02241 }
02242 
02243 gh_manager *gh_manager::instance = 0;
02244 
02245 static void
02246 xset (const graphics_handle& h, const caseless_str& name,
02247       const octave_value& val)
02248 {
02249   graphics_object obj = gh_manager::get_object (h);
02250   obj.set (name, val);
02251 }
02252 
02253 static void
02254 xset (const graphics_handle& h, const octave_value_list& args)
02255 {
02256   if (args.length () > 0)
02257     {
02258       graphics_object obj = gh_manager::get_object (h);
02259       obj.set (args);
02260     }
02261 }
02262 
02263 static octave_value
02264 xget (const graphics_handle& h, const caseless_str& name)
02265 {
02266   graphics_object obj = gh_manager::get_object (h);
02267   return obj.get (name);
02268 }
02269 
02270 static graphics_handle
02271 reparent (const octave_value& ov, const std::string& who,
02272           const std::string& property, const graphics_handle& new_parent,
02273           bool adopt = true)
02274 {
02275   graphics_handle h = octave_NaN;
02276 
02277   double val = ov.double_value ();
02278 
02279   if (! error_state)
02280     {
02281       h = gh_manager::lookup (val);
02282 
02283       if (h.ok ())
02284         {
02285           graphics_object obj = gh_manager::get_object (h);
02286 
02287           graphics_handle parent_h = obj.get_parent ();
02288 
02289           graphics_object parent_obj = gh_manager::get_object (parent_h);
02290 
02291           parent_obj.remove_child (h);
02292 
02293           if (adopt)
02294             obj.set ("parent", new_parent.value ());
02295           else
02296             obj.reparent (new_parent);
02297         }
02298       else
02299         error ("%s: invalid graphics handle (= %g) for %s",
02300                who.c_str (), val, property.c_str ());
02301     }
02302   else
02303     error ("%s: expecting %s to be a graphics handle",
02304            who.c_str (), property.c_str ());
02305 
02306   return h;
02307 }
02308 
02309 // This function is NOT equivalent to the scripting language function gcf.
02310 graphics_handle
02311 gcf (void)
02312 {
02313   octave_value val = xget (0, "currentfigure");
02314 
02315   return val.is_empty () ? octave_NaN : val.double_value ();
02316 }
02317 
02318 // This function is NOT equivalent to the scripting language function gca.
02319 graphics_handle
02320 gca (void)
02321 {
02322   octave_value val = xget (gcf (), "currentaxes");
02323 
02324   return val.is_empty () ? octave_NaN : val.double_value ();
02325 }
02326 
02327 static void
02328 delete_graphics_object (const graphics_handle& h)
02329 {
02330   if (h.ok ())
02331     {
02332       graphics_object obj = gh_manager::get_object (h);
02333 
02334       // Don't do recursive deleting, due to callbacks
02335       if (! obj.get_properties ().is_beingdeleted ())
02336         {
02337           graphics_handle parent_h = obj.get_parent ();
02338 
02339           graphics_object parent_obj =
02340             gh_manager::get_object (parent_h);
02341 
02342           // NOTE: free the handle before removing it from its
02343           //       parent's children, such that the object's
02344           //       state is correct when the deletefcn callback
02345           //       is executed
02346 
02347           gh_manager::free (h);
02348 
02349           // A callback function might have already deleted
02350           // the parent
02351           if (parent_obj.valid_object ())
02352             parent_obj.remove_child (h);
02353 
02354           Vdrawnow_requested = true;
02355         }
02356     }
02357 }
02358 
02359 static void
02360 delete_graphics_object (double val)
02361 {
02362   delete_graphics_object (gh_manager::lookup (val));
02363 }
02364 
02365 static void
02366 delete_graphics_objects (const NDArray vals)
02367 {
02368   for (octave_idx_type i = 0; i < vals.numel (); i++)
02369     delete_graphics_object (vals.elem (i));
02370 }
02371 
02372 static void
02373 close_figure (const graphics_handle& handle)
02374 {
02375   octave_value closerequestfcn = xget (handle, "closerequestfcn");
02376 
02377   OCTAVE_SAFE_CALL (gh_manager::execute_callback, (handle, closerequestfcn));
02378 }
02379 
02380 static void
02381 force_close_figure (const graphics_handle& handle)
02382 {
02383   // Remove the deletefcn and closerequestfcn callbacks and delete the
02384   // object directly.
02385 
02386   xset (handle, "deletefcn", Matrix ());
02387   xset (handle, "closerequestfcn", Matrix ());
02388 
02389   delete_graphics_object (handle);
02390 }
02391 
02392 void
02393 gh_manager::do_close_all_figures (void)
02394 {
02395   // FIXME -- should we process or discard pending events?
02396 
02397   event_queue.clear ();
02398 
02399   // Don't use figure_list_iterator because we'll be removing elements
02400   // from the list elsewhere.
02401 
02402   Matrix hlist = do_figure_handle_list (true);
02403 
02404   for (octave_idx_type i = 0; i < hlist.numel (); i++)
02405     {
02406       graphics_handle h = gh_manager::lookup (hlist(i));
02407 
02408       if (h.ok ())
02409         close_figure (h);
02410     }
02411 
02412   // They should all be closed now.  If not, force them to close.
02413 
02414   hlist = do_figure_handle_list (true);
02415 
02416   for (octave_idx_type i = 0; i < hlist.numel (); i++)
02417     {
02418       graphics_handle h = gh_manager::lookup (hlist(i));
02419 
02420       if (h.ok ())
02421         force_close_figure (h);
02422     }
02423 
02424   // None left now, right?
02425 
02426   hlist = do_figure_handle_list (true);
02427 
02428   assert (hlist.numel () == 0);
02429 
02430   // Clear all callback objects from our list.
02431 
02432   callback_objects.clear ();
02433 }
02434 
02435 static void
02436 adopt (const graphics_handle& p, const graphics_handle& h)
02437 {
02438   graphics_object parent_obj = gh_manager::get_object (p);
02439   parent_obj.adopt (h);
02440 }
02441 
02442 static bool
02443 is_handle (const graphics_handle& h)
02444 {
02445   return h.ok ();
02446 }
02447 
02448 static bool
02449 is_handle (double val)
02450 {
02451   graphics_handle h = gh_manager::lookup (val);
02452 
02453   return h.ok ();
02454 }
02455 
02456 static octave_value
02457 is_handle (const octave_value& val)
02458 {
02459   octave_value retval = false;
02460 
02461   if (val.is_real_scalar () && is_handle (val.double_value ()))
02462     retval = true;
02463   else if (val.is_numeric_type () && val.is_real_type ())
02464     {
02465       const NDArray handles = val.array_value ();
02466 
02467       if (! error_state)
02468         {
02469           boolNDArray result (handles.dims ());
02470 
02471           for (octave_idx_type i = 0; i < handles.numel (); i++)
02472             result.xelem (i) = is_handle (handles (i));
02473 
02474           retval = result;
02475         }
02476     }
02477 
02478   return retval;
02479 }
02480 
02481 static bool
02482 is_figure (double val)
02483 {
02484   graphics_object obj = gh_manager::get_object (val);
02485 
02486   return obj && obj.isa ("figure");
02487 }
02488 
02489 static void
02490 xcreatefcn (const graphics_handle& h)
02491 {
02492   graphics_object obj = gh_manager::get_object (h);
02493   obj.get_properties ().execute_createfcn  ();
02494 }
02495 
02496 static void
02497 xinitialize (const graphics_handle& h)
02498 {
02499   graphics_object go = gh_manager::get_object (h);
02500 
02501   if (go)
02502     go.initialize ();
02503 }
02504 
02505 // ---------------------------------------------------------------------
02506 
02507 void
02508 base_graphics_toolkit::update (const graphics_handle& h, int id)
02509 {
02510   graphics_object go = gh_manager::get_object (h);
02511 
02512   update (go, id);
02513 }
02514 
02515 bool
02516 base_graphics_toolkit::initialize (const graphics_handle& h)
02517 {
02518   graphics_object go = gh_manager::get_object (h);
02519 
02520   return initialize (go);
02521 }
02522 
02523 void
02524 base_graphics_toolkit::finalize (const graphics_handle& h)
02525 {
02526   graphics_object go = gh_manager::get_object (h);
02527 
02528   finalize (go);
02529 }
02530 
02531 // ---------------------------------------------------------------------
02532 
02533 void
02534 base_properties::set_from_list (base_graphics_object& obj,
02535                                 property_list& defaults)
02536 {
02537   std::string go_name = graphics_object_name ();
02538 
02539   property_list::plist_map_const_iterator p = defaults.find (go_name);
02540 
02541   if (p != defaults.end ())
02542     {
02543       const property_list::pval_map_type pval_map = p->second;
02544 
02545       for (property_list::pval_map_const_iterator q = pval_map.begin ();
02546            q != pval_map.end ();
02547            q++)
02548         {
02549           std::string pname = q->first;
02550 
02551           obj.set (pname, q->second);
02552 
02553           if (error_state)
02554             {
02555               error ("error setting default property %s", pname.c_str ());
02556               break;
02557             }
02558         }
02559     }
02560 }
02561 
02562 octave_value
02563 base_properties::get_dynamic (const caseless_str& name) const
02564 {
02565   octave_value retval;
02566 
02567   std::map<caseless_str, property, cmp_caseless_str>::const_iterator it = all_props.find (name);
02568 
02569   if (it != all_props.end ())
02570     retval = it->second.get ();
02571   else
02572     error ("get: unknown property \"%s\"", name.c_str ());
02573 
02574   return retval;
02575 }
02576 
02577 octave_value
02578 base_properties::get_dynamic (bool all) const
02579 {
02580   octave_scalar_map m;
02581 
02582   for (std::map<caseless_str, property, cmp_caseless_str>::const_iterator it = all_props.begin ();
02583        it != all_props.end (); ++it)
02584     if (all || ! it->second.is_hidden ())
02585       m.assign (it->second.get_name (), it->second.get ());
02586 
02587   return m;
02588 }
02589 
02590 std::set<std::string>
02591 base_properties::dynamic_property_names (void) const
02592 {
02593   return dynamic_properties;
02594 }
02595 
02596 bool
02597 base_properties::has_dynamic_property (const std::string& pname)
02598 {
02599   const std::set<std::string>& dynprops = dynamic_property_names ();
02600 
02601   if (dynprops.find (pname) != dynprops.end ())
02602     return true;
02603   else
02604     return all_props.find (pname) != all_props.end ();
02605 }
02606 
02607 void
02608 base_properties::set_dynamic (const caseless_str& pname,
02609                               const octave_value& val)
02610 {
02611   std::map<caseless_str, property, cmp_caseless_str>::iterator it = all_props.find (pname);
02612 
02613   if (it != all_props.end ())
02614     it->second.set (val);
02615   else
02616     error ("set: unknown property \"%s\"", pname.c_str ());
02617 
02618   if (! error_state)
02619     {
02620       dynamic_properties.insert (pname);
02621 
02622       mark_modified ();
02623     }
02624 }
02625 
02626 property
02627 base_properties::get_property_dynamic (const caseless_str& name)
02628 {
02629   std::map<caseless_str, property, cmp_caseless_str>::const_iterator it = all_props.find (name);
02630 
02631   if (it == all_props.end ())
02632     {
02633       error ("get_property: unknown property \"%s\"", name.c_str ());
02634       return property ();
02635     }
02636   else
02637     return it->second;
02638 }
02639 
02640 void
02641 base_properties::set_parent (const octave_value& val)
02642 {
02643   double tmp = val.double_value ();
02644 
02645   graphics_handle new_parent = octave_NaN;
02646 
02647   if (! error_state)
02648     {
02649       new_parent = gh_manager::lookup (tmp);
02650 
02651       if (new_parent.ok ())
02652         {
02653           graphics_object parent_obj = gh_manager::get_object (get_parent ());
02654 
02655           parent_obj.remove_child (__myhandle__);
02656 
02657           parent = new_parent.as_octave_value ();
02658 
02659           ::adopt (parent.handle_value (), __myhandle__);
02660         }
02661       else
02662         error ("set: invalid graphics handle (= %g) for parent", tmp);
02663     }
02664   else
02665     error ("set: expecting parent to be a graphics handle");
02666 }
02667 
02668 void
02669 base_properties::mark_modified (void)
02670 {
02671   __modified__ = "on";
02672   graphics_object parent_obj = gh_manager::get_object (get_parent ());
02673   if (parent_obj)
02674     parent_obj.mark_modified ();
02675 }
02676 
02677 void
02678 base_properties::override_defaults (base_graphics_object& obj)
02679 {
02680   graphics_object parent_obj = gh_manager::get_object (get_parent ());
02681 
02682   if (parent_obj)
02683     parent_obj.override_defaults (obj);
02684 }
02685 
02686 void
02687 base_properties::update_axis_limits (const std::string& axis_type) const
02688 {
02689   graphics_object obj = gh_manager::get_object (__myhandle__);
02690 
02691   if (obj)
02692     obj.update_axis_limits (axis_type);
02693 }
02694 
02695 void
02696 base_properties::update_axis_limits (const std::string& axis_type,
02697                                      const graphics_handle& h) const
02698 {
02699   graphics_object obj = gh_manager::get_object (__myhandle__);
02700 
02701   if (obj)
02702     obj.update_axis_limits (axis_type, h);
02703 }
02704 
02705 bool
02706 base_properties::is_handle_visible (void) const
02707 {
02708   return (handlevisibility.is ("on")
02709           || (executing_callback && ! handlevisibility.is ("off")));
02710 }
02711 
02712 graphics_toolkit
02713 base_properties::get_toolkit (void) const
02714 {
02715   graphics_object go = gh_manager::get_object (get_parent ());
02716 
02717   if (go)
02718     return go.get_toolkit ();
02719   else
02720     return graphics_toolkit ();
02721 }
02722 
02723 void
02724 base_properties::update_boundingbox (void)
02725 {
02726   Matrix kids = get_children ();
02727 
02728   for (int i = 0; i < kids.numel (); i++)
02729     {
02730       graphics_object go = gh_manager::get_object (kids(i));
02731 
02732       if (go.valid_object ())
02733         go.get_properties ().update_boundingbox ();
02734     }
02735 }
02736 
02737 void
02738 base_properties::update_autopos (const std::string& elem_type)
02739 {
02740   graphics_object parent_obj = gh_manager::get_object (get_parent ());
02741 
02742   if (parent_obj.valid_object ())
02743     parent_obj.get_properties ().update_autopos (elem_type);
02744 }
02745 
02746 void
02747 base_properties::add_listener (const caseless_str& nm, const octave_value& v,
02748                                listener_mode mode)
02749 {
02750   property p = get_property (nm);
02751 
02752   if (! error_state && p.ok ())
02753     p.add_listener (v, mode);
02754 }
02755 
02756 void
02757 base_properties::delete_listener (const caseless_str& nm,
02758                                   const octave_value& v, listener_mode mode)
02759 {
02760   property p = get_property (nm);
02761 
02762   if (! error_state && p.ok ())
02763     p.delete_listener (v, mode);
02764 }
02765 
02766 // ---------------------------------------------------------------------
02767 
02768 void
02769 base_graphics_object::update_axis_limits (const std::string& axis_type)
02770 {
02771   if (valid_object ())
02772     {
02773       graphics_object parent_obj = gh_manager::get_object (get_parent ());
02774 
02775       if (parent_obj)
02776         parent_obj.update_axis_limits (axis_type);
02777     }
02778   else
02779     error ("base_graphics_object::update_axis_limits: invalid graphics object");
02780 }
02781 
02782 void
02783 base_graphics_object::update_axis_limits (const std::string& axis_type,
02784                                           const graphics_handle& h)
02785 {
02786   if (valid_object ())
02787     {
02788       graphics_object parent_obj = gh_manager::get_object (get_parent ());
02789 
02790       if (parent_obj)
02791         parent_obj.update_axis_limits (axis_type, h);
02792     }
02793   else
02794     error ("base_graphics_object::update_axis_limits: invalid graphics object");
02795 }
02796 
02797 void
02798 base_graphics_object::remove_all_listeners (void)
02799 {
02800   octave_map m = get (true).map_value ();
02801 
02802   for (octave_map::const_iterator pa = m.begin (); pa != m.end (); pa++)
02803     {
02804       // FIXME -- there has to be a better way.  I think we want to
02805       // ask whether it is OK to delete the listener for the given
02806       // property.  How can we know in advance that it will be OK?
02807 
02808       unwind_protect frame;
02809 
02810       frame.protect_var (error_state);
02811       frame.protect_var (discard_error_messages);
02812       frame.protect_var (Vdebug_on_error);
02813       frame.protect_var (Vdebug_on_warning);
02814 
02815       discard_error_messages = true;
02816       Vdebug_on_error = false;
02817       Vdebug_on_warning = false;
02818 
02819       property p = get_properties ().get_property (pa->first);
02820 
02821       if (! error_state && p.ok ())
02822         p.delete_listener ();
02823     }
02824 }
02825 
02826 std::string
02827 base_graphics_object::values_as_string (void)
02828 {
02829   std::string retval;
02830 
02831   if (valid_object ())
02832     {
02833       octave_map m = get ().map_value ();
02834 
02835       for (octave_map::const_iterator pa = m.begin (); pa != m.end (); pa++)
02836         {
02837           if (pa->first != "children")
02838             {
02839               property p = get_properties ().get_property (pa->first);
02840 
02841               if (p.ok () && ! p.is_hidden ())
02842                 {
02843                   retval += "\n\t" + std::string (pa->first) + ":  ";
02844                   if (p.is_radio ())
02845                     retval += p.values_as_string ();
02846                 }
02847             }
02848         }
02849       if (retval != "")
02850         retval += "\n";
02851     }
02852   else
02853     error ("base_graphics_object::values_as_string: invalid graphics object");
02854 
02855   return retval;
02856 }
02857 
02858 octave_scalar_map
02859 base_graphics_object::values_as_struct (void)
02860 {
02861   octave_scalar_map retval;
02862 
02863   if (valid_object ())
02864     {
02865       octave_scalar_map m = get ().scalar_map_value ();
02866 
02867       for (octave_scalar_map::const_iterator pa = m.begin ();
02868            pa != m.end (); pa++)
02869         {
02870           if (pa->first != "children")
02871             {
02872               property p = get_properties ().get_property (pa->first);
02873 
02874               if (p.ok () && ! p.is_hidden ())
02875                 {
02876                   if (p.is_radio ())
02877                     retval.assign (p.get_name (), p.values_as_cell ());
02878                   else
02879                     retval.assign (p.get_name (), Cell ());
02880                 }
02881             }
02882         }
02883     }
02884   else
02885     error ("base_graphics_object::values_as_struct: invalid graphics object");
02886 
02887   return retval;
02888 }
02889 
02890 graphics_object
02891 graphics_object::get_ancestor (const std::string& obj_type) const
02892 {
02893   if (valid_object ())
02894     {
02895       if (isa (obj_type))
02896         return *this;
02897       else
02898         return gh_manager::get_object (get_parent ()).get_ancestor (obj_type);
02899     }
02900   else
02901     return graphics_object ();
02902 }
02903 
02904 // ---------------------------------------------------------------------
02905 
02906 #include "graphics-props.cc"
02907 
02908 // ---------------------------------------------------------------------
02909 
02910 void
02911 root_figure::properties::set_currentfigure (const octave_value& v)
02912 {
02913   graphics_handle val (v);
02914 
02915   if (error_state)
02916     return;
02917 
02918   if (xisnan (val.value ()) || is_handle (val))
02919     {
02920       currentfigure = val;
02921 
02922       if (val.ok ())
02923         gh_manager::push_figure (val);
02924     }
02925   else
02926     gripe_set_invalid ("currentfigure");
02927 }
02928 
02929 void
02930 root_figure::properties::set_callbackobject (const octave_value& v)
02931 {
02932   graphics_handle val (v);
02933 
02934   if (error_state)
02935     return;
02936 
02937   if (xisnan (val.value ()))
02938     {
02939       if (! cbo_stack.empty ())
02940         {
02941           val = cbo_stack.front ();
02942 
02943           cbo_stack.pop_front ();
02944         }
02945 
02946       callbackobject = val;
02947     }
02948   else if (is_handle (val))
02949     {
02950       if (get_callbackobject ().ok ())
02951         cbo_stack.push_front (get_callbackobject ());
02952 
02953       callbackobject = val;
02954     }
02955   else
02956     gripe_set_invalid ("callbackobject");
02957 }
02958 
02959 void
02960 figure::properties::set_integerhandle (const octave_value& val)
02961 {
02962   if (! error_state)
02963     {
02964       if (integerhandle.set (val, true))
02965         {
02966           bool int_fig_handle = integerhandle.is_on ();
02967 
02968           graphics_object this_go = gh_manager::get_object (__myhandle__);
02969 
02970           graphics_handle old_myhandle = __myhandle__;
02971 
02972           __myhandle__ = gh_manager::get_handle (int_fig_handle);
02973 
02974           gh_manager::renumber_figure (old_myhandle, __myhandle__);
02975 
02976           graphics_object parent_go = gh_manager::get_object (get_parent ());
02977 
02978           base_properties& props = parent_go.get_properties ();
02979 
02980           props.renumber_child (old_myhandle, __myhandle__);
02981 
02982           Matrix kids = get_children ();
02983 
02984           for (octave_idx_type i = 0; i < kids.numel (); i++)
02985             {
02986               graphics_object kid = gh_manager::get_object (kids(i));
02987 
02988               kid.get_properties ().renumber_parent (__myhandle__);
02989             }
02990 
02991           graphics_handle cf = gh_manager::current_figure ();
02992 
02993           if (__myhandle__ == cf)
02994             xset (0, "currentfigure", __myhandle__.value ());
02995 
02996           this_go.update (integerhandle.get_id ());
02997 
02998           mark_modified ();
02999         }
03000     }
03001 }
03002 
03003 // FIXME This should update monitorpositions and pointerlocation, but
03004 // as these properties are yet used, and so it doesn't matter that they
03005 // aren't set yet.
03006 void
03007 root_figure::properties::update_units (void)
03008 {
03009   caseless_str xunits = get_units ();
03010 
03011   Matrix ss = default_screensize ();
03012 
03013   double dpi = get_screenpixelsperinch ();
03014 
03015   if (xunits.compare ("inches"))
03016     {
03017       ss(0) = 0;
03018       ss(1) = 0;
03019       ss(2) /= dpi;
03020       ss(3) /= dpi;
03021     }
03022   else if (xunits.compare ("centimeters"))
03023     {
03024       ss(0) = 0;
03025       ss(1) = 0;
03026       ss(2) *= 2.54 / dpi;
03027       ss(3) *= 2.54 / dpi;
03028     }
03029   else if (xunits.compare ("normalized"))
03030     {
03031       ss = Matrix (1, 4, 1.0);
03032     }
03033   else if (xunits.compare ("points"))
03034     {
03035       ss(0) = 0;
03036       ss(1) = 0;
03037       ss(2) *= 72 / dpi;
03038       ss(3) *= 72 / dpi;
03039     }
03040 
03041   set_screensize (ss);
03042 }
03043 
03044 void
03045 root_figure::properties::remove_child (const graphics_handle& gh)
03046 {
03047   gh_manager::pop_figure (gh);
03048 
03049   graphics_handle cf = gh_manager::current_figure ();
03050 
03051   xset (0, "currentfigure", cf.value ());
03052 
03053   base_properties::remove_child (gh);
03054 }
03055 
03056 property_list
03057 root_figure::factory_properties = root_figure::init_factory_properties ();
03058 
03059 static void
03060 reset_default_properties (property_list& default_properties)
03061 {
03062   property_list new_defaults;
03063 
03064   for (property_list::plist_map_const_iterator p = default_properties.begin ();
03065        p != default_properties.end (); p++)
03066     {
03067       const property_list::pval_map_type pval_map = p->second;
03068       std::string prefix = p->first;
03069 
03070       for (property_list::pval_map_const_iterator q = pval_map.begin ();
03071            q != pval_map.end ();
03072            q++)
03073         {
03074           std::string s = q->first;
03075 
03076           if (prefix == "axes" && (s == "position" || s == "units"))
03077             new_defaults.set (prefix + s, q->second);
03078           else if (prefix == "figure" && (s == "position" || s == "units"
03079                                           || s == "windowstyle"
03080                                           || s == "paperunits"))
03081             new_defaults.set (prefix + s, q->second);
03082         }
03083     }
03084 
03085   default_properties = new_defaults;
03086 }
03087 
03088 void
03089 root_figure::reset_default_properties (void)
03090 {
03091   ::reset_default_properties (default_properties);
03092 }
03093 
03094 // ---------------------------------------------------------------------
03095 
03096 void
03097 figure::properties::set_currentaxes (const octave_value& v)
03098 {
03099   graphics_handle val (v);
03100 
03101   if (error_state)
03102     return;
03103 
03104   if (xisnan (val.value ()) || is_handle (val))
03105     currentaxes = val;
03106   else
03107     gripe_set_invalid ("currentaxes");
03108 }
03109 
03110 void
03111 figure::properties::remove_child (const graphics_handle& gh)
03112 {
03113   base_properties::remove_child (gh);
03114 
03115   if (gh == currentaxes.handle_value ())
03116     {
03117       graphics_handle new_currentaxes;
03118 
03119       Matrix kids = get_children ();
03120 
03121       for (octave_idx_type i = 0; i < kids.numel (); i++)
03122         {
03123           graphics_handle kid = kids(i);
03124 
03125           graphics_object go = gh_manager::get_object (kid);
03126 
03127           if (go.isa ("axes"))
03128             {
03129               new_currentaxes = kid;
03130               break;
03131             }
03132         }
03133 
03134       currentaxes = new_currentaxes;
03135     }
03136 }
03137 
03138 void
03139 figure::properties::set_visible (const octave_value& val)
03140 {
03141   std::string s = val.string_value ();
03142 
03143   if (! error_state)
03144     {
03145       if (s == "on")
03146         xset (0, "currentfigure", __myhandle__.value ());
03147 
03148       visible = val;
03149     }
03150 }
03151 
03152 Matrix
03153 figure::properties::get_boundingbox (bool internal, const Matrix&) const
03154 {
03155   Matrix screen_size = screen_size_pixels ();
03156   Matrix pos = (internal ?
03157                 get_position ().matrix_value () :
03158                 get_outerposition ().matrix_value ());
03159 
03160   pos = convert_position (pos, get_units (), "pixels", screen_size);
03161 
03162   pos(0)--;
03163   pos(1)--;
03164   pos(1) = screen_size(1) - pos(1) - pos(3);
03165 
03166   return pos;
03167 }
03168 
03169 void
03170 figure::properties::set_boundingbox (const Matrix& bb, bool internal,
03171                                      bool do_notify_toolkit)
03172 {
03173   Matrix screen_size = screen_size_pixels ();
03174   Matrix pos = bb;
03175 
03176   pos(1) = screen_size(1) - pos(1) - pos(3);
03177   pos(1)++;
03178   pos(0)++;
03179   pos = convert_position (pos, "pixels", get_units (), screen_size);
03180 
03181   if (internal)
03182     set_position (pos, do_notify_toolkit);
03183   else
03184     set_outerposition (pos, do_notify_toolkit);
03185 }
03186 
03187 Matrix
03188 figure::properties::map_from_boundingbox (double x, double y) const
03189 {
03190   Matrix bb = get_boundingbox (true);
03191   Matrix pos (1, 2, 0);
03192 
03193   pos(0) = x;
03194   pos(1) = y;
03195 
03196   pos(1) = bb(3) - pos(1);
03197   pos(0)++;
03198   pos = convert_position (pos, "pixels", get_units (),
03199                           bb.extract_n (0, 2, 1, 2));
03200 
03201   return pos;
03202 }
03203 
03204 Matrix
03205 figure::properties::map_to_boundingbox (double x, double y) const
03206 {
03207   Matrix bb = get_boundingbox (true);
03208   Matrix pos (1, 2, 0);
03209 
03210   pos(0) = x;
03211   pos(1) = y;
03212 
03213   pos = convert_position (pos, get_units (), "pixels",
03214                           bb.extract_n (0, 2, 1, 2));
03215   pos(0)--;
03216   pos(1) = bb(3) - pos(1);
03217 
03218   return pos;
03219 }
03220 
03221 void
03222 figure::properties::set_position (const octave_value& v,
03223                                   bool do_notify_toolkit)
03224 {
03225   if (! error_state)
03226     {
03227       Matrix old_bb, new_bb;
03228 
03229       old_bb = get_boundingbox ();
03230       position.set (v, true, do_notify_toolkit);
03231       new_bb = get_boundingbox ();
03232 
03233       if (old_bb != new_bb)
03234         {
03235           if (old_bb(2) != new_bb(2) || old_bb(3) != new_bb(3))
03236             {
03237               execute_resizefcn ();
03238               update_boundingbox ();
03239             }
03240         }
03241 
03242       mark_modified ();
03243     }
03244 }
03245 
03246 void
03247 figure::properties::set_outerposition (const octave_value& v,
03248                                        bool do_notify_toolkit)
03249 {
03250   if (! error_state)
03251     {
03252       if (outerposition.set (v, true, do_notify_toolkit))
03253         {
03254           mark_modified ();
03255         }
03256     }
03257 }
03258 
03259 void
03260 figure::properties::set_paperunits (const octave_value& v)
03261 {
03262   if (! error_state)
03263     {
03264       caseless_str typ = get_papertype ();
03265       caseless_str punits = v.string_value ();
03266       if (! error_state)
03267         {
03268           if (punits.compare ("normalized") && typ.compare ("<custom>"))
03269             error ("set: can't set the paperunits to normalized when the papertype is custom");
03270           else
03271             {
03272               caseless_str old_paperunits = get_paperunits ();
03273               if (paperunits.set (v, true))
03274                 {
03275                   update_paperunits (old_paperunits);
03276                   mark_modified ();
03277                 }
03278             }
03279         }
03280     }
03281 }
03282 
03283 void
03284 figure::properties::set_papertype (const octave_value& v)
03285 {
03286   if (! error_state)
03287     {
03288       caseless_str typ = v.string_value ();
03289       caseless_str punits = get_paperunits ();
03290       if (! error_state)
03291         {
03292           if (punits.compare ("normalized") && typ.compare ("<custom>"))
03293             error ("set: can't set the paperunits to normalized when the papertype is custom");
03294           else
03295             {
03296               if (papertype.set (v, true))
03297                 {
03298                   update_papertype ();
03299                   mark_modified ();
03300                 }
03301             }
03302         }
03303     }
03304 }
03305 
03306 static Matrix
03307 papersize_from_type (const caseless_str punits, const caseless_str typ)
03308 {
03309   Matrix ret (1, 2, 1.0);
03310 
03311   if (! punits.compare ("normalized"))
03312     {
03313       double in2units;
03314       double mm2units;
03315 
03316       if (punits.compare ("inches"))
03317         {
03318           in2units = 1.0;
03319           mm2units = 1 / 25.4 ;
03320         }
03321       else if (punits.compare ("centimeters"))
03322         {
03323           in2units = 2.54;
03324           mm2units = 1 / 10.0;
03325         }
03326       else // points
03327         {
03328           in2units = 72.0;
03329           mm2units = 72.0 / 25.4;
03330         }
03331 
03332       if (typ.compare ("usletter"))
03333         {
03334           ret (0) = 8.5 * in2units;
03335           ret (1) = 11.0 * in2units;
03336         }
03337       else if (typ.compare ("uslegal"))
03338         {
03339           ret (0) = 8.5 * in2units;
03340           ret (1) = 14.0 * in2units;
03341         }
03342       else if (typ.compare ("tabloid"))
03343         {
03344           ret (0) = 11.0 * in2units;
03345           ret (1) = 17.0 * in2units;
03346         }
03347       else if (typ.compare ("a0"))
03348         {
03349           ret (0) = 841.0 * mm2units;
03350           ret (1) = 1189.0 * mm2units;
03351         }
03352       else if (typ.compare ("a1"))
03353         {
03354           ret (0) = 594.0 * mm2units;
03355           ret (1) = 841.0 * mm2units;
03356         }
03357       else if (typ.compare ("a2"))
03358         {
03359           ret (0) = 420.0 * mm2units;
03360           ret (1) = 594.0 * mm2units;
03361         }
03362       else if (typ.compare ("a3"))
03363         {
03364           ret (0) = 297.0 * mm2units;
03365           ret (1) = 420.0 * mm2units;
03366         }
03367       else if (typ.compare ("a4"))
03368         {
03369           ret (0) = 210.0 * mm2units;
03370           ret (1) = 297.0 * mm2units;
03371         }
03372       else if (typ.compare ("a5"))
03373         {
03374           ret (0) = 148.0 * mm2units;
03375           ret (1) = 210.0 * mm2units;
03376         }
03377       else if (typ.compare ("b0"))
03378         {
03379           ret (0) = 1029.0 * mm2units;
03380           ret (1) = 1456.0 * mm2units;
03381         }
03382       else if (typ.compare ("b1"))
03383         {
03384           ret (0) = 728.0 * mm2units;
03385           ret (1) = 1028.0 * mm2units;
03386         }
03387       else if (typ.compare ("b2"))
03388         {
03389           ret (0) = 514.0 * mm2units;
03390           ret (1) = 728.0 * mm2units;
03391         }
03392       else if (typ.compare ("b3"))
03393         {
03394           ret (0) = 364.0 * mm2units;
03395           ret (1) = 514.0 * mm2units;
03396         }
03397       else if (typ.compare ("b4"))
03398         {
03399           ret (0) = 257.0 * mm2units;
03400           ret (1) = 364.0 * mm2units;
03401         }
03402       else if (typ.compare ("b5"))
03403         {
03404           ret (0) = 182.0 * mm2units;
03405           ret (1) = 257.0 * mm2units;
03406         }
03407       else if (typ.compare ("arch-a"))
03408         {
03409           ret (0) = 9.0 * in2units;
03410           ret (1) = 12.0 * in2units;
03411         }
03412       else if (typ.compare ("arch-b"))
03413         {
03414           ret (0) = 12.0 * in2units;
03415           ret (1) = 18.0 * in2units;
03416         }
03417       else if (typ.compare ("arch-c"))
03418         {
03419           ret (0) = 18.0 * in2units;
03420           ret (1) = 24.0 * in2units;
03421         }
03422       else if (typ.compare ("arch-d"))
03423         {
03424           ret (0) = 24.0 * in2units;
03425           ret (1) = 36.0 * in2units;
03426         }
03427       else if (typ.compare ("arch-e"))
03428         {
03429           ret (0) = 36.0 * in2units;
03430           ret (1) = 48.0 * in2units;
03431         }
03432       else if (typ.compare ("a"))
03433         {
03434           ret (0) = 8.5 * in2units;
03435           ret (1) = 11.0 * in2units;
03436         }
03437       else if (typ.compare ("b"))
03438         {
03439           ret (0) = 11.0 * in2units;
03440           ret (1) = 17.0 * in2units;
03441         }
03442       else if (typ.compare ("c"))
03443         {
03444           ret (0) = 17.0 * in2units;
03445           ret (1) = 22.0 * in2units;
03446         }
03447       else if (typ.compare ("d"))
03448         {
03449           ret (0) = 22.0 * in2units;
03450           ret (1) = 34.0 * in2units;
03451         }
03452       else if (typ.compare ("e"))
03453         {
03454           ret (0) = 34.0 * in2units;
03455           ret (1) = 43.0 * in2units;
03456         }
03457     }
03458 
03459   return ret;
03460 }
03461 
03462 void
03463 figure::properties::update_paperunits (const caseless_str& old_paperunits)
03464 {
03465   Matrix pos = get_paperposition ().matrix_value ();
03466   Matrix sz = get_papersize ().matrix_value ();
03467 
03468   pos (0) = pos (0) / sz(0);
03469   pos (1) = pos (1) / sz(1);
03470   pos (2) = pos (2) / sz(0);
03471   pos (3) = pos (3) / sz(1);
03472 
03473   caseless_str punits = get_paperunits ();
03474   caseless_str typ = get_papertype ();
03475 
03476   if (typ.compare ("<custom>"))
03477     {
03478       if (old_paperunits.compare ("centimeters"))
03479         {
03480           sz (0) = sz (0) / 2.54;
03481           sz (1) = sz (1) / 2.54;
03482         }
03483       else if (old_paperunits.compare ("points"))
03484         {
03485           sz (0) = sz (0) / 72.0;
03486           sz (1) = sz (1) / 72.0;
03487         }
03488 
03489       if (punits.compare ("centimeters"))
03490         {
03491           sz(0) = sz(0) * 2.54;
03492           sz(1) = sz(1) * 2.54;
03493         }
03494       else if (old_paperunits.compare ("points"))
03495         {
03496           sz (0) = sz (0) * 72.0;
03497           sz (1) = sz (1) * 72.0;
03498         }
03499     }
03500   else
03501     sz = papersize_from_type (punits, typ);
03502 
03503   pos (0) = pos (0) * sz(0);
03504   pos (1) = pos (1) * sz(1);
03505   pos (2) = pos (2) * sz(0);
03506   pos (3) = pos (3) * sz(1);
03507 
03508   papersize.set (octave_value (sz));
03509   paperposition.set (octave_value (pos));
03510 }
03511 
03512 void
03513 figure::properties::update_papertype (void)
03514 {
03515   caseless_str typ = get_papertype ();
03516 
03517   if (! typ.compare ("<custom>"))
03518     // Call papersize.set rather than set_papersize to avoid loops between
03519     // update_papersize and update_papertype
03520     papersize.set (octave_value (papersize_from_type (get_paperunits (), typ)));
03521 }
03522 
03523 void
03524 figure::properties::update_papersize (void)
03525 {
03526   papertype.set ("<custom>");
03527 }
03528 
03529 void
03530 figure::properties::set_units (const octave_value& v)
03531 {
03532   if (! error_state)
03533     {
03534       caseless_str old_units = get_units ();
03535       if (units.set (v, true))
03536         {
03537           update_units (old_units);
03538           mark_modified ();
03539         }
03540     }
03541 }
03542 
03543 void
03544 figure::properties::update_units (const caseless_str& old_units)
03545 {
03546   set_position (convert_position (get_position ().matrix_value (), old_units,
03547                                   get_units (), screen_size_pixels ()));
03548 }
03549 
03550 std::string
03551 figure::properties::get_title (void) const
03552 {
03553   if (is_numbertitle ())
03554     {
03555       std::ostringstream os;
03556       std::string nm = get_name ();
03557 
03558       os << "Figure " << __myhandle__.value ();
03559       if (! nm.empty ())
03560         os << ": " << get_name ();
03561 
03562       return os.str ();
03563     }
03564   else
03565     return get_name ();
03566 }
03567 
03568 octave_value
03569 figure::get_default (const caseless_str& name) const
03570 {
03571   octave_value retval = default_properties.lookup (name);
03572 
03573   if (retval.is_undefined ())
03574     {
03575       graphics_handle parent = get_parent ();
03576       graphics_object parent_obj = gh_manager::get_object (parent);
03577 
03578       retval = parent_obj.get_default (name);
03579     }
03580 
03581   return retval;
03582 }
03583 
03584 void
03585 figure::reset_default_properties (void)
03586 {
03587   ::reset_default_properties (default_properties);
03588 }
03589 
03590 // ---------------------------------------------------------------------
03591 
03592 void
03593 axes::properties::init (void)
03594 {
03595   position.add_constraint (dim_vector (1, 4));
03596   position.add_constraint (dim_vector (0, 0));
03597   outerposition.add_constraint (dim_vector (1, 4));
03598   colororder.add_constraint (dim_vector (-1, 3));
03599   dataaspectratio.add_constraint (dim_vector (1, 3));
03600   plotboxaspectratio.add_constraint (dim_vector (1, 3));
03601   xlim.add_constraint (2);
03602   ylim.add_constraint (2);
03603   zlim.add_constraint (2);
03604   clim.add_constraint (2);
03605   alim.add_constraint (2);
03606   xtick.add_constraint (dim_vector (1, -1));
03607   ytick.add_constraint (dim_vector (1, -1));
03608   ztick.add_constraint (dim_vector (1, -1));
03609   Matrix vw (1, 2, 0);
03610   vw(1) = 90;
03611   view = vw;
03612   view.add_constraint (dim_vector (1, 2));
03613   cameraposition.add_constraint (dim_vector (1, 3));
03614   Matrix upv (1, 3, 0.0);
03615   upv(2) = 1.0;
03616   cameraupvector = upv;
03617   cameraupvector.add_constraint (dim_vector (1, 3));
03618   currentpoint.add_constraint (dim_vector (2, 3));
03619   ticklength.add_constraint (dim_vector (1, 2));
03620   tightinset.add_constraint (dim_vector (1, 4));
03621   looseinset.add_constraint (dim_vector (1, 4));
03622   update_font ();
03623 
03624   x_zlim.resize (1, 2);
03625 
03626   sx = "linear";
03627   sy = "linear";
03628   sz = "linear";
03629 
03630   calc_ticklabels (xtick, xticklabel, xscale.is ("log"));
03631   calc_ticklabels (ytick, yticklabel, yscale.is ("log"));
03632   calc_ticklabels (ztick, zticklabel, zscale.is ("log"));
03633 
03634   xset (xlabel.handle_value (), "handlevisibility", "off");
03635   xset (ylabel.handle_value (), "handlevisibility", "off");
03636   xset (zlabel.handle_value (), "handlevisibility", "off");
03637   xset (title.handle_value (), "handlevisibility", "off");
03638 
03639   xset (xlabel.handle_value (), "horizontalalignment", "center");
03640   xset (xlabel.handle_value (), "horizontalalignmentmode", "auto");
03641   xset (ylabel.handle_value (), "horizontalalignment", "center");
03642   xset (ylabel.handle_value (), "horizontalalignmentmode", "auto");
03643   xset (zlabel.handle_value (), "horizontalalignment", "right");
03644   xset (zlabel.handle_value (), "horizontalalignmentmode", "auto");
03645   xset (title.handle_value (), "horizontalalignment", "center");
03646   xset (title.handle_value (), "horizontalalignmentmode", "auto");
03647 
03648   xset (xlabel.handle_value (), "verticalalignment", "cap");
03649   xset (xlabel.handle_value (), "verticalalignmentmode", "auto");
03650   xset (ylabel.handle_value (), "verticalalignment", "bottom");
03651   xset (ylabel.handle_value (), "verticalalignmentmode", "auto");
03652   xset (title.handle_value (), "verticalalignment", "bottom");
03653   xset (title.handle_value (), "verticalalignmentmode", "auto");
03654 
03655   xset (ylabel.handle_value (), "rotation", 90.0);
03656   xset (ylabel.handle_value (), "rotationmode", "auto");
03657 
03658   xset (zlabel.handle_value (), "visible", "off");
03659 
03660   xset (xlabel.handle_value (), "clipping", "off");
03661   xset (ylabel.handle_value (), "clipping", "off");
03662   xset (zlabel.handle_value (), "clipping", "off");
03663   xset (title.handle_value (), "clipping", "off");
03664 
03665   xset (xlabel.handle_value (), "autopos_tag", "xlabel");
03666   xset (ylabel.handle_value (), "autopos_tag", "ylabel");
03667   xset (zlabel.handle_value (), "autopos_tag", "zlabel");
03668   xset (title.handle_value (), "autopos_tag", "title");
03669 
03670   adopt (xlabel.handle_value ());
03671   adopt (ylabel.handle_value ());
03672   adopt (zlabel.handle_value ());
03673   adopt (title.handle_value ());
03674 
03675   Matrix tlooseinset = default_axes_position ();
03676   tlooseinset(2) = 1-tlooseinset(0)-tlooseinset(2);
03677   tlooseinset(3) = 1-tlooseinset(1)-tlooseinset(3);
03678   looseinset = tlooseinset;
03679 }
03680 
03681 Matrix
03682 axes::properties::calc_tightbox (const Matrix& init_pos)
03683 {
03684   Matrix pos = init_pos;
03685   graphics_object obj = gh_manager::get_object (get_parent ());
03686   Matrix parent_bb = obj.get_properties ().get_boundingbox (true);
03687   Matrix ext = get_extent (true, true);
03688   ext(1) = parent_bb(3) - ext(1) - ext(3);
03689   ext(0)++;
03690   ext(1)++;
03691   ext = convert_position (ext, "pixels", get_units (),
03692                           parent_bb.extract_n (0, 2, 1, 2));
03693   if (ext(0) < pos(0))
03694     {
03695       pos(2) += pos(0)-ext(0);
03696       pos(0) = ext(0);
03697     }
03698   if (ext(0)+ext(2) > pos(0)+pos(2))
03699     pos(2) = ext(0)+ext(2)-pos(0);
03700 
03701   if (ext(1) < pos(1))
03702     {
03703       pos(3) += pos(1)-ext(1);
03704       pos(1) = ext(1);
03705     }
03706   if (ext(1)+ext(3) > pos(1)+pos(3))
03707     pos(3) = ext(1)+ext(3)-pos(1);
03708   return pos;
03709 }
03710 
03711 void
03712 axes::properties::sync_positions (void)
03713 {
03714   Matrix ref_linset = looseinset.get ().matrix_value ();
03715   if (autopos_tag_is ("subplot"))
03716     {
03717       graphics_object parent_obj = gh_manager::get_object (get_parent ());
03718       if (parent_obj.isa ("figure"))
03719         {
03720            // FIXME: temporarily changed units should be protected
03721            //        from interrupts
03722            std::string fig_units = parent_obj.get ("units").string_value ();
03723            parent_obj.set ("units", "pixels");
03724 
03725            Matrix ref_outbox = outerposition.get ().matrix_value ();
03726            ref_outbox(2) += ref_outbox(0);
03727            ref_outbox(3) += ref_outbox(1);
03728 
03729            // Find those subplots that are left, right, bottom and top aligned
03730            // with the current subplot
03731            Matrix kids = parent_obj.get_properties ().get_children ();
03732            std::vector<octave_value> aligned;
03733            std::vector<bool> l_aligned, b_aligned, r_aligned, t_aligned;
03734            for (octave_idx_type i = 0; i < kids.numel (); i++)
03735              {
03736                graphics_object go = gh_manager::get_object (kids(i));
03737                if (go.isa ("axes"))
03738                  {
03739                    axes::properties& props =
03740                      dynamic_cast<axes::properties&> (go.get_properties ());
03741                    if (props.autopos_tag_is("subplot"))
03742                      {
03743                        Matrix outpos = go.get ("outerposition").matrix_value ();
03744                        bool l_align=(std::abs (outpos(0)-ref_outbox(0)) < 1e-15);
03745                        bool b_align=(std::abs (outpos(1)-ref_outbox(1)) < 1e-15);
03746                        bool r_align=(std::abs (outpos(0)+outpos(2)-ref_outbox(2)) < 1e-15);
03747                        bool t_align=(std::abs (outpos(1)+outpos(3)-ref_outbox(3)) < 1e-15);
03748                        if (l_align || b_align || r_align || t_align)
03749                          {
03750                            aligned.push_back(kids(i));
03751                            l_aligned.push_back(l_align);
03752                            b_aligned.push_back(b_align);
03753                            r_aligned.push_back(r_align);
03754                            t_aligned.push_back(t_align);
03755                            // FIXME: the temporarily deleted tags should be
03756                            //        protected from interrupts
03757                            props.set_autopos_tag ("none");
03758                          }
03759                      }
03760                  }
03761              }
03762            // Determine a minimum box which aligns the subplots
03763            Matrix ref_box(1, 4, 0.);
03764            ref_box(2) = 1.;
03765            ref_box(3) = 1.;
03766            for (size_t i = 0; i < aligned.size (); i++)
03767              {
03768                graphics_object go = gh_manager::get_object (aligned[i]);
03769                axes::properties& props =
03770                  dynamic_cast<axes::properties&> (go.get_properties ());
03771                Matrix linset = props.get_looseinset ().matrix_value ();
03772                if (l_aligned[i])
03773                  linset(0) = std::min (0., linset(0)-0.01);
03774                if (b_aligned[i])
03775                  linset(1) = std::min (0., linset(1)-0.01);
03776                if (r_aligned[i])
03777                  linset(2) = std::min (0., linset(2)-0.01);
03778                if (t_aligned[i])
03779                  linset(3) = std::min (0., linset(3)-0.01);
03780                props.set_looseinset (linset);
03781                Matrix pos = props.get_position ().matrix_value ();
03782                if (l_aligned[i])
03783                  ref_box(0) = std::max (ref_box(0), pos(0));
03784                if (b_aligned[i])
03785                  ref_box(1) = std::max (ref_box(1), pos(1));
03786                if (r_aligned[i])
03787                  ref_box(2) = std::min (ref_box(2), pos(0)+pos(2));
03788                if (t_aligned[i])
03789                  ref_box(3) = std::min (ref_box(3), pos(1)+pos(3));
03790              }
03791            // Set common looseinset values for all aligned subplots and
03792            // revert their tag values
03793            for (size_t i = 0; i < aligned.size (); i++)
03794              {
03795                graphics_object go = gh_manager::get_object (aligned[i]);
03796                axes::properties& props =
03797                  dynamic_cast<axes::properties&> (go.get_properties ());
03798                Matrix outpos = props.get_outerposition ().matrix_value ();
03799                Matrix linset = props.get_looseinset ().matrix_value ();
03800                if (l_aligned[i])
03801                  linset(0) = (ref_box(0)-outpos(0))/outpos(2);
03802                if (b_aligned[i])
03803                  linset(1) = (ref_box(1)-outpos(1))/outpos(3);
03804                if (r_aligned[i])
03805                  linset(2) = (outpos(0)+outpos(2)-ref_box(2))/outpos(2);
03806                if (t_aligned[i])
03807                  linset(3) = (outpos(1)+outpos(3)-ref_box(3))/outpos(3);
03808                props.set_looseinset (linset);
03809                props.set_autopos_tag ("subplot");
03810              }
03811            parent_obj.set ("units", fig_units);
03812         }
03813     }
03814   else
03815     sync_positions (ref_linset);
03816 }
03817 
03818 void
03819 axes::properties::sync_positions (const Matrix& linset)
03820 {
03821   Matrix pos = position.get ().matrix_value ();
03822   Matrix outpos = outerposition.get ().matrix_value ();
03823   double lratio = linset(0);
03824   double bratio = linset(1);
03825   double wratio = 1-linset(0)-linset(2);
03826   double hratio = 1-linset(1)-linset(3);
03827   if (activepositionproperty.is ("outerposition"))
03828     {
03829       pos = outpos;
03830       pos(0) = outpos(0)+lratio*outpos(2);
03831       pos(1) = outpos(1)+bratio*outpos(3);
03832       pos(2) = wratio*outpos(2);
03833       pos(3) = hratio*outpos(3);
03834 
03835       position = pos;
03836       update_transform ();
03837       Matrix tightpos = calc_tightbox (pos);
03838 
03839       double thrshldx = 0.005*outpos(2);
03840       double thrshldy = 0.005*outpos(3);
03841       double minsizex = 0.2*outpos(2);
03842       double minsizey = 0.2*outpos(3);
03843       bool updatex = true, updatey = true;
03844       for (int i = 0; i < 10; i++)
03845         {
03846           double dt;
03847           bool modified = false;
03848           dt = outpos(0)+outpos(2)-tightpos(0)-tightpos(2);
03849           if (dt < -thrshldx && updatex)
03850             {
03851               pos(2) += dt;
03852               modified = true;
03853             }
03854           dt = outpos(1)+outpos(3)-tightpos(1)-tightpos(3);
03855           if (dt < -thrshldy && updatey)
03856             {
03857               pos(3) += dt;
03858               modified = true;
03859             }
03860           dt = outpos(0)-tightpos(0);
03861           if (dt > thrshldx && updatex)
03862             {
03863               pos(0) += dt;
03864               pos(2) -= dt;
03865               modified = true;
03866             }
03867           dt = outpos(1)-tightpos(1);
03868           if (dt > thrshldy && updatey)
03869             {
03870               pos(1) += dt;
03871               pos(3) -= dt;
03872               modified = true;
03873             }
03874 
03875           // Note: checking limit for minimum axes size
03876           if (pos(2) < minsizex)
03877             {
03878               pos(0) -= 0.5*(minsizex-pos(2));
03879               pos(2) = minsizex;
03880               updatex = false;
03881             }
03882           if (pos(3) < minsizey)
03883             {
03884               pos(1) -= 0.5*(minsizey-pos(3));
03885               pos(3) = minsizey;
03886               updatey = false;
03887             }
03888 
03889           if (modified)
03890             {
03891               position = pos;
03892               update_transform ();
03893               tightpos = calc_tightbox (pos);
03894             }
03895           else
03896             break;
03897         }
03898     }
03899   else
03900     {
03901       update_transform ();
03902 
03903       outpos(0) = pos(0)-pos(2)*lratio/wratio;
03904       outpos(1) = pos(1)-pos(3)*bratio/hratio;
03905       outpos(2) = pos(2)/wratio;
03906       outpos(3) = pos(3)/hratio;
03907 
03908       outerposition = calc_tightbox (outpos);
03909     }
03910 
03911   Matrix inset (1, 4, 1.0);
03912   inset(0) = pos(0)-outpos(0);
03913   inset(1) = pos(1)-outpos(1);
03914   inset(2) = outpos(0)+outpos(2)-pos(0)-pos(2);
03915   inset(3) = outpos(1)+outpos(3)-pos(1)-pos(3);
03916 
03917   tightinset = inset;
03918 }
03919 
03920 void
03921 axes::properties::set_text_child (handle_property& hp,
03922                                   const std::string& who,
03923                                   const octave_value& v)
03924 {
03925   graphics_handle val;
03926 
03927   if (v.is_string ())
03928     {
03929       val = gh_manager::make_graphics_handle ("text", __myhandle__,
03930                                               false, false);
03931 
03932       xset (val, "string", v);
03933     }
03934   else
03935     {
03936       graphics_object go = gh_manager::get_object (gh_manager::lookup (v));
03937 
03938       if (go.isa ("text"))
03939         val = ::reparent (v, "set", who, __myhandle__, false);
03940       else
03941         {
03942           std::string cname = v.class_name ();
03943 
03944           error ("set: expecting text graphics object or character string for %s property, found %s",
03945                  who.c_str (), cname.c_str ());
03946         }
03947     }
03948 
03949   if (! error_state)
03950     {
03951       xset (val, "handlevisibility", "off");
03952 
03953       gh_manager::free (hp.handle_value ());
03954 
03955       base_properties::remove_child (hp.handle_value ());
03956 
03957       hp = val;
03958 
03959       adopt (hp.handle_value ());
03960     }
03961 }
03962 
03963 void
03964 axes::properties::set_xlabel (const octave_value& v)
03965 {
03966   set_text_child (xlabel, "xlabel", v);
03967   xset (xlabel.handle_value (), "positionmode", "auto");
03968   xset (xlabel.handle_value (), "rotationmode", "auto");
03969   xset (xlabel.handle_value (), "horizontalalignmentmode", "auto");
03970   xset (xlabel.handle_value (), "verticalalignmentmode", "auto");
03971   xset (xlabel.handle_value (), "clipping", "off");
03972   xset (xlabel.handle_value (), "color", get_xcolor ());
03973   xset (xlabel.handle_value (), "autopos_tag", "xlabel");
03974   update_xlabel_position ();
03975 }
03976 
03977 void
03978 axes::properties::set_ylabel (const octave_value& v)
03979 {
03980   set_text_child (ylabel, "ylabel", v);
03981   xset (ylabel.handle_value (), "positionmode", "auto");
03982   xset (ylabel.handle_value (), "rotationmode", "auto");
03983   xset (ylabel.handle_value (), "horizontalalignmentmode", "auto");
03984   xset (ylabel.handle_value (), "verticalalignmentmode", "auto");
03985   xset (ylabel.handle_value (), "clipping", "off");
03986   xset (ylabel.handle_value (), "color", get_ycolor ());
03987   xset (ylabel.handle_value (), "autopos_tag", "ylabel");
03988   update_ylabel_position ();
03989 }
03990 
03991 void
03992 axes::properties::set_zlabel (const octave_value& v)
03993 {
03994   set_text_child (zlabel, "zlabel", v);
03995   xset (zlabel.handle_value (), "positionmode", "auto");
03996   xset (zlabel.handle_value (), "rotationmode", "auto");
03997   xset (zlabel.handle_value (), "horizontalalignmentmode", "auto");
03998   xset (zlabel.handle_value (), "verticalalignmentmode", "auto");
03999   xset (zlabel.handle_value (), "clipping", "off");
04000   xset (zlabel.handle_value (), "color", get_zcolor ());
04001   xset (zlabel.handle_value (), "autopos_tag", "zlabel");
04002   update_zlabel_position ();
04003 }
04004 
04005 void
04006 axes::properties::set_title (const octave_value& v)
04007 {
04008   set_text_child (title, "title", v);
04009   xset (title.handle_value (), "positionmode", "auto");
04010   xset (title.handle_value (), "horizontalalignment", "center");
04011   xset (title.handle_value (), "horizontalalignmentmode", "auto");
04012   xset (title.handle_value (), "verticalalignment", "bottom");
04013   xset (title.handle_value (), "verticalalignmentmode", "auto");
04014   xset (title.handle_value (), "clipping", "off");
04015   xset (title.handle_value (), "autopos_tag", "title");
04016   update_title_position ();
04017 }
04018 
04019 void
04020 axes::properties::set_defaults (base_graphics_object& obj,
04021                                 const std::string& mode)
04022 {
04023   box = "on";
04024   colororder = default_colororder ();
04025   dataaspectratio = Matrix (1, 3, 1.0);
04026   dataaspectratiomode = "auto";
04027   layer = "bottom";
04028 
04029   Matrix tlim (1, 2, 0.0);
04030   tlim(1) = 1;
04031   xlim = tlim;
04032   ylim = tlim;
04033   zlim = tlim;
04034 
04035   Matrix cl (1, 2, 0);
04036   cl(1) = 1;
04037   clim = cl;
04038 
04039   xlimmode = "auto";
04040   ylimmode = "auto";
04041   zlimmode = "auto";
04042   climmode = "auto";
04043 
04044   xgrid = "off";
04045   ygrid = "off";
04046   zgrid = "off";
04047   xminorgrid = "off";
04048   yminorgrid = "off";
04049   zminorgrid = "off";
04050   xtick = Matrix ();
04051   ytick = Matrix ();
04052   ztick = Matrix ();
04053   xtickmode = "auto";
04054   ytickmode = "auto";
04055   ztickmode = "auto";
04056   xticklabel = "";
04057   yticklabel = "";
04058   zticklabel = "";
04059   xticklabelmode = "auto";
04060   yticklabelmode = "auto";
04061   zticklabelmode = "auto";
04062   color = "none";
04063   xcolor = color_values ("black");
04064   ycolor = color_values ("black");
04065   zcolor = color_values ("black");
04066   xscale = "linear";
04067   yscale = "linear";
04068   zscale = "linear";
04069   xdir = "normal";
04070   ydir = "normal";
04071   zdir = "normal";
04072   yaxislocation = "left";
04073   xaxislocation = "bottom";
04074 
04075   // Note: camera properties will be set through update_transform
04076   camerapositionmode = "auto";
04077   cameratargetmode = "auto";
04078   cameraupvectormode = "auto";
04079   cameraviewanglemode = "auto";
04080   plotboxaspectratio = Matrix (1, 3, 1.0);
04081   drawmode = "normal";
04082   gridlinestyle = ":";
04083   linestyleorder = "-";
04084   linewidth = 0.5;
04085   minorgridlinestyle = ":";
04086   // Note: plotboxaspectratio will be set through update_aspectratiors
04087   plotboxaspectratiomode = "auto";
04088   projection = "orthographic";
04089   tickdir = "in";
04090   tickdirmode = "auto";
04091   ticklength = default_axes_ticklength ();
04092   tightinset = Matrix (1, 4, 0.0);
04093 
04094   sx = "linear";
04095   sy = "linear";
04096   sz = "linear";
04097 
04098   Matrix tview (1, 2, 0.0);
04099   tview(1) = 90;
04100   view = tview;
04101 
04102   visible = "on";
04103   nextplot = "replace";
04104 
04105   if (mode != "replace")
04106     {
04107       fontangle = "normal";
04108       fontname = OCTAVE_DEFAULT_FONTNAME;
04109       fontsize = 10;
04110       fontunits = "points";
04111       fontweight = "normal";
04112 
04113       Matrix touterposition (1, 4, 0.0);
04114       touterposition(2) = 1;
04115       touterposition(3) = 1;
04116       outerposition = touterposition;
04117 
04118       position = default_axes_position ();
04119 
04120       Matrix tlooseinset = default_axes_position ();
04121       tlooseinset(2) = 1-tlooseinset(0)-tlooseinset(2);
04122       tlooseinset(3) = 1-tlooseinset(1)-tlooseinset(3);
04123       looseinset = tlooseinset;
04124 
04125       activepositionproperty = "outerposition";
04126     }
04127 
04128   delete_children (true);
04129 
04130   xlabel = gh_manager::make_graphics_handle ("text", __myhandle__,
04131                                              false, false);
04132 
04133   ylabel = gh_manager::make_graphics_handle ("text", __myhandle__,
04134                                              false, false);
04135 
04136   zlabel = gh_manager::make_graphics_handle ("text", __myhandle__,
04137                                              false, false);
04138 
04139   title = gh_manager::make_graphics_handle ("text", __myhandle__,
04140                                             false, false);
04141 
04142   xset (xlabel.handle_value (), "handlevisibility", "off");
04143   xset (ylabel.handle_value (), "handlevisibility", "off");
04144   xset (zlabel.handle_value (), "handlevisibility", "off");
04145   xset (title.handle_value (), "handlevisibility", "off");
04146 
04147   xset (xlabel.handle_value (), "horizontalalignment", "center");
04148   xset (xlabel.handle_value (), "horizontalalignmentmode", "auto");
04149   xset (ylabel.handle_value (), "horizontalalignment", "center");
04150   xset (ylabel.handle_value (), "horizontalalignmentmode", "auto");
04151   xset (zlabel.handle_value (), "horizontalalignment", "right");
04152   xset (zlabel.handle_value (), "horizontalalignmentmode", "auto");
04153   xset (title.handle_value (), "horizontalalignment", "center");
04154   xset (title.handle_value (), "horizontalalignmentmode", "auto");
04155 
04156   xset (xlabel.handle_value (), "verticalalignment", "cap");
04157   xset (xlabel.handle_value (), "verticalalignmentmode", "auto");
04158   xset (ylabel.handle_value (), "verticalalignment", "bottom");
04159   xset (ylabel.handle_value (), "verticalalignmentmode", "auto");
04160   xset (title.handle_value (), "verticalalignment", "bottom");
04161   xset (title.handle_value (), "verticalalignmentmode", "auto");
04162 
04163   xset (ylabel.handle_value (), "rotation", 90.0);
04164   xset (ylabel.handle_value (), "rotationmode", "auto");
04165 
04166   xset (zlabel.handle_value (), "visible", "off");
04167 
04168   xset (xlabel.handle_value (), "clipping", "off");
04169   xset (ylabel.handle_value (), "clipping", "off");
04170   xset (zlabel.handle_value (), "clipping", "off");
04171   xset (title.handle_value (), "clipping", "off");
04172 
04173   xset (xlabel.handle_value (), "autopos_tag", "xlabel");
04174   xset (ylabel.handle_value (), "autopos_tag", "ylabel");
04175   xset (zlabel.handle_value (), "autopos_tag", "zlabel");
04176   xset (title.handle_value (), "autopos_tag", "title");
04177 
04178   adopt (xlabel.handle_value ());
04179   adopt (ylabel.handle_value ());
04180   adopt (zlabel.handle_value ());
04181   adopt (title.handle_value ());
04182 
04183   update_transform ();
04184 
04185   override_defaults (obj);
04186 }
04187 
04188 void
04189 axes::properties::delete_text_child (handle_property& hp)
04190 {
04191   graphics_handle h = hp.handle_value ();
04192 
04193   if (h.ok ())
04194     {
04195       graphics_object go = gh_manager::get_object (h);
04196 
04197       if (go.valid_object ())
04198         gh_manager::free (h);
04199 
04200       base_properties::remove_child (h);
04201     }
04202 
04203   // FIXME -- is it necessary to check whether the axes object is
04204   // being deleted now?  I think this function is only called when an
04205   // individual child object is delete and not when the parent axes
04206   // object is deleted.
04207 
04208   if (! is_beingdeleted ())
04209     {
04210       hp = gh_manager::make_graphics_handle ("text", __myhandle__,
04211                                              false, false);
04212 
04213       xset (hp.handle_value (), "handlevisibility", "off");
04214 
04215       adopt (hp.handle_value ());
04216     }
04217 }
04218 
04219 void
04220 axes::properties::remove_child (const graphics_handle& h)
04221 {
04222   if (xlabel.handle_value ().ok () && h == xlabel.handle_value ())
04223     delete_text_child (xlabel);
04224   else if (ylabel.handle_value ().ok () && h == ylabel.handle_value ())
04225     delete_text_child (ylabel);
04226   else if (zlabel.handle_value ().ok () && h == zlabel.handle_value ())
04227     delete_text_child (zlabel);
04228   else if (title.handle_value ().ok () && h == title.handle_value ())
04229     delete_text_child (title);
04230   else
04231     base_properties::remove_child (h);
04232 }
04233 
04234 inline Matrix
04235 xform_matrix (void)
04236 {
04237   Matrix m (4, 4, 0.0);
04238   for (int i = 0; i < 4; i++)
04239     m(i,i) = 1;
04240   return m;
04241 }
04242 
04243 inline ColumnVector
04244 xform_vector (void)
04245 {
04246   ColumnVector v (4, 0.0);
04247   v(3) = 1;
04248   return v;
04249 }
04250 
04251 inline ColumnVector
04252 xform_vector (double x, double y, double z)
04253 {
04254   ColumnVector v (4, 1.0);
04255   v(0) = x; v(1) = y; v(2) = z;
04256   return v;
04257 }
04258 
04259 inline ColumnVector
04260 transform (const Matrix& m, double x, double y, double z)
04261 {
04262   return (m * xform_vector (x, y, z));
04263 }
04264 
04265 inline Matrix
04266 xform_scale (double x, double y, double z)
04267 {
04268   Matrix m (4, 4, 0.0);
04269   m(0,0) = x; m(1,1) = y; m(2,2) = z; m(3,3) = 1;
04270   return m;
04271 }
04272 
04273 inline Matrix
04274 xform_translate (double x, double y, double z)
04275 {
04276   Matrix m = xform_matrix ();
04277   m(0,3) = x; m(1,3) = y; m(2,3) = z; m(3,3) = 1;
04278   return m;
04279 }
04280 
04281 inline void
04282 scale (Matrix& m, double x, double y, double z)
04283 {
04284   m = m * xform_scale (x, y, z);
04285 }
04286 
04287 inline void
04288 translate (Matrix& m, double x, double y, double z)
04289 {
04290   m = m * xform_translate (x, y, z);
04291 }
04292 
04293 inline void
04294 xform (ColumnVector& v, const Matrix& m)
04295 {
04296   v = m*v;
04297 }
04298 
04299 inline void
04300 scale (ColumnVector& v, double x, double y, double z)
04301 {
04302   v(0) *= x;
04303   v(1) *= y;
04304   v(2) *= z;
04305 }
04306 
04307 inline void
04308 translate (ColumnVector& v, double x, double y, double z)
04309 {
04310   v(0) += x;
04311   v(1) += y;
04312   v(2) += z;
04313 }
04314 
04315 inline void
04316 normalize (ColumnVector& v)
04317 {
04318   double fact = 1.0/sqrt(v(0)*v(0)+v(1)*v(1)+v(2)*v(2));
04319   scale (v, fact, fact, fact);
04320 }
04321 
04322 inline double
04323 dot (const ColumnVector& v1, const ColumnVector& v2)
04324 {
04325   return (v1(0)*v2(0)+v1(1)*v2(1)+v1(2)*v2(2));
04326 }
04327 
04328 inline double
04329 norm (const ColumnVector& v)
04330 {
04331   return sqrt (dot (v, v));
04332 }
04333 
04334 inline ColumnVector
04335 cross (const ColumnVector& v1, const ColumnVector& v2)
04336 {
04337   ColumnVector r = xform_vector ();
04338   r(0) = v1(1)*v2(2)-v1(2)*v2(1);
04339   r(1) = v1(2)*v2(0)-v1(0)*v2(2);
04340   r(2) = v1(0)*v2(1)-v1(1)*v2(0);
04341   return r;
04342 }
04343 
04344 inline Matrix
04345 unit_cube (void)
04346 {
04347   static double data[32] = {
04348       0,0,0,1,
04349       1,0,0,1,
04350       0,1,0,1,
04351       0,0,1,1,
04352       1,1,0,1,
04353       1,0,1,1,
04354       0,1,1,1,
04355       1,1,1,1};
04356   Matrix m (4, 8);
04357   memcpy (m.fortran_vec (), data, sizeof(double)*32);
04358   return m;
04359 }
04360 
04361 inline ColumnVector
04362 cam2xform (const Array<double>& m)
04363 {
04364   ColumnVector retval (4, 1.0);
04365   memcpy (retval.fortran_vec (), m.fortran_vec (), sizeof(double)*3);
04366   return retval;
04367 }
04368 
04369 inline RowVector
04370 xform2cam (const ColumnVector& v)
04371 {
04372   return v.extract_n (0, 3).transpose ();
04373 }
04374 
04375 void
04376 axes::properties::update_camera (void)
04377 {
04378   double xd = (xdir_is ("normal") ? 1 : -1);
04379   double yd = (ydir_is ("normal") ? 1 : -1);
04380   double zd = (zdir_is ("normal") ? 1 : -1);
04381 
04382   Matrix xlimits = sx.scale (get_xlim ().matrix_value ());
04383   Matrix ylimits = sy.scale (get_ylim ().matrix_value ());
04384   Matrix zlimits = sz.scale (get_zlim ().matrix_value ());
04385 
04386   double xo = xlimits(xd > 0 ? 0 : 1);
04387   double yo = ylimits(yd > 0 ? 0 : 1);
04388   double zo = zlimits(zd > 0 ? 0 : 1);
04389 
04390   Matrix pb  = get_plotboxaspectratio ().matrix_value ();
04391 
04392   bool autocam = (camerapositionmode_is ("auto")
04393                   && cameratargetmode_is ("auto")
04394                   && cameraupvectormode_is ("auto")
04395                   && cameraviewanglemode_is ("auto"));
04396   bool dowarp = (autocam && dataaspectratiomode_is("auto")
04397                  && plotboxaspectratiomode_is ("auto"));
04398 
04399   ColumnVector c_eye (xform_vector ());
04400   ColumnVector c_center (xform_vector ());
04401   ColumnVector c_upv (xform_vector ());
04402 
04403   if (cameratargetmode_is ("auto"))
04404     {
04405       c_center(0) = (xlimits(0)+xlimits(1))/2;
04406       c_center(1) = (ylimits(0)+ylimits(1))/2;
04407       c_center(2) = (zlimits(0)+zlimits(1))/2;
04408 
04409       cameratarget = xform2cam (c_center);
04410     }
04411   else
04412     c_center = cam2xform (get_cameratarget ().matrix_value ());
04413 
04414   if (camerapositionmode_is ("auto"))
04415     {
04416       Matrix tview = get_view ().matrix_value ();
04417       double az = tview(0), el = tview(1);
04418       double d = 5*sqrt(pb(0)*pb(0)+pb(1)*pb(1)+pb(2)*pb(2));
04419 
04420       if (el == 90 || el == -90)
04421         c_eye(2) = d*signum(el);
04422       else
04423         {
04424           az *= M_PI/180.0;
04425           el *= M_PI/180.0;
04426           c_eye(0) = d*cos(el)*sin(az);
04427           c_eye(1) = -d*cos(el)*cos(az);
04428           c_eye(2) = d*sin(el);
04429         }
04430       c_eye(0) = c_eye(0)*(xlimits(1)-xlimits(0))/(xd*pb(0))+c_center(0);
04431       c_eye(1) = c_eye(1)*(ylimits(1)-ylimits(0))/(yd*pb(1))+c_center(1);
04432       c_eye(2) = c_eye(2)*(zlimits(1)-zlimits(0))/(zd*pb(2))+c_center(2);
04433 
04434       cameraposition = xform2cam (c_eye);
04435     }
04436   else
04437     c_eye = cam2xform (get_cameraposition ().matrix_value ());
04438 
04439   if (cameraupvectormode_is ("auto"))
04440     {
04441       Matrix tview = get_view ().matrix_value ();
04442       double az = tview(0), el = tview(1);
04443 
04444       if (el == 90 || el == -90)
04445         {
04446           c_upv(0) =
04447             -signum(el)*sin(az*M_PI/180.0)*(xlimits(1)-xlimits(0))/pb(0);
04448           c_upv(1) =
04449             signum(el)*cos(az*M_PI/180.0)*(ylimits(1)-ylimits(0))/pb(1);
04450         }
04451       else
04452         c_upv(2) = 1;
04453 
04454       cameraupvector = xform2cam (c_upv);
04455     }
04456   else
04457     c_upv = cam2xform (get_cameraupvector ().matrix_value ());
04458 
04459   Matrix x_view = xform_matrix ();
04460   Matrix x_projection = xform_matrix ();
04461   Matrix x_viewport = xform_matrix ();
04462   Matrix x_normrender = xform_matrix ();
04463   Matrix x_pre = xform_matrix ();
04464 
04465   x_render = xform_matrix ();
04466   x_render_inv = xform_matrix ();
04467 
04468   scale (x_pre, pb(0), pb(1), pb(2));
04469   translate (x_pre, -0.5, -0.5, -0.5);
04470   scale (x_pre, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
04471          zd/(zlimits(1)-zlimits(0)));
04472   translate (x_pre, -xo, -yo, -zo);
04473 
04474   xform (c_eye, x_pre);
04475   xform (c_center, x_pre);
04476   scale (c_upv, pb(0)/(xlimits(1)-xlimits(0)), pb(1)/(ylimits(1)-ylimits(0)),
04477          pb(2)/(zlimits(1)-zlimits(0)));
04478   translate (c_center, -c_eye(0), -c_eye(1), -c_eye(2));
04479 
04480   ColumnVector F (c_center), f (F), UP (c_upv);
04481   normalize (f);
04482   normalize (UP);
04483 
04484   if (std::abs (dot (f, UP)) > 1e-15)
04485     {
04486       double fa = 1/sqrt(1-f(2)*f(2));
04487       scale (UP, fa, fa, fa);
04488     }
04489 
04490   ColumnVector s = cross (f, UP);
04491   ColumnVector u = cross (s, f);
04492 
04493   scale (x_view, 1, 1, -1);
04494   Matrix l = xform_matrix ();
04495   l(0,0) = s(0); l(0,1) = s(1); l(0,2) = s(2);
04496   l(1,0) = u(0); l(1,1) = u(1); l(1,2) = u(2);
04497   l(2,0) = -f(0); l(2,1) = -f(1); l(2,2) = -f(2);
04498   x_view = x_view * l;
04499   translate (x_view, -c_eye(0), -c_eye(1), -c_eye(2));
04500   scale (x_view, pb(0), pb(1), pb(2));
04501   translate (x_view, -0.5, -0.5, -0.5);
04502 
04503   Matrix x_cube = x_view * unit_cube ();
04504   ColumnVector cmin = x_cube.row_min (), cmax = x_cube.row_max ();
04505   double xM = cmax(0)-cmin(0);
04506   double yM = cmax(1)-cmin(1);
04507 
04508   Matrix bb = get_boundingbox (true);
04509 
04510   double v_angle;
04511 
04512   if (cameraviewanglemode_is ("auto"))
04513     {
04514       double af;
04515 
04516       // FIXME -- was this really needed?  When compared to Matlab, it
04517       // does not seem to be required. Need investigation with concrete
04518       // graphics toolkit to see results visually.
04519       if (false && dowarp)
04520         af = 1.0 / (xM > yM ? xM : yM);
04521       else
04522         {
04523           if ((bb(2)/bb(3)) > (xM/yM))
04524             af = 1.0 / yM;
04525           else
04526             af = 1.0 / xM;
04527         }
04528       v_angle = 2 * (180.0 / M_PI) * atan (1 / (2 * af * norm (F)));
04529 
04530       cameraviewangle = v_angle;
04531     }
04532   else
04533     v_angle = get_cameraviewangle ();
04534 
04535   double pf = 1 / (2 * tan ((v_angle / 2) * M_PI / 180.0) * norm (F));
04536   scale (x_projection, pf, pf, 1);
04537 
04538   if (dowarp)
04539     {
04540       xM *= pf;
04541       yM *= pf;
04542       translate (x_viewport, bb(0)+bb(2)/2, bb(1)+bb(3)/2, 0);
04543       scale (x_viewport, bb(2)/xM, -bb(3)/yM, 1);
04544     }
04545   else
04546     {
04547       double pix = 1;
04548       if (autocam)
04549         {
04550           if ((bb(2)/bb(3)) > (xM/yM))
04551             pix = bb(3);
04552           else
04553             pix = bb(2);
04554         }
04555       else
04556         pix = (bb(2) < bb(3) ? bb(2) : bb(3));
04557       translate (x_viewport, bb(0)+bb(2)/2, bb(1)+bb(3)/2, 0);
04558       scale (x_viewport, pix, -pix, 1);
04559     }
04560 
04561   x_normrender = x_viewport * x_projection * x_view;
04562 
04563   x_cube = x_normrender * unit_cube ();
04564   cmin = x_cube.row_min ();
04565   cmax = x_cube.row_max ();
04566   x_zlim.resize (1, 2);
04567   x_zlim(0) = cmin(2);
04568   x_zlim(1) = cmax(2);
04569 
04570   x_render = x_normrender;
04571   scale (x_render, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
04572          zd/(zlimits(1)-zlimits(0)));
04573   translate (x_render, -xo, -yo, -zo);
04574 
04575   x_viewtransform = x_view;
04576   x_projectiontransform = x_projection;
04577   x_viewporttransform = x_viewport;
04578   x_normrendertransform = x_normrender;
04579   x_rendertransform = x_render;
04580 
04581   x_render_inv = x_render.inverse ();
04582 
04583   // Note: these matrices are a slight modified version of the regular
04584   // matrices, more suited for OpenGL rendering (x_gl_mat1 => light
04585   // => x_gl_mat2)
04586   x_gl_mat1 = x_view;
04587   scale (x_gl_mat1, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
04588          zd/(zlimits(1)-zlimits(0)));
04589   translate (x_gl_mat1, -xo, -yo, -zo);
04590   x_gl_mat2 = x_viewport * x_projection;
04591 }
04592 
04593 static bool updating_axes_layout = false;
04594 
04595 void
04596 axes::properties::update_axes_layout (void)
04597 {
04598   if (updating_axes_layout)
04599     return;
04600 
04601   graphics_xform xform = get_transform ();
04602 
04603   double xd = (xdir_is ("normal") ? 1 : -1);
04604   double yd = (ydir_is ("normal") ? 1 : -1);
04605   double zd = (zdir_is ("normal") ? 1 : -1);
04606 
04607   const Matrix xlims = xform.xscale (get_xlim ().matrix_value ());
04608   const Matrix ylims = xform.yscale (get_ylim ().matrix_value ());
04609   const Matrix zlims = xform.zscale (get_zlim ().matrix_value ());
04610   double x_min = xlims(0), x_max = xlims(1);
04611   double y_min = ylims(0), y_max = ylims(1);
04612   double z_min = zlims(0), z_max = zlims(1);
04613 
04614   ColumnVector p1, p2, dir (3);
04615 
04616   xstate = ystate = zstate = AXE_ANY_DIR;
04617 
04618   p1 = xform.transform (x_min, (y_min+y_max)/2, (z_min+z_max)/2, false);
04619   p2 = xform.transform (x_max, (y_min+y_max)/2, (z_min+z_max)/2, false);
04620   dir(0) = xround (p2(0)-p1(0));
04621   dir(1) = xround (p2(1)-p1(1));
04622   dir(2) = (p2(2)-p1(2));
04623   if (dir(0) == 0 && dir(1) == 0)
04624     xstate = AXE_DEPTH_DIR;
04625   else if (dir(2) == 0)
04626     {
04627       if (dir(0) == 0)
04628         xstate = AXE_VERT_DIR;
04629       else if (dir(1) == 0)
04630         xstate = AXE_HORZ_DIR;
04631     }
04632 
04633   if (dir(2) == 0)
04634     {
04635       if (dir(1) == 0)
04636         xPlane = (dir(0) > 0 ? x_max : x_min);
04637       else
04638         xPlane = (dir(1) < 0 ? x_max : x_min);
04639     }
04640   else
04641     xPlane = (dir(2) < 0 ? x_min : x_max);
04642 
04643   xPlaneN = (xPlane == x_min ? x_max : x_min);
04644   fx = (x_max-x_min)/sqrt(dir(0)*dir(0)+dir(1)*dir(1));
04645 
04646   p1 = xform.transform ((x_min+x_max)/2, y_min, (z_min+z_max)/2, false);
04647   p2 = xform.transform ((x_min+x_max)/2, y_max, (z_min+z_max)/2, false);
04648   dir(0) = xround (p2(0)-p1(0));
04649   dir(1) = xround (p2(1)-p1(1));
04650   dir(2) = (p2(2)-p1(2));
04651   if (dir(0) == 0 && dir(1) == 0)
04652     ystate = AXE_DEPTH_DIR;
04653   else if (dir(2) == 0)
04654     {
04655       if (dir(0) == 0)
04656         ystate = AXE_VERT_DIR;
04657       else if (dir(1) == 0)
04658         ystate = AXE_HORZ_DIR;
04659     }
04660 
04661   if (dir(2) == 0)
04662     {
04663       if (dir(1) == 0)
04664         yPlane = (dir(0) > 0 ? y_max : y_min);
04665       else
04666         yPlane = (dir(1) < 0 ? y_max : y_min);
04667     }
04668   else
04669     yPlane = (dir(2) < 0 ? y_min : y_max);
04670 
04671   yPlaneN = (yPlane == y_min ? y_max : y_min);
04672   fy = (y_max-y_min)/sqrt(dir(0)*dir(0)+dir(1)*dir(1));
04673 
04674   p1 = xform.transform((x_min+x_max)/2, (y_min+y_max)/2, z_min, false);
04675   p2 = xform.transform((x_min+x_max)/2, (y_min+y_max)/2, z_max, false);
04676   dir(0) = xround(p2(0)-p1(0));
04677   dir(1) = xround (p2(1)-p1(1));
04678   dir(2) = (p2(2)-p1(2));
04679   if (dir(0) == 0 && dir(1) == 0)
04680     zstate = AXE_DEPTH_DIR;
04681   else if (dir(2) == 0)
04682     {
04683       if (dir(0) == 0)
04684         zstate = AXE_VERT_DIR;
04685       else if (dir(1) == 0)
04686         zstate = AXE_HORZ_DIR;
04687     }
04688 
04689   if (dir(2) == 0)
04690     {
04691       if (dir(1) == 0)
04692         zPlane = (dir(0) > 0 ? z_min : z_max);
04693       else
04694         zPlane = (dir(1) < 0 ? z_min : z_max);
04695     }
04696   else
04697     zPlane = (dir(2) < 0 ? z_min : z_max);
04698 
04699   zPlaneN = (zPlane == z_min ? z_max : z_min);
04700   fz = (z_max-z_min)/sqrt(dir(0)*dir(0)+dir(1)*dir(1));
04701 
04702   unwind_protect frame;
04703   frame.protect_var (updating_axes_layout);
04704   updating_axes_layout = true;
04705 
04706   xySym = (xd*yd*(xPlane-xPlaneN)*(yPlane-yPlaneN) > 0);
04707   zSign = (zd*(zPlane-zPlaneN) <= 0);
04708   xyzSym = zSign ? xySym : !xySym;
04709   xpTick = (zSign ? xPlaneN : xPlane);
04710   ypTick = (zSign ? yPlaneN : yPlane);
04711   zpTick = (zSign ? zPlane : zPlaneN);
04712   xpTickN = (zSign ? xPlane : xPlaneN);
04713   ypTickN = (zSign ? yPlane : yPlaneN);
04714   zpTickN = (zSign ? zPlaneN : zPlane);
04715 
04716   /* 2D mode */
04717   x2Dtop = false;
04718   y2Dright = false;
04719   layer2Dtop = false;
04720   if (xstate == AXE_HORZ_DIR && ystate == AXE_VERT_DIR)
04721   {
04722     if (xaxislocation_is ("top"))
04723     {
04724       double tmp = yPlane;
04725       yPlane = yPlaneN;
04726       yPlaneN = tmp;
04727       x2Dtop = true;
04728     }
04729     ypTick = yPlaneN;
04730     ypTickN = yPlane;
04731     if (yaxislocation_is ("right"))
04732     {
04733       double tmp = xPlane;
04734       xPlane = xPlaneN;
04735       xPlaneN = tmp;
04736       y2Dright = true;
04737     }
04738     xpTick = xPlaneN;
04739     xpTickN = xPlane;
04740     if (layer_is ("top"))
04741       {
04742         zpTick = zPlaneN;
04743         layer2Dtop = true;
04744       }
04745     else
04746       zpTick = zPlane;
04747   }
04748 
04749   Matrix viewmat = get_view ().matrix_value ();
04750   nearhoriz = std::abs(viewmat(1)) <= 5;
04751 
04752   update_ticklengths ();
04753 }
04754 
04755 void
04756 axes::properties::update_ticklengths (void)
04757 {
04758   bool mode2d = (((xstate > AXE_DEPTH_DIR ? 1 : 0) +
04759                   (ystate > AXE_DEPTH_DIR ? 1 : 0) +
04760                   (zstate > AXE_DEPTH_DIR ? 1 : 0)) == 2);
04761   if (tickdirmode_is ("auto"))
04762   {
04763     // FIXME: tickdir should be updated (code below comes
04764     //        from JHandles)
04765     //autoMode++;
04766     //TickDir.set(mode2d ? "in" : "out", true);
04767     //autoMode--;
04768   }
04769 
04770   //double ticksign = (tickdir_is ("in") ? -1 : 1);
04771   double ticksign = (tickdirmode_is ("auto") ?
04772                      (mode2d ? -1 : 1) :
04773                      (tickdir_is ("in") ? -1 : 1));
04774   // FIXME: use ticklength property
04775   xticklen = ticksign*7;
04776   yticklen = ticksign*7;
04777   zticklen = ticksign*7;
04778 
04779   xtickoffset = (mode2d ? std::max (0., xticklen) : std::abs (xticklen)) + 5;
04780   ytickoffset = (mode2d ? std::max (0., yticklen) : std::abs (yticklen)) + 5;
04781   ztickoffset = (mode2d ? std::max (0., zticklen) : std::abs (zticklen)) + 5;
04782 
04783   update_xlabel_position ();
04784   update_ylabel_position ();
04785   update_zlabel_position ();
04786   update_title_position ();
04787 }
04788 
04789 static bool updating_xlabel_position = false;
04790 
04791 void
04792 axes::properties::update_xlabel_position (void)
04793 {
04794   if (updating_xlabel_position)
04795     return;
04796 
04797   text::properties& xlabel_props = reinterpret_cast<text::properties&>
04798     (gh_manager::get_object (get_xlabel ()).get_properties ());
04799 
04800   bool is_empty = xlabel_props.get_string ().is_empty ();
04801 
04802   unwind_protect frame;
04803   frame.protect_var (updating_xlabel_position);
04804   updating_xlabel_position = true;
04805 
04806   if (! is_empty)
04807     {
04808       if (xlabel_props.horizontalalignmentmode_is ("auto"))
04809         {
04810           xlabel_props.set_horizontalalignment
04811             (xstate > AXE_DEPTH_DIR
04812              ? "center" : (xyzSym ? "left" : "right"));
04813 
04814           xlabel_props.set_horizontalalignmentmode ("auto");
04815         }
04816 
04817       if (xlabel_props.verticalalignmentmode_is ("auto"))
04818         {
04819           xlabel_props.set_verticalalignment
04820             (xstate == AXE_VERT_DIR || x2Dtop ? "bottom" : "top");
04821 
04822           xlabel_props.set_verticalalignmentmode ("auto");
04823         }
04824     }
04825 
04826   if (xlabel_props.positionmode_is ("auto")
04827       || xlabel_props.rotationmode_is ("auto"))
04828     {
04829       graphics_xform xform = get_transform ();
04830 
04831       Matrix ext (1, 2, 0.0);
04832       ext = get_ticklabel_extents (get_xtick ().matrix_value (),
04833                                    get_xticklabel ().all_strings (),
04834                                    get_xlim ().matrix_value ());
04835 
04836       double wmax = ext(0), hmax = ext(1), angle = 0;
04837       ColumnVector p =
04838         graphics_xform::xform_vector ((xpTickN+xpTick)/2, ypTick, zpTick);
04839 
04840       bool tick_along_z = nearhoriz || xisinf (fy);
04841       if (tick_along_z)
04842         p(2) += (signum(zpTick-zpTickN)*fz*xtickoffset);
04843       else
04844         p(1) += (signum(ypTick-ypTickN)*fy*xtickoffset);
04845 
04846       p = xform.transform (p(0), p(1), p(2), false);
04847 
04848       switch (xstate)
04849         {
04850           case AXE_ANY_DIR:
04851             p(0) += (xyzSym ? wmax : -wmax);
04852             p(1) += hmax;
04853             break;
04854 
04855           case AXE_VERT_DIR:
04856             p(0) -= wmax;
04857             angle = 90;
04858             break;
04859 
04860           case AXE_HORZ_DIR:
04861             p(1) += (x2Dtop ? -hmax : hmax);
04862             break;
04863         }
04864 
04865       if (xlabel_props.positionmode_is ("auto"))
04866         {
04867           p = xform.untransform (p(0), p(1), p(2), true);
04868           xlabel_props.set_position (p.extract_n (0, 3).transpose ());
04869           xlabel_props.set_positionmode ("auto");
04870         }
04871 
04872       if (! is_empty && xlabel_props.rotationmode_is ("auto"))
04873         {
04874           xlabel_props.set_rotation (angle);
04875           xlabel_props.set_rotationmode ("auto");
04876         }
04877     }
04878 }
04879 
04880 static bool updating_ylabel_position = false;
04881 
04882 void
04883 axes::properties::update_ylabel_position (void)
04884 {
04885   if (updating_ylabel_position)
04886     return;
04887 
04888   text::properties& ylabel_props = reinterpret_cast<text::properties&>
04889     (gh_manager::get_object (get_ylabel ()).get_properties ());
04890 
04891   bool is_empty = ylabel_props.get_string ().is_empty ();
04892 
04893   unwind_protect frame;
04894   frame.protect_var (updating_ylabel_position);
04895   updating_ylabel_position = true;
04896 
04897   if (! is_empty)
04898     {
04899       if (ylabel_props.horizontalalignmentmode_is ("auto"))
04900         {
04901           ylabel_props.set_horizontalalignment
04902             (ystate > AXE_DEPTH_DIR
04903              ? "center" : (!xyzSym ? "left" : "right"));
04904 
04905           ylabel_props.set_horizontalalignmentmode ("auto");
04906         }
04907 
04908       if (ylabel_props.verticalalignmentmode_is ("auto"))
04909         {
04910           ylabel_props.set_verticalalignment
04911             (ystate == AXE_VERT_DIR && !y2Dright ? "bottom" : "top");
04912 
04913           ylabel_props.set_verticalalignmentmode ("auto");
04914         }
04915     }
04916 
04917   if (ylabel_props.positionmode_is ("auto")
04918       || ylabel_props.rotationmode_is ("auto"))
04919     {
04920       graphics_xform xform = get_transform ();
04921 
04922       Matrix ext (1, 2, 0.0);
04923       ext = get_ticklabel_extents (get_ytick ().matrix_value (),
04924                                    get_yticklabel ().all_strings (),
04925                                    get_ylim ().matrix_value ());
04926 
04927       double wmax = ext(0), hmax = ext(1), angle = 0;
04928       ColumnVector p =
04929         graphics_xform::xform_vector (xpTick, (ypTickN+ypTick)/2, zpTick);
04930 
04931       bool tick_along_z = nearhoriz || xisinf (fx);
04932       if (tick_along_z)
04933         p(2) += (signum(zpTick-zpTickN)*fz*ytickoffset);
04934       else
04935         p(0) += (signum(xpTick-xpTickN)*fx*ytickoffset);
04936 
04937       p = xform.transform (p(0), p(1), p(2), false);
04938 
04939       switch (ystate)
04940         {
04941           case AXE_ANY_DIR:
04942             p(0) += (!xyzSym ? wmax : -wmax);
04943             p(1) += hmax;
04944             break;
04945 
04946           case AXE_VERT_DIR:
04947             p(0) += (y2Dright ? wmax : -wmax);
04948             angle = 90;
04949             break;
04950 
04951           case AXE_HORZ_DIR:
04952             p(1) += hmax;
04953             break;
04954         }
04955 
04956       if (ylabel_props.positionmode_is ("auto"))
04957         {
04958           p = xform.untransform (p(0), p(1), p(2), true);
04959           ylabel_props.set_position (p.extract_n (0, 3).transpose ());
04960           ylabel_props.set_positionmode ("auto");
04961         }
04962 
04963       if (! is_empty && ylabel_props.rotationmode_is ("auto"))
04964         {
04965           ylabel_props.set_rotation (angle);
04966           ylabel_props.set_rotationmode ("auto");
04967         }
04968     }
04969 }
04970 
04971 static bool updating_zlabel_position = false;
04972 
04973 void
04974 axes::properties::update_zlabel_position (void)
04975 {
04976   if (updating_zlabel_position)
04977     return;
04978 
04979   text::properties& zlabel_props = reinterpret_cast<text::properties&>
04980     (gh_manager::get_object (get_zlabel ()).get_properties ());
04981 
04982   bool camAuto = cameraupvectormode_is ("auto");
04983   bool is_empty = zlabel_props.get_string ().is_empty ();
04984 
04985   unwind_protect frame;
04986   frame.protect_var (updating_zlabel_position);
04987   updating_zlabel_position = true;
04988 
04989   if (! is_empty)
04990     {
04991       if (zlabel_props.horizontalalignmentmode_is ("auto"))
04992         {
04993           zlabel_props.set_horizontalalignment
04994             ((zstate > AXE_DEPTH_DIR || camAuto) ? "center" : "right");
04995 
04996           zlabel_props.set_horizontalalignmentmode ("auto");
04997         }
04998 
04999       if (zlabel_props.verticalalignmentmode_is ("auto"))
05000         {
05001           zlabel_props.set_verticalalignment
05002             (zstate == AXE_VERT_DIR
05003              ? "bottom" : ((zSign || camAuto) ? "bottom" : "top"));
05004 
05005           zlabel_props.set_verticalalignmentmode ("auto");
05006         }
05007     }
05008 
05009   if (zlabel_props.positionmode_is ("auto")
05010       || zlabel_props.rotationmode_is ("auto"))
05011     {
05012       graphics_xform xform = get_transform ();
05013 
05014       Matrix ext (1, 2, 0.0);
05015       ext = get_ticklabel_extents (get_ztick ().matrix_value (),
05016                                    get_zticklabel ().all_strings (),
05017                                    get_zlim ().matrix_value ());
05018 
05019       double wmax = ext(0), hmax = ext(1), angle = 0;
05020       ColumnVector p;
05021 
05022       if (xySym)
05023         {
05024           p = graphics_xform::xform_vector (xPlaneN, yPlane,
05025                                             (zpTickN+zpTick)/2);
05026           if (xisinf (fy))
05027             p(0) += (signum(xPlaneN-xPlane)*fx*ztickoffset);
05028           else
05029             p(1) += (signum(yPlane-yPlaneN)*fy*ztickoffset);
05030         }
05031       else
05032         {
05033           p = graphics_xform::xform_vector (xPlane, yPlaneN,
05034                                             (zpTickN+zpTick)/2);
05035           if (xisinf (fx))
05036             p(1) += (signum(yPlaneN-yPlane)*fy*ztickoffset);
05037           else
05038             p(0) += (signum(xPlane-xPlaneN)*fx*ztickoffset);
05039         }
05040 
05041       p = xform.transform (p(0), p(1), p(2), false);
05042 
05043       switch (zstate)
05044         {
05045           case AXE_ANY_DIR:
05046             if (camAuto)
05047               {
05048                 p(0) -= wmax;
05049                 angle = 90;
05050               }
05051 
05052             // FIXME -- what's the correct offset?
05053             //
05054             //   p[0] += (!xySym ? wmax : -wmax);
05055             //   p[1] += (zSign ? hmax : -hmax);
05056 
05057             break;
05058 
05059           case AXE_VERT_DIR:
05060             p(0) -= wmax;
05061             angle = 90;
05062             break;
05063 
05064           case AXE_HORZ_DIR:
05065             p(1) += hmax;
05066             break;
05067         }
05068 
05069       if (zlabel_props.positionmode_is ("auto"))
05070         {
05071           p = xform.untransform (p(0), p(1), p(2), true);
05072           zlabel_props.set_position (p.extract_n (0, 3).transpose ());
05073           zlabel_props.set_positionmode ("auto");
05074         }
05075 
05076       if (! is_empty && zlabel_props.rotationmode_is ("auto"))
05077         {
05078           zlabel_props.set_rotation (angle);
05079           zlabel_props.set_rotationmode ("auto");
05080         }
05081     }
05082 }
05083 
05084 static bool updating_title_position = false;
05085 
05086 void
05087 axes::properties::update_title_position (void)
05088 {
05089   if (updating_title_position)
05090     return;
05091 
05092   text::properties& title_props = reinterpret_cast<text::properties&>
05093     (gh_manager::get_object (get_title ()).get_properties ());
05094 
05095   unwind_protect frame;
05096   frame.protect_var (updating_title_position);
05097   updating_title_position = true;
05098 
05099   if (title_props.positionmode_is ("auto"))
05100     {
05101       graphics_xform xform = get_transform ();
05102 
05103       // FIXME: bbox should be stored in axes::properties
05104       Matrix bbox = get_extent (false);
05105 
05106       ColumnVector p =
05107         graphics_xform::xform_vector (bbox(0)+bbox(2)/2,
05108                                       bbox(1)-10,
05109                                       (x_zlim(0)+x_zlim(1))/2);
05110 
05111       if (x2Dtop)
05112         {
05113           Matrix ext (1, 2, 0.0);
05114           ext = get_ticklabel_extents (get_xtick ().matrix_value (),
05115                                        get_xticklabel ().all_strings (),
05116                                        get_xlim ().matrix_value ());
05117           p(1) -= ext(1);
05118         }
05119 
05120       p = xform.untransform (p(0), p(1), p(2), true);
05121 
05122       title_props.set_position (p.extract_n(0, 3).transpose ());
05123       title_props.set_positionmode ("auto");
05124     }
05125 }
05126 
05127 void
05128 axes::properties::update_autopos (const std::string& elem_type)
05129 {
05130   if (elem_type == "xlabel")
05131     update_xlabel_position ();
05132   else if (elem_type == "ylabel")
05133     update_ylabel_position ();
05134   else if (elem_type == "zlabel")
05135     update_zlabel_position ();
05136   else if (elem_type == "title")
05137     update_title_position ();
05138   else if (elem_type == "sync")
05139     sync_positions ();
05140 }
05141 
05142 static void
05143 normalized_aspectratios (Matrix& aspectratios, const Matrix& scalefactors,
05144                          double xlength, double ylength, double zlength)
05145 {
05146       double xval = xlength/scalefactors(0);
05147       double yval = ylength/scalefactors(1);
05148       double zval = zlength/scalefactors(2);
05149 
05150       double minval = xmin (xmin (xval, yval), zval);
05151 
05152       aspectratios(0) = xval/minval;
05153       aspectratios(1) = yval/minval;
05154       aspectratios(2) = zval/minval;
05155 }
05156 
05157 static void
05158 max_axes_scale (double& s, Matrix& limits, const Matrix& kids,
05159                 double pbfactor, double dafactor, char limit_type, bool tight)
05160 {
05161   if (tight)
05162     {
05163       double minval = octave_Inf;
05164       double maxval = -octave_Inf;
05165       double min_pos = octave_Inf;
05166       double max_neg = -octave_Inf;
05167       get_children_limits (minval, maxval, min_pos, max_neg, kids, limit_type);
05168       if (!xisinf (minval) && !xisnan (minval)
05169           && !xisinf (maxval) && !xisnan (maxval))
05170         {
05171           limits(0) = minval;
05172           limits(1) = maxval;
05173           s = xmax(s, (maxval - minval) / (pbfactor * dafactor));
05174         }
05175     }
05176   else
05177     s = xmax(s, (limits(1) - limits(0)) / (pbfactor * dafactor));
05178 }
05179 
05180 static bool updating_aspectratios = false;
05181 
05182 void
05183 axes::properties::update_aspectratios (void)
05184 {
05185   if (updating_aspectratios)
05186     return;
05187 
05188   Matrix xlimits = get_xlim ().matrix_value ();
05189   Matrix ylimits = get_ylim ().matrix_value ();
05190   Matrix zlimits = get_zlim ().matrix_value ();
05191 
05192   double dx = (xlimits(1)-xlimits(0));
05193   double dy = (ylimits(1)-ylimits(0));
05194   double dz = (zlimits(1)-zlimits(0));
05195 
05196   Matrix da = get_dataaspectratio ().matrix_value ();
05197   Matrix pba = get_plotboxaspectratio ().matrix_value ();
05198 
05199   if (dataaspectratiomode_is ("auto"))
05200     {
05201       if (plotboxaspectratiomode_is ("auto"))
05202         {
05203           pba = Matrix (1, 3, 1.0);
05204           plotboxaspectratio.set (pba, false);
05205         }
05206 
05207       normalized_aspectratios (da, pba, dx, dy, dz);
05208       dataaspectratio.set (da, false);
05209     }
05210   else if (plotboxaspectratiomode_is ("auto"))
05211     {
05212       normalized_aspectratios (pba, da, dx, dy, dz);
05213       plotboxaspectratio.set (pba, false);
05214     }
05215   else
05216     {
05217       double s = -octave_Inf;
05218       bool modified_limits = false;
05219       Matrix kids;
05220 
05221       if (xlimmode_is ("auto") && ylimmode_is ("auto") && zlimmode_is ("auto"))
05222         {
05223           modified_limits = true;
05224           kids = get_children ();
05225           max_axes_scale (s, xlimits, kids, pba(0), da(0), 'x', true);
05226           max_axes_scale (s, ylimits, kids, pba(1), da(1), 'y', true);
05227           max_axes_scale (s, zlimits, kids, pba(2), da(2), 'z', true);
05228         }
05229       else if (xlimmode_is ("auto") && ylimmode_is ("auto"))
05230         {
05231           modified_limits = true;
05232           max_axes_scale (s, zlimits, kids, pba(2), da(2), 'z', false);
05233         }
05234       else if (ylimmode_is ("auto") && zlimmode_is ("auto"))
05235         {
05236           modified_limits = true;
05237           max_axes_scale (s, xlimits, kids, pba(0), da(0), 'x', false);
05238         }
05239       else if (zlimmode_is ("auto") && xlimmode_is ("auto"))
05240         {
05241           modified_limits = true;
05242           max_axes_scale (s, ylimits, kids, pba(1), da(1), 'y', false);
05243         }
05244 
05245       if (modified_limits)
05246         {
05247 
05248           unwind_protect frame;
05249           frame.protect_var (updating_aspectratios);
05250 
05251           updating_aspectratios = true;
05252 
05253           dx = pba(0) *da(0);
05254           dy = pba(1) *da(1);
05255           dz = pba(2) *da(2);
05256           if (xisinf (s))
05257             s = 1 / xmin (xmin (dx, dy), dz);
05258 
05259           if (xlimmode_is ("auto"))
05260             {
05261               dx = s * dx;
05262               xlimits(0) = 0.5 * (xlimits(0) + xlimits(1) - dx);
05263               xlimits(1) = xlimits(0) + dx;
05264               set_xlim (xlimits);
05265               set_xlimmode ("auto");
05266             }
05267 
05268           if (ylimmode_is ("auto"))
05269             {
05270               dy = s * dy;
05271               ylimits(0) = 0.5 * (ylimits(0) + ylimits(1) - dy);
05272               ylimits(1) = ylimits(0) + dy;
05273               set_ylim (ylimits);
05274               set_ylimmode ("auto");
05275             }
05276 
05277           if (zlimmode_is ("auto"))
05278             {
05279               dz = s * dz;
05280               zlimits(0) = 0.5 * (zlimits(0) + zlimits(1) - dz);
05281               zlimits(1) = zlimits(0) + dz;
05282               set_zlim (zlimits);
05283               set_zlimmode ("auto");
05284             }
05285         }
05286       else
05287         {
05288           normalized_aspectratios (pba, da, dx, dy, dz);
05289           plotboxaspectratio.set (pba, false);
05290         }
05291     }
05292 }
05293 
05294 void
05295 axes::properties::update_font (void)
05296 {
05297 #ifdef HAVE_FREETYPE
05298 #ifdef HAVE_FONTCONFIG
05299   text_renderer.set_font (get ("fontname").string_value (),
05300                           get ("fontweight").string_value (),
05301                           get ("fontangle").string_value (),
05302                           get ("fontsize").double_value ());
05303 #endif
05304 #endif
05305 }
05306 
05307 // The INTERNAL flag defines whether position or outerposition is used.
05308 
05309 Matrix
05310 axes::properties::get_boundingbox (bool internal,
05311                                    const Matrix& parent_pix_size) const
05312 {
05313   Matrix pos = (internal ?
05314                   get_position ().matrix_value ()
05315                   : get_outerposition ().matrix_value ());
05316   Matrix parent_size (parent_pix_size);
05317 
05318   if (parent_size.numel () == 0)
05319     {
05320       graphics_object obj = gh_manager::get_object (get_parent ());
05321 
05322       parent_size =
05323        obj.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
05324     }
05325 
05326   pos = convert_position (pos, get_units (), "pixels", parent_size);
05327 
05328   pos(0)--;
05329   pos(1)--;
05330   pos(1) = parent_size(1) - pos(1) - pos(3);
05331 
05332   return pos;
05333 }
05334 
05335 Matrix
05336 axes::properties::get_extent (bool with_text, bool only_text_height) const
05337 {
05338   graphics_xform xform = get_transform ();
05339 
05340   Matrix ext (1, 4, 0.0);
05341   ext(0) = octave_Inf;
05342   ext(1) = octave_Inf;
05343   ext(2) = -octave_Inf;
05344   ext(3) = -octave_Inf;
05345   for (int i = 0; i <= 1; i++)
05346     for (int j = 0; j <= 1; j++)
05347       for (int k = 0; k <= 1; k++)
05348         {
05349           ColumnVector p = xform.transform (i ? xPlaneN : xPlane,
05350                                             j ? yPlaneN : yPlane,
05351                                             k ? zPlaneN : zPlane, false);
05352           ext(0) = std::min (ext(0), p(0));
05353           ext(1) = std::min (ext(1), p(1));
05354           ext(2) = std::max (ext(2), p(0));
05355           ext(3) = std::max (ext(3), p(1));
05356         }
05357 
05358   if (with_text)
05359     {
05360       for (int i = 0; i < 4; i++)
05361         {
05362           graphics_handle text_handle;
05363           if (i == 0)
05364             text_handle = get_title ();
05365           else if (i == 1)
05366             text_handle = get_xlabel ();
05367           else if (i == 2)
05368             text_handle = get_ylabel ();
05369           else if (i == 3)
05370             text_handle = get_zlabel ();
05371 
05372           text::properties& text_props = reinterpret_cast<text::properties&>
05373             (gh_manager::get_object (text_handle).get_properties ());
05374 
05375           Matrix text_pos = text_props.get_position ().matrix_value ();
05376           text_pos = xform.transform (text_pos(0), text_pos(1), text_pos(2));
05377           if (text_props.get_string ().is_empty ())
05378             {
05379               ext(0) = std::min (ext(0), text_pos(0));
05380               ext(1) = std::min (ext(1), text_pos(1));
05381               ext(2) = std::max (ext(2), text_pos(0));
05382               ext(3) = std::max (ext(3), text_pos(1));
05383             }
05384           else
05385             {
05386               Matrix text_ext = text_props.get_extent_matrix ();
05387 
05388               bool ignore_horizontal = false;
05389               bool ignore_vertical = false;
05390               if (only_text_height)
05391                 {
05392                   double text_rotation = text_props.get_rotation();
05393                   if (text_rotation == 0. || text_rotation == 180.)
05394                       ignore_horizontal = true;
05395                   else if (text_rotation == 90. || text_rotation == 270.)
05396                       ignore_vertical = true;
05397                 }
05398 
05399               if (! ignore_horizontal)
05400                 {
05401                   ext(0) = std::min (ext(0), text_pos(0)+text_ext(0));
05402                   ext(2) = std::max (ext(2), text_pos(0)+text_ext(0)+text_ext(2));
05403                 }
05404 
05405               if (! ignore_vertical)
05406                 {
05407                   ext(1) = std::min (ext(1), text_pos(1)-text_ext(1)-text_ext(3));
05408                   ext(3) = std::max (ext(3), text_pos(1)-text_ext(1));
05409                 }
05410             }
05411         }
05412     }
05413 
05414   ext(2) = ext(2)-ext(0);
05415   ext(3) = ext(3)-ext(1);
05416 
05417   return ext;
05418 }
05419 
05420 void
05421 axes::properties::set_units (const octave_value& v)
05422 {
05423   if (! error_state)
05424     {
05425       caseless_str old_units = get_units ();
05426       if (units.set (v, true))
05427         {
05428           update_units (old_units);
05429           mark_modified ();
05430         }
05431     }
05432 }
05433 
05434 void
05435 axes::properties::update_units (const caseless_str& old_units)
05436 {
05437   graphics_object obj = gh_manager::get_object (get_parent ());
05438   Matrix parent_bb = obj.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
05439   caseless_str new_units = get_units ();
05440   position.set (octave_value (convert_position (get_position().matrix_value(), old_units, new_units, parent_bb)), false);
05441   outerposition.set (octave_value (convert_position (get_outerposition().matrix_value(), old_units, new_units, parent_bb)), false);
05442   tightinset.set (octave_value (convert_position (get_tightinset().matrix_value(), old_units, new_units, parent_bb)), false);
05443 }
05444 
05445 void
05446 axes::properties::set_fontunits (const octave_value& v)
05447 {
05448   if (! error_state)
05449     {
05450       caseless_str old_fontunits = get_fontunits ();
05451       if (fontunits.set (v, true))
05452         {
05453           update_fontunits (old_fontunits);
05454           mark_modified ();
05455         }
05456     }
05457 }
05458 
05459 void
05460 axes::properties::update_fontunits (const caseless_str& old_units)
05461 {
05462   caseless_str new_units = get_fontunits ();
05463   double parent_height = get_boundingbox (true).elem (3);
05464   double fsz = get_fontsize ();
05465 
05466   fsz = convert_font_size (fsz, old_units, new_units, parent_height);
05467 
05468   set_fontsize (octave_value (fsz));
05469 }
05470 
05471 double
05472 axes::properties::get_fontsize_points (double box_pix_height) const
05473 {
05474   double fs = get_fontsize ();
05475   double parent_height = box_pix_height;
05476 
05477   if (fontunits_is ("normalized") && parent_height <= 0)
05478     parent_height = get_boundingbox (true).elem(3);
05479 
05480   return convert_font_size (fs, get_fontunits (), "points", parent_height);
05481 }
05482 
05483 ColumnVector
05484 graphics_xform::xform_vector (double x, double y, double z)
05485 {
05486   return ::xform_vector (x, y, z);
05487 }
05488 
05489 Matrix
05490 graphics_xform::xform_eye (void)
05491 {
05492   return ::xform_matrix ();
05493 }
05494 
05495 ColumnVector
05496 graphics_xform::transform (double x, double y, double z,
05497                            bool use_scale) const
05498 {
05499   if (use_scale)
05500     {
05501       x = sx.scale (x);
05502       y = sy.scale (y);
05503       z = sz.scale (z);
05504     }
05505 
05506   return ::transform (xform, x, y, z);
05507 }
05508 
05509 ColumnVector
05510 graphics_xform::untransform (double x, double y, double z,
05511                              bool use_scale) const
05512 {
05513   ColumnVector v = ::transform (xform_inv, x, y, z);
05514 
05515   if (use_scale)
05516     {
05517       v(0) = sx.unscale (v(0));
05518       v(1) = sy.unscale (v(1));
05519       v(2) = sz.unscale (v(2));
05520     }
05521 
05522   return v;
05523 }
05524 
05525 octave_value
05526 axes::get_default (const caseless_str& name) const
05527 {
05528   octave_value retval = default_properties.lookup (name);
05529 
05530   if (retval.is_undefined ())
05531     {
05532       graphics_handle parent = get_parent ();
05533       graphics_object parent_obj = gh_manager::get_object (parent);
05534 
05535       retval = parent_obj.get_default (name);
05536     }
05537 
05538   return retval;
05539 }
05540 
05541 // FIXME -- remove.
05542 // FIXME -- maybe this should go into array_property class?
05543 /*
05544 static void
05545 check_limit_vals (double& min_val, double& max_val,
05546                   double& min_pos, double& max_neg,
05547                   const array_property& data)
05548 {
05549   double val = data.min_val ();
05550   if (! (xisinf (val) || xisnan (val)) && val < min_val)
05551     min_val = val;
05552   val = data.max_val ();
05553   if (! (xisinf (val) || xisnan (val)) && val > max_val)
05554     max_val = val;
05555   val = data.min_pos ();
05556   if (! (xisinf (val) || xisnan (val)) && val > 0 && val < min_pos)
05557     min_pos = val;
05558   val = data.max_neg ();
05559   if (! (xisinf (val) || xisnan (val)) && val < 0 && val > max_neg)
05560     max_neg = val;
05561 }
05562 */
05563 
05564 static void
05565 check_limit_vals (double& min_val, double& max_val,
05566                   double& min_pos, double& max_neg,
05567                   const octave_value& data)
05568 {
05569   if (data.is_matrix_type ())
05570     {
05571       Matrix m = data.matrix_value ();
05572 
05573       if (! error_state && m.numel () == 4)
05574         {
05575           double val;
05576 
05577           val = m(0);
05578           if (! (xisinf (val) || xisnan (val)) && val < min_val)
05579             min_val = val;
05580 
05581           val = m(1);
05582           if (! (xisinf (val) || xisnan (val)) && val > max_val)
05583             max_val = val;
05584 
05585           val = m(2);
05586           if (! (xisinf (val) || xisnan (val)) && val > 0 && val < min_pos)
05587             min_pos = val;
05588 
05589           val = m(3);
05590           if (! (xisinf (val) || xisnan (val)) && val < 0 && val > max_neg)
05591             max_neg = val;
05592         }
05593     }
05594 }
05595 
05596 // magform(x) Returns (a, b), where x = a * 10^b, abs (a) >= 1., and b is
05597 // integer.
05598 
05599 static void
05600 magform (double x, double& a, int& b)
05601 {
05602   if (x == 0)
05603     {
05604       a = 0;
05605       b = 0;
05606     }
05607   else
05608     {
05609       b = static_cast<int> (gnulib::floor (std::log10 (std::abs (x))));
05610       a = x / std::pow (10.0, b);
05611     }
05612 }
05613 
05614 // A translation from Tom Holoryd's python code at
05615 // http://kurage.nimh.nih.gov/tomh/tics.py
05616 // FIXME -- add log ticks
05617 
05618 double
05619 axes::properties::calc_tick_sep (double lo, double hi)
05620 {
05621   int ticint = 5;
05622 
05623   // Reference: Lewart, C. R., "Algorithms SCALE1, SCALE2, and
05624   // SCALE3 for Determination of Scales on Computer Generated
05625   // Plots", Communications of the ACM, 10 (1973), 639-640.
05626   // Also cited as ACM Algorithm 463.
05627 
05628   double a;
05629   int b, x;
05630 
05631   magform ((hi-lo)/ticint, a, b);
05632 
05633   static const double sqrt_2 = sqrt (2.0);
05634   static const double sqrt_10 = sqrt (10.0);
05635   static const double sqrt_50 = sqrt (50.0);
05636 
05637   if (a < sqrt_2)
05638     x = 1;
05639   else if (a < sqrt_10)
05640     x = 2;
05641   else if (a < sqrt_50)
05642     x = 5;
05643   else
05644     x = 10;
05645 
05646   return x * std::pow (10., b);
05647 
05648 }
05649 
05650 // Attempt to make "nice" limits from the actual max and min of the
05651 // data.  For log plots, we will also use the smallest strictly positive
05652 // value.
05653 
05654 Matrix
05655 axes::properties::get_axis_limits (double xmin, double xmax,
05656                                    double min_pos, double max_neg,
05657                                    bool logscale)
05658 {
05659   Matrix retval;
05660 
05661   double min_val = xmin;
05662   double max_val = xmax;
05663 
05664   if (xisinf (min_val) && min_val > 0 && xisinf (max_val) && max_val < 0)
05665     {
05666       retval = default_lim (logscale);
05667       return retval;
05668     }
05669   else if (! (xisinf (min_val) || xisinf (max_val)))
05670     {
05671       if (logscale)
05672         {
05673           if (xisinf (min_pos) && xisinf (max_neg))
05674             {
05675               // TODO -- max_neg is needed for "loglog ([0 -Inf])"
05676               //         This is the only place where max_neg is needed.
05677               //         Is there another way?
05678               retval = default_lim ();
05679               retval(0) = pow (10., retval(0));
05680               retval(1) = pow (10., retval(1));
05681               return retval;
05682             }
05683           if ((min_val <= 0 && max_val > 0))
05684             {
05685               warning ("axis: omitting non-positive data in log plot");
05686               min_val = min_pos;
05687             }
05688           // FIXME -- maybe this test should also be relative?
05689           if (std::abs (min_val - max_val) < sqrt (DBL_EPSILON))
05690             {
05691               min_val *= 0.9;
05692               max_val *= 1.1;
05693             }
05694           if (min_val > 0)
05695             {
05696               // Log plots with all positive data
05697               min_val = pow (10, gnulib::floor (log10 (min_val)));
05698               max_val = pow (10, std::ceil (log10 (max_val)));
05699             }
05700           else
05701             {
05702               // Log plots with all negative data
05703               min_val = -pow (10, std::ceil (log10 (-min_val)));
05704               max_val = -pow (10, gnulib::floor (log10 (-max_val)));
05705             }
05706         }
05707       else
05708         {
05709           if (min_val == 0 && max_val == 0)
05710             {
05711               min_val = -1;
05712               max_val = 1;
05713             }
05714           // FIXME -- maybe this test should also be relative?
05715           else if (std::abs (min_val - max_val) < sqrt (DBL_EPSILON))
05716             {
05717               min_val -= 0.1 * std::abs (min_val);
05718               max_val += 0.1 * std::abs (max_val);
05719             }
05720 
05721           double tick_sep = calc_tick_sep (min_val , max_val);
05722           double min_tick = gnulib::floor (min_val / tick_sep);
05723           double max_tick = std::ceil (max_val / tick_sep);
05724           // Prevent round-off from cropping ticks
05725           min_val = std::min (min_val, tick_sep * min_tick);
05726           max_val = std::max (max_val, tick_sep * max_tick);
05727         }
05728     }
05729 
05730   retval.resize (1, 2);
05731 
05732   retval(1) = max_val;
05733   retval(0) = min_val;
05734 
05735   return retval;
05736 }
05737 
05738 void
05739 axes::properties::calc_ticks_and_lims (array_property& lims,
05740                                        array_property& ticks,
05741                                        array_property& mticks,
05742                                        bool limmode_is_auto, bool is_logscale)
05743 {
05744   // FIXME -- add log ticks and lims
05745 
05746   if (lims.get ().is_empty ())
05747     return;
05748 
05749   double lo = (lims.get ().matrix_value ()) (0);
05750   double hi = (lims.get ().matrix_value ()) (1);
05751   bool is_negative = lo < 0 && hi < 0;
05752   double tmp;
05753   // FIXME should this be checked for somewhere else? (i.e. set{x,y,z}lim)
05754   if (hi < lo)
05755     {
05756       tmp = hi;
05757       hi = lo;
05758       lo = tmp;
05759     }
05760 
05761   if (is_logscale)
05762     {
05763       if (is_negative)
05764         {
05765           tmp = hi;
05766           hi = std::log10 (-lo);
05767           lo = std::log10 (-tmp);
05768         }
05769       else
05770         {
05771           hi = std::log10 (hi);
05772           lo = std::log10 (lo);
05773         }
05774     }
05775 
05776   double tick_sep = calc_tick_sep (lo , hi);
05777 
05778   if (is_logscale && ! (xisinf (hi) || xisinf (lo)))
05779     {
05780       // FIXME - what if (hi-lo) < tick_sep?
05781       //         ex: loglog ([1 1.1])
05782       tick_sep = std::max (tick_sep, 1.);
05783       tick_sep = std::ceil (tick_sep);
05784     }
05785 
05786   int i1 = static_cast<int> (gnulib::floor (lo / tick_sep));
05787   int i2 = static_cast<int> (std::ceil (hi / tick_sep));
05788 
05789   if (limmode_is_auto)
05790     {
05791       // adjust limits to include min and max tics
05792       Matrix tmp_lims (1,2);
05793       tmp_lims(0) = std::min (tick_sep * i1, lo);
05794       tmp_lims(1) = std::max (tick_sep * i2, hi);
05795 
05796       if (is_logscale)
05797         {
05798           tmp_lims(0) = std::pow (10.,tmp_lims(0));
05799           tmp_lims(1) = std::pow (10.,tmp_lims(1));
05800           if (tmp_lims(0) <= 0)
05801             tmp_lims(0) = std::pow (10., lo);
05802           if (is_negative)
05803             {
05804               tmp = tmp_lims(0);
05805               tmp_lims(0) = -tmp_lims(1);
05806               tmp_lims(1) = -tmp;
05807             }
05808         }
05809       lims = tmp_lims;
05810     }
05811 
05812   Matrix tmp_ticks (1, i2-i1+1);
05813   for (int i = 0; i <= i2-i1; i++)
05814     {
05815       tmp_ticks (i) = tick_sep * (i+i1);
05816       if (is_logscale)
05817         tmp_ticks (i) = std::pow (10., tmp_ticks (i));
05818     }
05819   if (is_logscale && is_negative)
05820     {
05821       Matrix rev_ticks (1, i2-i1+1);
05822       rev_ticks = -tmp_ticks;
05823       for (int i = 0; i <= i2-i1; i++)
05824         tmp_ticks (i) = rev_ticks (i2-i1-i);
05825     }
05826 
05827   ticks = tmp_ticks;
05828 
05829   int n = is_logscale ? 8 : 4;
05830   Matrix tmp_mticks (1, n * (tmp_ticks.numel () - 1));
05831 
05832   for (int i = 0; i < tmp_ticks.numel ()-1; i++)
05833     {
05834       double d = (tmp_ticks (i+1) - tmp_ticks (i)) / (n+1);
05835       for (int j = 0; j < n; j++)
05836         {
05837           tmp_mticks (n*i+j) = tmp_ticks (i) + d * (j+1);
05838         }
05839     }
05840   mticks = tmp_mticks;
05841 }
05842 
05843 void
05844 axes::properties::calc_ticklabels (const array_property& ticks,
05845                                    any_property& labels, bool logscale)
05846 {
05847   Matrix values = ticks.get ().matrix_value ();
05848   Cell c (values.dims ());
05849   std::ostringstream os;
05850 
05851   if (logscale)
05852     {
05853       double significand;
05854       double exponent;
05855       double exp_max = 0.;
05856       double exp_min = 0.;
05857 
05858       for (int i = 0; i < values.numel (); i++)
05859         {
05860           exp_max = std::max (exp_max, std::log10 (values(i)));
05861           exp_min = std::max (exp_min, std::log10 (values(i)));
05862         }
05863 
05864       for (int i = 0; i < values.numel (); i++)
05865         {
05866           if (values(i) < 0.)
05867             exponent = gnulib::floor (std::log10 (-values(i)));
05868           else
05869             exponent = gnulib::floor (std::log10 (values(i)));
05870           significand = values(i) * std::pow (10., -exponent);
05871           os.str (std::string ());
05872           os << significand;
05873           if (exponent < 0.)
05874             {
05875               os << "e-";
05876               exponent = -exponent;
05877             }
05878           else
05879             os << "e+";
05880           if (exponent < 10. && (exp_max > 9 || exp_min < -9))
05881             os << "0";
05882           os << exponent;
05883           c(i) = os.str ();
05884         }
05885     }
05886   else
05887     {
05888       for (int i = 0; i < values.numel (); i++)
05889         {
05890           os.str (std::string ());
05891           os << values(i);
05892           c(i) = os.str ();
05893         }
05894     }
05895 
05896   labels = c;
05897 }
05898 
05899 Matrix
05900 axes::properties::get_ticklabel_extents (const Matrix& ticks,
05901                                          const string_vector& ticklabels,
05902                                          const Matrix& limits)
05903 {
05904 #ifndef HAVE_FREETYPE
05905   double fontsize = get ("fontsize").double_value ();
05906 #endif
05907 
05908   Matrix ext (1, 2, 0.0);
05909   double wmax = 0., hmax = 0.;
05910   int n = std::min (ticklabels.numel (), ticks.numel ());
05911   for (int i = 0; i < n; i++)
05912     {
05913       double val = ticks(i);
05914       if (limits(0) <= val && val <= limits(1))
05915         {
05916 #ifdef HAVE_FREETYPE
05917           ext = text_renderer.get_extent (ticklabels(i));
05918           wmax = std::max (wmax, ext(0));
05919           hmax = std::max (hmax, ext(1));
05920 #else
05921           //FIXME: find a better approximation
05922           int len = ticklabels(i).length();
05923           wmax = std::max (wmax, 0.5*fontsize*len);
05924           hmax = fontsize;
05925 #endif
05926         }
05927     }
05928 
05929   ext(0) = wmax;
05930   ext(1) = hmax;
05931   return ext;
05932 }
05933 
05934 void
05935 get_children_limits (double& min_val, double& max_val,
05936                      double& min_pos, double& max_neg,
05937                      const Matrix& kids, char limit_type)
05938 {
05939   octave_idx_type n = kids.numel ();
05940 
05941   switch (limit_type)
05942     {
05943     case 'x':
05944       for (octave_idx_type i = 0; i < n; i++)
05945         {
05946           graphics_object obj = gh_manager::get_object (kids(i));
05947 
05948           if (obj.is_xliminclude ())
05949             {
05950               octave_value lim = obj.get_xlim ();
05951 
05952               check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
05953             }
05954         }
05955       break;
05956 
05957     case 'y':
05958       for (octave_idx_type i = 0; i < n; i++)
05959         {
05960           graphics_object obj = gh_manager::get_object (kids(i));
05961 
05962           if (obj.is_yliminclude ())
05963             {
05964               octave_value lim = obj.get_ylim ();
05965 
05966               check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
05967             }
05968         }
05969       break;
05970 
05971     case 'z':
05972       for (octave_idx_type i = 0; i < n; i++)
05973         {
05974           graphics_object obj = gh_manager::get_object (kids(i));
05975 
05976           if (obj.is_zliminclude ())
05977             {
05978               octave_value lim = obj.get_zlim ();
05979 
05980               check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
05981             }
05982         }
05983       break;
05984 
05985     case 'c':
05986       for (octave_idx_type i = 0; i < n; i++)
05987         {
05988           graphics_object obj = gh_manager::get_object (kids(i));
05989 
05990           if (obj.is_climinclude ())
05991             {
05992               octave_value lim = obj.get_clim ();
05993 
05994               check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
05995             }
05996         }
05997       break;
05998 
05999     case 'a':
06000       for (octave_idx_type i = 0; i < n; i++)
06001         {
06002           graphics_object obj = gh_manager::get_object (kids(i));
06003 
06004           if (obj.is_aliminclude ())
06005             {
06006               octave_value lim = obj.get_alim ();
06007 
06008               check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
06009             }
06010         }
06011       break;
06012 
06013     default:
06014       break;
06015     }
06016 }
06017 
06018 static bool updating_axis_limits = false;
06019 
06020 void
06021 axes::update_axis_limits (const std::string& axis_type,
06022                           const graphics_handle& h)
06023 {
06024   if (updating_axis_limits)
06025     return;
06026 
06027   Matrix kids = Matrix (1, 1, h.value ());
06028 
06029   double min_val = octave_Inf;
06030   double max_val = -octave_Inf;
06031   double min_pos = octave_Inf;
06032   double max_neg = -octave_Inf;
06033 
06034   char update_type = 0;
06035 
06036   Matrix limits;
06037   double val;
06038 
06039 #define FIX_LIMITS \
06040   if (limits.numel() == 4) \
06041     { \
06042       val = limits(0); \
06043       if (! (xisinf (val) || xisnan (val))) \
06044         min_val = val; \
06045       val = limits(1); \
06046       if (! (xisinf (val) || xisnan (val))) \
06047         max_val = val; \
06048       val = limits(2); \
06049       if (! (xisinf (val) || xisnan (val))) \
06050         min_pos = val; \
06051       val = limits(3); \
06052       if (! (xisinf (val) || xisnan (val))) \
06053         max_neg = val; \
06054     } \
06055   else \
06056     { \
06057       limits.resize(4, 1); \
06058       limits(0) = min_val; \
06059       limits(1) = max_val; \
06060       limits(2) = min_pos; \
06061       limits(3) = max_neg; \
06062     }
06063 
06064   if (axis_type == "xdata" || axis_type == "xscale"
06065       || axis_type == "xlimmode" || axis_type == "xliminclude"
06066       || axis_type == "xlim")
06067     {
06068       if (xproperties.xlimmode_is ("auto"))
06069         {
06070           limits = xproperties.get_xlim ().matrix_value ();
06071           FIX_LIMITS ;
06072 
06073           get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'x');
06074 
06075           limits = xproperties.get_axis_limits (min_val, max_val,
06076                                                 min_pos, max_neg,
06077                                                 xproperties.xscale_is ("log"));
06078 
06079           update_type = 'x';
06080         }
06081     }
06082   else if (axis_type == "ydata" || axis_type == "yscale"
06083            || axis_type == "ylimmode" || axis_type == "yliminclude"
06084            || axis_type == "ylim")
06085     {
06086       if (xproperties.ylimmode_is ("auto"))
06087         {
06088           limits = xproperties.get_ylim ().matrix_value ();
06089           FIX_LIMITS ;
06090 
06091           get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'y');
06092 
06093           limits = xproperties.get_axis_limits (min_val, max_val,
06094                                                 min_pos, max_neg,
06095                                                 xproperties.yscale_is ("log"));
06096 
06097           update_type = 'y';
06098         }
06099     }
06100   else if (axis_type == "zdata" || axis_type == "zscale"
06101            || axis_type == "zlimmode" || axis_type == "zliminclude"
06102            || axis_type == "zlim")
06103     {
06104       if (xproperties.zlimmode_is ("auto"))
06105         {
06106           limits = xproperties.get_zlim ().matrix_value ();
06107           FIX_LIMITS ;
06108 
06109           get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'z');
06110 
06111           limits = xproperties.get_axis_limits (min_val, max_val,
06112                                                 min_pos, max_neg,
06113                                                 xproperties.zscale_is ("log"));
06114 
06115           update_type = 'z';
06116         }
06117     }
06118   else if (axis_type == "cdata" || axis_type == "climmode"
06119            || axis_type == "cdatamapping" || axis_type == "climinclude"
06120            || axis_type == "clim")
06121     {
06122       if (xproperties.climmode_is ("auto"))
06123         {
06124           limits = xproperties.get_clim ().matrix_value ();
06125           FIX_LIMITS ;
06126 
06127           get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'c');
06128 
06129           if (min_val > max_val)
06130             {
06131               min_val = min_pos = 0;
06132               max_val = 1;
06133             }
06134           else if (min_val == max_val)
06135             {
06136               max_val = min_val + 1;
06137               min_val -= 1;
06138             }
06139 
06140           limits.resize (1, 2);
06141 
06142           limits(0) = min_val;
06143           limits(1) = max_val;
06144 
06145           update_type = 'c';
06146         }
06147 
06148     }
06149   else if (axis_type == "alphadata" || axis_type == "alimmode"
06150            || axis_type == "alphadatamapping" || axis_type == "aliminclude"
06151            || axis_type == "alim")
06152     {
06153       if (xproperties.alimmode_is ("auto"))
06154         {
06155           limits = xproperties.get_alim ().matrix_value ();
06156           FIX_LIMITS ;
06157 
06158           get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'a');
06159 
06160           if (min_val > max_val)
06161             {
06162               min_val = min_pos = 0;
06163               max_val = 1;
06164             }
06165           else if (min_val == max_val)
06166             max_val = min_val + 1;
06167 
06168           limits.resize (1, 2);
06169 
06170           limits(0) = min_val;
06171           limits(1) = max_val;
06172 
06173           update_type = 'a';
06174         }
06175 
06176     }
06177 
06178 #undef FIX_LIMITS
06179 
06180   unwind_protect frame;
06181   frame.protect_var (updating_axis_limits);
06182 
06183   updating_axis_limits = true;
06184 
06185   switch (update_type)
06186     {
06187     case 'x':
06188       xproperties.set_xlim (limits);
06189       xproperties.set_xlimmode ("auto");
06190       xproperties.update_xlim ();
06191       break;
06192 
06193     case 'y':
06194       xproperties.set_ylim (limits);
06195       xproperties.set_ylimmode ("auto");
06196       xproperties.update_ylim ();
06197       break;
06198 
06199     case 'z':
06200       xproperties.set_zlim (limits);
06201       xproperties.set_zlimmode ("auto");
06202       xproperties.update_zlim ();
06203       break;
06204 
06205     case 'c':
06206       xproperties.set_clim (limits);
06207       xproperties.set_climmode ("auto");
06208       break;
06209 
06210     case 'a':
06211       xproperties.set_alim (limits);
06212       xproperties.set_alimmode ("auto");
06213       break;
06214 
06215     default:
06216       break;
06217     }
06218 
06219   xproperties.update_transform ();
06220 
06221 }
06222 
06223 void
06224 axes::update_axis_limits (const std::string& axis_type)
06225 {
06226   if (updating_axis_limits || updating_aspectratios)
06227     return;
06228 
06229   Matrix kids = xproperties.get_children ();
06230 
06231   double min_val = octave_Inf;
06232   double max_val = -octave_Inf;
06233   double min_pos = octave_Inf;
06234   double max_neg = -octave_Inf;
06235 
06236   char update_type = 0;
06237 
06238   Matrix limits;
06239 
06240   if (axis_type == "xdata" || axis_type == "xscale"
06241       || axis_type == "xlimmode" || axis_type == "xliminclude"
06242       || axis_type == "xlim")
06243     {
06244       if (xproperties.xlimmode_is ("auto"))
06245         {
06246           get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'x');
06247 
06248           limits = xproperties.get_axis_limits (min_val, max_val,
06249                                                 min_pos, max_neg,
06250                                                 xproperties.xscale_is ("log"));
06251 
06252           update_type = 'x';
06253         }
06254     }
06255   else if (axis_type == "ydata" || axis_type == "yscale"
06256            || axis_type == "ylimmode" || axis_type == "yliminclude"
06257            || axis_type == "ylim")
06258     {
06259       if (xproperties.ylimmode_is ("auto"))
06260         {
06261           get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'y');
06262 
06263           limits = xproperties.get_axis_limits (min_val, max_val,
06264                                                 min_pos, max_neg,
06265                                                 xproperties.yscale_is ("log"));
06266 
06267           update_type = 'y';
06268         }
06269     }
06270   else if (axis_type == "zdata" || axis_type == "zscale"
06271            || axis_type == "zlimmode" || axis_type == "zliminclude"
06272            || axis_type == "zlim")
06273     {
06274       if (xproperties.zlimmode_is ("auto"))
06275         {
06276           get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'z');
06277 
06278           limits = xproperties.get_axis_limits (min_val, max_val,
06279                                                 min_pos, max_neg,
06280                                                 xproperties.zscale_is ("log"));
06281 
06282           update_type = 'z';
06283         }
06284     }
06285   else if (axis_type == "cdata" || axis_type == "climmode"
06286            || axis_type == "cdatamapping" || axis_type == "climinclude"
06287            || axis_type == "clim")
06288     {
06289       if (xproperties.climmode_is ("auto"))
06290         {
06291           get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'c');
06292 
06293           if (min_val > max_val)
06294             {
06295               min_val = min_pos = 0;
06296               max_val = 1;
06297             }
06298           else if (min_val == max_val)
06299             {
06300               max_val = min_val + 1;
06301               min_val -= 1;
06302             }
06303 
06304           limits.resize (1, 2);
06305 
06306           limits(0) = min_val;
06307           limits(1) = max_val;
06308 
06309           update_type = 'c';
06310         }
06311 
06312     }
06313   else if (axis_type == "alphadata" || axis_type == "alimmode"
06314            || axis_type == "alphadatamapping" || axis_type == "aliminclude"
06315            || axis_type == "alim")
06316     {
06317       if (xproperties.alimmode_is ("auto"))
06318         {
06319           get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'a');
06320 
06321           if (min_val > max_val)
06322             {
06323               min_val = min_pos = 0;
06324               max_val = 1;
06325             }
06326           else if (min_val == max_val)
06327             max_val = min_val + 1;
06328 
06329           limits.resize (1, 2);
06330 
06331           limits(0) = min_val;
06332           limits(1) = max_val;
06333 
06334           update_type = 'a';
06335         }
06336 
06337     }
06338 
06339   unwind_protect frame;
06340   frame.protect_var (updating_axis_limits);
06341 
06342   updating_axis_limits = true;
06343 
06344   switch (update_type)
06345     {
06346     case 'x':
06347       xproperties.set_xlim (limits);
06348       xproperties.set_xlimmode ("auto");
06349       xproperties.update_xlim ();
06350       break;
06351 
06352     case 'y':
06353       xproperties.set_ylim (limits);
06354       xproperties.set_ylimmode ("auto");
06355       xproperties.update_ylim ();
06356       break;
06357 
06358     case 'z':
06359       xproperties.set_zlim (limits);
06360       xproperties.set_zlimmode ("auto");
06361       xproperties.update_zlim ();
06362       break;
06363 
06364     case 'c':
06365       xproperties.set_clim (limits);
06366       xproperties.set_climmode ("auto");
06367       break;
06368 
06369     case 'a':
06370       xproperties.set_alim (limits);
06371       xproperties.set_alimmode ("auto");
06372       break;
06373 
06374     default:
06375       break;
06376     }
06377 
06378   xproperties.update_transform ();
06379 }
06380 
06381 inline
06382 double force_in_range (const double x, const double lower, const double upper)
06383 {
06384   if (x < lower)
06385     { return lower; }
06386   else if (x > upper)
06387     { return upper; }
06388   else
06389     { return x; }
06390 }
06391 
06392 static Matrix
06393 do_zoom (double val, double factor, const Matrix& lims, bool is_logscale)
06394 {
06395   Matrix new_lims = lims;
06396 
06397   double lo = lims(0);
06398   double hi = lims(1);
06399 
06400   bool is_negative = lo < 0 && hi < 0;
06401 
06402   if (is_logscale)
06403     {
06404       if (is_negative)
06405         {
06406           double tmp = hi;
06407           hi = std::log10 (-lo);
06408           lo = std::log10 (-tmp);
06409           val = std::log10 (-val);
06410         }
06411       else
06412         {
06413           hi = std::log10 (hi);
06414           lo = std::log10 (lo);
06415           val = std::log10 (val);
06416         }
06417     }
06418 
06419   // Perform the zooming
06420   lo = val + factor * (lo - val);
06421   hi = val + factor * (hi - val);
06422 
06423   if (is_logscale)
06424     {
06425       if (is_negative)
06426         {
06427           double tmp = -std::pow (10.0, hi);
06428           hi = -std::pow (10.0, lo);
06429           lo = tmp;
06430         }
06431       else
06432         {
06433           lo = std::pow (10.0, lo);
06434           hi = std::pow (10.0, hi);
06435         }
06436     }
06437 
06438   new_lims(0) = lo;
06439   new_lims(1) = hi;
06440 
06441   return new_lims;
06442 }
06443 
06444 void
06445 axes::properties::zoom_about_point (double x, double y, double factor,
06446                                     bool push_to_zoom_stack)
06447 {
06448   // FIXME: Do we need error checking here?
06449   Matrix xlims = get_xlim ().matrix_value ();
06450   Matrix ylims = get_ylim ().matrix_value ();
06451 
06452   // Get children axes limits
06453   Matrix kids = get_children ();
06454   double minx = octave_Inf;
06455   double maxx = -octave_Inf;
06456   double min_pos_x = octave_Inf;
06457   double max_neg_x = -octave_Inf;
06458   get_children_limits (minx, maxx, min_pos_x, max_neg_x, kids, 'x');
06459 
06460   double miny = octave_Inf;
06461   double maxy = -octave_Inf;
06462   double min_pos_y = octave_Inf;
06463   double max_neg_y = -octave_Inf;
06464   get_children_limits (miny, maxy, min_pos_y, max_neg_y, kids, 'y');
06465 
06466   if (! (xscale_is ("log") && xlims(0) < 0 && xlims(1) < 0))
06467     xlims = do_zoom (x, factor, xlims, xscale_is ("log"));
06468 
06469   if (! (yscale_is ("log") && ylims(0) < 0 && ylims(1) < 0))
06470     ylims = do_zoom (y, factor, ylims, yscale_is ("log"));
06471 
06472   zoom (xlims, ylims, push_to_zoom_stack);
06473 }
06474 
06475 void
06476 axes::properties::zoom (const Matrix& xl, const Matrix& yl, bool push_to_zoom_stack)
06477 {
06478   // FIXME: Do we need error checking here?
06479   Matrix xlims = get_xlim ().matrix_value ();
06480   Matrix ylims = get_ylim ().matrix_value ();
06481 
06482   if ((xscale_is ("log") && xlims(0) < 0 && xlims(1) < 0)
06483       || (yscale_is ("log") && ylims(0) < 0 && ylims(1) < 0))
06484     return;
06485 
06486   if (push_to_zoom_stack)
06487     {
06488       zoom_stack.push_front (xlimmode.get ());
06489       zoom_stack.push_front (xlim.get ());
06490       zoom_stack.push_front (ylimmode.get ());
06491       zoom_stack.push_front (ylim.get ());
06492     }
06493 
06494   xlim = xl;
06495   xlimmode = "manual";
06496   ylim = yl;
06497   ylimmode = "manual";
06498 
06499   update_transform ();
06500   update_xlim (false);
06501   update_ylim (false);
06502 }
06503 
06504 void
06505 axes::properties::translate_view (double delta_x, double delta_y)
06506 {
06507   // FIXME: Do we need error checking here?
06508   Matrix xlims = get_xlim ().matrix_value ();
06509   Matrix ylims = get_ylim ().matrix_value ();
06510 
06511   // Get children axes limits
06512   Matrix kids = get_children ();
06513   double minx = octave_Inf;
06514   double maxx = -octave_Inf;
06515   double min_pos_x = octave_Inf;
06516   double max_neg_x = -octave_Inf;
06517   get_children_limits (minx, maxx, min_pos_x, max_neg_x, kids, 'x');
06518 
06519   double miny = octave_Inf;
06520   double maxy = -octave_Inf;
06521   double min_pos_y = octave_Inf;
06522   double max_neg_y = -octave_Inf;
06523   get_children_limits (miny, maxy, min_pos_y, max_neg_y, kids, 'y');
06524 
06525   if (! xscale_is ("log"))
06526     {
06527       xlims (0) += delta_x;
06528       xlims (1) += delta_x;
06529     }
06530 
06531   if (! yscale_is ("log"))
06532     {
06533       ylims (0) += delta_y;
06534       ylims (1) += delta_y;
06535     }
06536 
06537   zoom (xlims, ylims, false);
06538 }
06539 
06540 void
06541 axes::properties::rotate_view (double delta_el, double delta_az)
06542 {
06543   Matrix v = get_view ().matrix_value ();
06544 
06545   v (1) += delta_el;
06546 
06547   if(v(1) > 90)
06548     v(1) = 90;
06549   if(v(1) < -90)
06550     v(1) = -90;
06551 
06552   v (0) = fmod(v(0) - delta_az + 720,360);
06553 
06554   set_view(v);
06555   update_transform();
06556 }
06557 
06558 void
06559 axes::properties::unzoom (void)
06560 {
06561   if (zoom_stack.size () >= 4)
06562     {
06563       ylim = zoom_stack.front ();
06564       zoom_stack.pop_front ();
06565       ylimmode = zoom_stack.front ();
06566       zoom_stack.pop_front ();
06567       xlim = zoom_stack.front ();
06568       zoom_stack.pop_front ();
06569       xlimmode = zoom_stack.front ();
06570       zoom_stack.pop_front ();
06571 
06572       update_transform ();
06573       update_xlim (false);
06574       update_ylim (false);
06575     }
06576 }
06577 
06578 void
06579 axes::properties::clear_zoom_stack (void)
06580 {
06581   while (zoom_stack.size () > 4)
06582     zoom_stack.pop_front ();
06583 
06584   unzoom ();
06585 }
06586 
06587 void
06588 axes::reset_default_properties (void)
06589 {
06590   ::reset_default_properties (default_properties);
06591 }
06592 
06593 void
06594 axes::initialize (const graphics_object& go)
06595 {
06596   base_graphics_object::initialize (go);
06597 
06598   xinitialize (xproperties.get_title ());
06599   xinitialize (xproperties.get_xlabel ());
06600   xinitialize (xproperties.get_ylabel ());
06601   xinitialize (xproperties.get_zlabel ());
06602 }
06603 
06604 // ---------------------------------------------------------------------
06605 
06606 Matrix
06607 line::properties::compute_xlim (void) const
06608 {
06609   Matrix m (1, 4);
06610 
06611   m(0) = xdata.min_val ();
06612   m(1) = xdata.max_val ();
06613   m(2) = xdata.min_pos ();
06614   m(3) = xdata.max_neg ();
06615 
06616   return m;
06617 }
06618 
06619 Matrix
06620 line::properties::compute_ylim (void) const
06621 {
06622   Matrix m (1, 4);
06623 
06624   m(0) = ydata.min_val ();
06625   m(1) = ydata.max_val ();
06626   m(2) = ydata.min_pos ();
06627   m(3) = ydata.max_neg ();
06628 
06629   return m;
06630 }
06631 
06632 // ---------------------------------------------------------------------
06633 
06634 Matrix
06635 text::properties::get_data_position (void) const
06636 {
06637   Matrix pos = get_position ().matrix_value ();
06638 
06639   if (! units_is ("data"))
06640     pos = convert_text_position (pos, *this, get_units (), "data");
06641 
06642   return pos;
06643 }
06644 
06645 Matrix
06646 text::properties::get_extent_matrix (void) const
06647 {
06648   return extent.get ().matrix_value ();
06649 }
06650 
06651 octave_value
06652 text::properties::get_extent (void) const
06653 {
06654   Matrix m = extent.get ().matrix_value ();
06655 
06656   return convert_text_position (m, *this, "pixels", get_units ());
06657 }
06658 
06659 void
06660 text::properties::update_font (void)
06661 {
06662 #ifdef HAVE_FREETYPE
06663 #ifdef HAVE_FONTCONFIG
06664   renderer.set_font (get ("fontname").string_value (),
06665                      get ("fontweight").string_value (),
06666                      get ("fontangle").string_value (),
06667                      get ("fontsize").double_value ());
06668 #endif
06669   renderer.set_color (get_color_rgb ());
06670 #endif
06671 }
06672 
06673 void
06674 text::properties::update_text_extent (void)
06675 {
06676 #ifdef HAVE_FREETYPE
06677 
06678   int halign = 0, valign = 0;
06679 
06680   if (horizontalalignment_is ("center"))
06681     halign = 1;
06682   else if (horizontalalignment_is ("right"))
06683     halign = 2;
06684 
06685   if (verticalalignment_is ("top"))
06686     valign = 2;
06687   else if (verticalalignment_is ("baseline"))
06688     valign = 3;
06689   else if (verticalalignment_is ("middle"))
06690     valign = 1;
06691 
06692   Matrix bbox;
06693 
06694   // FIXME: string should be parsed only when modified, for efficiency
06695 
06696   octave_value string_prop = get_string ();
06697 
06698   string_vector sv = string_prop.all_strings ();
06699 
06700   renderer.text_to_pixels (sv.join ("\n"), pixels, bbox,
06701                            halign, valign, get_rotation ());
06702   set_extent (bbox);
06703 
06704 #endif
06705 
06706   if (autopos_tag_is ("xlabel") || autopos_tag_is ("ylabel") ||
06707       autopos_tag_is ("zlabel") || autopos_tag_is ("title"))
06708     update_autopos ("sync");
06709 }
06710 
06711 void
06712 text::properties::request_autopos (void)
06713 {
06714   if (autopos_tag_is ("xlabel") || autopos_tag_is ("ylabel") ||
06715       autopos_tag_is ("zlabel") || autopos_tag_is ("title"))
06716     update_autopos (get_autopos_tag ());
06717 }
06718 
06719 void
06720 text::properties::update_units (void)
06721 {
06722   if (! units_is ("data"))
06723     {
06724       set_xliminclude ("off");
06725       set_yliminclude ("off");
06726       set_zliminclude ("off");
06727     }
06728 
06729   Matrix pos = get_position ().matrix_value ();
06730 
06731   pos = convert_text_position (pos, *this, cached_units, get_units ());
06732   // FIXME: if the current axes view is 2D, then one should
06733   // probably drop the z-component of "pos" and leave "zliminclude"
06734   // to "off".
06735   set_position (pos);
06736 
06737   if (units_is ("data"))
06738     {
06739       set_xliminclude ("on");
06740       set_yliminclude ("on");
06741       // FIXME: see above
06742       set_zliminclude ("off");
06743     }
06744 
06745   cached_units = get_units ();
06746 }
06747 
06748 double
06749 text::properties::get_fontsize_points (double box_pix_height) const
06750 {
06751   double fs = get_fontsize ();
06752   double parent_height = box_pix_height;
06753 
06754   if (fontunits_is ("normalized") && parent_height <= 0)
06755     {
06756       graphics_object go (gh_manager::get_object (get___myhandle__ ()));
06757       graphics_object ax (go.get_ancestor ("axes"));
06758 
06759       parent_height = ax.get_properties ().get_boundingbox (true).elem(3);
06760     }
06761 
06762   return convert_font_size (fs, get_fontunits (), "points", parent_height);
06763 }
06764 
06765 // ---------------------------------------------------------------------
06766 
06767 octave_value
06768 image::properties::get_color_data (void) const
06769 {
06770   return convert_cdata (*this, get_cdata (),
06771                         cdatamapping_is ("scaled"), 3);
06772 }
06773 
06774 // ---------------------------------------------------------------------
06775 
06776 octave_value
06777 patch::properties::get_color_data (void) const
06778 {
06779   octave_value fvc = get_facevertexcdata();
06780   if (fvc.is_undefined () || fvc.is_empty ())
06781     return Matrix ();
06782   else
06783     return convert_cdata (*this, fvc,cdatamapping_is ("scaled"), 2);
06784 }
06785 
06786 // ---------------------------------------------------------------------
06787 
06788 octave_value
06789 surface::properties::get_color_data (void) const
06790 {
06791   return convert_cdata (*this, get_cdata (), cdatamapping_is ("scaled"), 3);
06792 }
06793 
06794 inline void
06795 cross_product (double x1, double y1, double z1,
06796                double x2, double y2, double z2,
06797                double& x, double& y, double& z)
06798 {
06799   x += (y1 * z2 - z1 * y2);
06800   y += (z1 * x2 - x1 * z2);
06801   z += (x1 * y2 - y1 * x2);
06802 }
06803 
06804 void
06805 surface::properties::update_normals (void)
06806 {
06807   if (normalmode_is ("auto"))
06808     {
06809       Matrix x = get_xdata ().matrix_value ();
06810       Matrix y = get_ydata ().matrix_value ();
06811       Matrix z = get_zdata ().matrix_value ();
06812 
06813 
06814       int p = z.columns (), q = z.rows ();
06815       int i1 = 0, i2 = 0, i3 = 0;
06816       int j1 = 0, j2 = 0, j3 = 0;
06817 
06818       bool x_mat = (x.rows () == q);
06819       bool y_mat = (y.columns () == p);
06820 
06821       NDArray n (dim_vector (q, p, 3), 0.0);
06822 
06823       for (int i = 0; i < p; i++)
06824         {
06825           if (y_mat)
06826             {
06827               i1 = i - 1;
06828               i2 = i;
06829               i3 = i + 1;
06830             }
06831 
06832           for (int j = 0; j < q; j++)
06833             {
06834               if (x_mat)
06835                 {
06836                   j1 = j - 1;
06837                   j2 = j;
06838                   j3 = j + 1;
06839                 }
06840 
06841               double& nx = n(j, i, 0);
06842               double& ny = n(j, i, 1);
06843               double& nz = n(j, i, 2);
06844 
06845               if ((j > 0) && (i > 0))
06846                   // upper left quadrangle
06847                   cross_product (x(j1,i-1)-x(j2,i), y(j-1,i1)-y(j,i2), z(j-1,i-1)-z(j,i),
06848                                  x(j2,i-1)-x(j1,i), y(j,i1)-y(j-1,i2), z(j,i-1)-z(j-1,i),
06849                                  nx, ny, nz);
06850 
06851               if ((j > 0) && (i < (p -1)))
06852                   // upper right quadrangle
06853                   cross_product (x(j1,i+1)-x(j2,i), y(j-1,i3)-y(j,i2), z(j-1,i+1)-z(j,i),
06854                                  x(j1,i)-x(j2,i+1), y(j-1,i2)-y(j,i3), z(j-1,i)-z(j,i+1),
06855                                  nx, ny, nz);
06856 
06857               if ((j < (q - 1)) && (i > 0))
06858                   // lower left quadrangle
06859                   cross_product (x(j2,i-1)-x(j3,i), y(j,i1)-y(j+1,i2), z(j,i-1)-z(j+1,i),
06860                                  x(j3,i-1)-x(j2,i), y(j+1,i1)-y(j,i2), z(j+1,i-1)-z(j,i),
06861                                  nx, ny, nz);
06862 
06863               if ((j < (q - 1)) && (i < (p -1)))
06864                   // lower right quadrangle
06865                   cross_product (x(j3,i)-x(j2,i+1), y(j+1,i2)-y(j,i3), z(j+1,i)-z(j,i+1),
06866                                  x(j3,i+1)-x(j2,i), y(j+1,i3)-y(j,i2), z(j+1,i+1)-z(j,i),
06867                                  nx, ny, nz);
06868 
06869               double d = -std::max (std::max (fabs (nx), fabs (ny)), fabs (nz));
06870 
06871               nx /= d;
06872               ny /= d;
06873               nz /= d;
06874             }
06875         }
06876       vertexnormals = n;
06877     }
06878 }
06879 
06880 // ---------------------------------------------------------------------
06881 
06882 void
06883 hggroup::properties::update_limits (void) const
06884 {
06885   graphics_object obj = gh_manager::get_object (__myhandle__);
06886 
06887   if (obj)
06888     {
06889       obj.update_axis_limits ("xlim");
06890       obj.update_axis_limits ("ylim");
06891       obj.update_axis_limits ("zlim");
06892       obj.update_axis_limits ("clim");
06893       obj.update_axis_limits ("alim");
06894     }
06895 }
06896 
06897 void
06898 hggroup::properties::update_limits (const graphics_handle& h) const
06899 {
06900   graphics_object obj = gh_manager::get_object (__myhandle__);
06901 
06902   if (obj)
06903     {
06904       obj.update_axis_limits ("xlim", h);
06905       obj.update_axis_limits ("ylim", h);
06906       obj.update_axis_limits ("zlim", h);
06907       obj.update_axis_limits ("clim", h);
06908       obj.update_axis_limits ("alim", h);
06909     }
06910 }
06911 
06912 static bool updating_hggroup_limits = false;
06913 
06914 void
06915 hggroup::update_axis_limits (const std::string& axis_type,
06916                              const graphics_handle& h)
06917 {
06918   if (updating_hggroup_limits)
06919     return;
06920 
06921   Matrix kids = Matrix (1, 1, h.value ());
06922 
06923   double min_val = octave_Inf;
06924   double max_val = -octave_Inf;
06925   double min_pos = octave_Inf;
06926   double max_neg = -octave_Inf;
06927 
06928   Matrix limits;
06929   double val;
06930 
06931   char update_type = 0;
06932 
06933   if (axis_type == "xlim" || axis_type == "xliminclude")
06934     {
06935       limits = xproperties.get_xlim ().matrix_value ();
06936       update_type = 'x';
06937     }
06938   else if (axis_type == "ylim" || axis_type == "yliminclude")
06939     {
06940       limits = xproperties.get_ylim ().matrix_value ();
06941       update_type = 'y';
06942     }
06943   else if (axis_type == "zlim" || axis_type == "zliminclude")
06944     {
06945       limits = xproperties.get_zlim ().matrix_value ();
06946       update_type = 'z';
06947     }
06948   else if (axis_type == "clim" || axis_type == "climinclude")
06949     {
06950       limits = xproperties.get_clim ().matrix_value ();
06951       update_type = 'c';
06952     }
06953   else if (axis_type == "alim" || axis_type == "aliminclude")
06954     {
06955       limits = xproperties.get_alim ().matrix_value ();
06956       update_type = 'a';
06957     }
06958 
06959   if (limits.numel() == 4)
06960     {
06961       val = limits(0);
06962       if (! (xisinf (val) || xisnan (val)))
06963         min_val = val;
06964       val = limits(1);
06965       if (! (xisinf (val) || xisnan (val)))
06966         max_val = val;
06967       val = limits(2);
06968       if (! (xisinf (val) || xisnan (val)))
06969         min_pos = val;
06970       val = limits(3);
06971       if (! (xisinf (val) || xisnan (val)))
06972         max_neg = val;
06973     }
06974   else
06975     {
06976       limits.resize(4,1);
06977       limits(0) = min_val;
06978       limits(1) = max_val;
06979       limits(2) = min_pos;
06980       limits(3) = max_neg;
06981     }
06982 
06983   get_children_limits (min_val, max_val, min_pos, max_neg, kids, update_type);
06984 
06985   unwind_protect frame;
06986   frame.protect_var (updating_hggroup_limits);
06987 
06988   updating_hggroup_limits = true;
06989 
06990   if (limits(0) != min_val || limits(1) != max_val
06991       || limits(2) != min_pos || limits(3) != max_neg)
06992     {
06993       limits(0) = min_val;
06994       limits(1) = max_val;
06995       limits(2) = min_pos;
06996       limits(3) = max_neg;
06997 
06998       switch (update_type)
06999         {
07000         case 'x':
07001           xproperties.set_xlim (limits);
07002           break;
07003 
07004         case 'y':
07005           xproperties.set_ylim (limits);
07006           break;
07007 
07008         case 'z':
07009           xproperties.set_zlim (limits);
07010           break;
07011 
07012         case 'c':
07013           xproperties.set_clim (limits);
07014           break;
07015 
07016         case 'a':
07017           xproperties.set_alim (limits);
07018           break;
07019 
07020         default:
07021           break;
07022         }
07023 
07024       base_graphics_object::update_axis_limits (axis_type, h);
07025     }
07026 }
07027 
07028 void
07029 hggroup::update_axis_limits (const std::string& axis_type)
07030 {
07031   if (updating_hggroup_limits)
07032     return;
07033 
07034   Matrix kids = xproperties.get_children ();
07035 
07036   double min_val = octave_Inf;
07037   double max_val = -octave_Inf;
07038   double min_pos = octave_Inf;
07039   double max_neg = -octave_Inf;
07040 
07041   char update_type = 0;
07042 
07043   if (axis_type == "xlim" || axis_type == "xliminclude")
07044     {
07045       get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'x');
07046 
07047       update_type = 'x';
07048     }
07049   else if (axis_type == "ylim" || axis_type == "yliminclude")
07050     {
07051       get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'y');
07052 
07053       update_type = 'y';
07054     }
07055   else if (axis_type == "zlim" || axis_type == "zliminclude")
07056     {
07057       get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'z');
07058 
07059       update_type = 'z';
07060     }
07061   else if (axis_type == "clim" || axis_type == "climinclude")
07062     {
07063       get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'c');
07064 
07065       update_type = 'c';
07066     }
07067   else if (axis_type == "alim" || axis_type == "aliminclude")
07068     {
07069       get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'a');
07070 
07071       update_type = 'a';
07072     }
07073 
07074   unwind_protect frame;
07075   frame.protect_var (updating_hggroup_limits);
07076 
07077   updating_hggroup_limits = true;
07078 
07079   Matrix limits (1, 4, 0.0);
07080 
07081   limits(0) = min_val;
07082   limits(1) = max_val;
07083   limits(2) = min_pos;
07084   limits(3) = max_neg;
07085 
07086   switch (update_type)
07087     {
07088     case 'x':
07089       xproperties.set_xlim (limits);
07090       break;
07091 
07092     case 'y':
07093       xproperties.set_ylim (limits);
07094       break;
07095 
07096     case 'z':
07097       xproperties.set_zlim (limits);
07098       break;
07099 
07100     case 'c':
07101       xproperties.set_clim (limits);
07102       break;
07103 
07104     case 'a':
07105       xproperties.set_alim (limits);
07106       break;
07107 
07108     default:
07109       break;
07110     }
07111 
07112   base_graphics_object::update_axis_limits (axis_type);
07113 }
07114 
07115 // ---------------------------------------------------------------------
07116 
07117 octave_value
07118 uicontrol::properties::get_extent (void) const
07119 {
07120   Matrix m = extent.get ().matrix_value ();
07121 
07122   graphics_object parent_obj =
07123     gh_manager::get_object (get_parent ());
07124   Matrix parent_bbox = parent_obj.get_properties ().get_boundingbox (true),
07125          parent_size = parent_bbox.extract_n (0, 2, 1, 2);
07126 
07127   return convert_position (m, "pixels", get_units (), parent_size);
07128 }
07129 
07130 void
07131 uicontrol::properties::update_text_extent (void)
07132 {
07133 #ifdef HAVE_FREETYPE
07134 
07135   text_element *elt;
07136   ft_render text_renderer;
07137   Matrix box;
07138 
07139   // FIXME: parsed content should be cached for efficiency
07140   // FIXME: support multiline text
07141 
07142   elt = text_parser_none ().parse (get_string_string ());
07143 #ifdef HAVE_FONTCONFIG
07144   text_renderer.set_font (get_fontname (),
07145                           get_fontweight (),
07146                           get_fontangle (),
07147                           get_fontsize ());
07148 #endif
07149   box = text_renderer.get_extent (elt, 0);
07150 
07151   Matrix ext (1, 4, 0.0);
07152 
07153   // FIXME: also handle left and bottom components
07154 
07155   ext(0) = ext(1) = 1;
07156   ext(2) = box(0);
07157   ext(3) = box(1);
07158 
07159   set_extent (ext);
07160 
07161 #endif
07162 }
07163 
07164 void
07165 uicontrol::properties::update_units (void)
07166 {
07167   Matrix pos = get_position ().matrix_value ();
07168 
07169   graphics_object parent_obj = gh_manager::get_object (get_parent ());
07170   Matrix parent_bbox = parent_obj.get_properties ().get_boundingbox (true),
07171          parent_size = parent_bbox.extract_n (0, 2, 1, 2);
07172 
07173   pos = convert_position (pos, cached_units, get_units (), parent_size);
07174   set_position (pos);
07175 
07176   cached_units = get_units ();
07177 }
07178 
07179 void
07180 uicontrol::properties::set_style (const octave_value& st)
07181 {
07182   if (get___object__ ().is_empty())
07183     style = st;
07184   else
07185     error ("set: cannot change the style of a uicontrol object after creation.");
07186 }
07187 
07188 Matrix
07189 uicontrol::properties::get_boundingbox (bool,
07190                                         const Matrix& parent_pix_size) const
07191 {
07192   Matrix pos = get_position ().matrix_value ();
07193   Matrix parent_size (parent_pix_size);
07194 
07195   if (parent_size.numel () == 0)
07196     {
07197       graphics_object obj = gh_manager::get_object (get_parent ());
07198 
07199       parent_size =
07200        obj.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
07201     }
07202 
07203   pos = convert_position (pos, get_units (), "pixels", parent_size);
07204 
07205   pos(0)--;
07206   pos(1)--;
07207   pos(1) = parent_size(1) - pos(1) - pos(3);
07208 
07209   return pos;
07210 }
07211 
07212 void
07213 uicontrol::properties::set_fontunits (const octave_value& v)
07214 {
07215   if (! error_state)
07216     {
07217       caseless_str old_fontunits = get_fontunits ();
07218       if (fontunits.set (v, true))
07219         {
07220           update_fontunits (old_fontunits);
07221           mark_modified ();
07222         }
07223     }
07224 }
07225 
07226 void
07227 uicontrol::properties::update_fontunits (const caseless_str& old_units)
07228 {
07229   caseless_str new_units = get_fontunits ();
07230   double parent_height = get_boundingbox (false).elem (3);
07231   double fsz = get_fontsize ();
07232 
07233   fsz = convert_font_size (fsz, old_units, new_units, parent_height);
07234 
07235   fontsize.set (octave_value (fsz), true);
07236 }
07237 
07238 double
07239 uicontrol::properties::get_fontsize_points (double box_pix_height) const
07240 {
07241   double fs = get_fontsize ();
07242   double parent_height = box_pix_height;
07243 
07244   if (fontunits_is ("normalized") && parent_height <= 0)
07245     parent_height = get_boundingbox (false).elem(3);
07246 
07247   return convert_font_size (fs, get_fontunits (), "points", parent_height);
07248 }
07249 
07250 // ---------------------------------------------------------------------
07251 
07252 Matrix
07253 uipanel::properties::get_boundingbox (bool internal,
07254                                       const Matrix& parent_pix_size) const
07255 {
07256   Matrix pos = get_position ().matrix_value ();
07257   Matrix parent_size (parent_pix_size);
07258 
07259   if (parent_size.numel () == 0)
07260     {
07261       graphics_object obj = gh_manager::get_object (get_parent ());
07262 
07263       parent_size =
07264        obj.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
07265     }
07266 
07267   pos = convert_position (pos, get_units (), "pixels", parent_size);
07268 
07269   pos(0)--;
07270   pos(1)--;
07271   pos(1) = parent_size(1) - pos(1) - pos(3);
07272 
07273   if (internal)
07274     {
07275       double outer_height = pos(3);
07276 
07277       pos(0) = pos(1) = 0;
07278 
07279       if (! bordertype_is ("none"))
07280         {
07281           double bw = get_borderwidth ();
07282           double mul = 1.0;
07283 
07284           if (bordertype_is ("etchedin") || bordertype_is ("etchedout"))
07285             mul = 2.0;
07286 
07287           pos(0) += mul * bw;
07288           pos(1) += mul * bw;
07289           pos(2) -= 2 * mul * bw;
07290           pos(3) -= 2 * mul * bw;
07291         }
07292 
07293       if (! get_title ().empty ())
07294         {
07295           double fs = get_fontsize ();
07296 
07297           if (! fontunits_is ("pixels"))
07298             {
07299               double res = xget (0, "screenpixelsperinch").double_value ();
07300 
07301               if (fontunits_is ("points"))
07302                 fs *= (res / 72.0);
07303               else if (fontunits_is ("inches"))
07304                 fs *= res;
07305               else if (fontunits_is ("centimeters"))
07306                 fs *= (res / 2.54);
07307               else if (fontunits_is ("normalized"))
07308                 fs *= outer_height;
07309             }
07310 
07311           if (titleposition_is ("lefttop") || titleposition_is ("centertop")
07312               || titleposition_is ("righttop"))
07313             pos(1) += (fs / 2);
07314           pos(3) -= (fs / 2);
07315         }
07316     }
07317 
07318   return pos;
07319 }
07320 
07321 void
07322 uipanel::properties::set_units (const octave_value& v)
07323 {
07324   if (! error_state)
07325     {
07326       caseless_str old_units = get_units ();
07327       if (units.set (v, true))
07328         {
07329           update_units (old_units);
07330           mark_modified ();
07331         }
07332     }
07333 }
07334 
07335 void
07336 uipanel::properties::update_units (const caseless_str& old_units)
07337 {
07338   Matrix pos = get_position ().matrix_value ();
07339 
07340   graphics_object parent_obj = gh_manager::get_object (get_parent ());
07341   Matrix parent_bbox = parent_obj.get_properties ().get_boundingbox (true),
07342          parent_size = parent_bbox.extract_n (0, 2, 1, 2);
07343 
07344   pos = convert_position (pos, old_units, get_units (), parent_size);
07345   set_position (pos);
07346 }
07347 
07348 void
07349 uipanel::properties::set_fontunits (const octave_value& v)
07350 {
07351   if (! error_state)
07352     {
07353       caseless_str old_fontunits = get_fontunits ();
07354       if (fontunits.set (v, true))
07355         {
07356           update_fontunits (old_fontunits);
07357           mark_modified ();
07358         }
07359     }
07360 }
07361 
07362 void
07363 uipanel::properties::update_fontunits (const caseless_str& old_units)
07364 {
07365   caseless_str new_units = get_fontunits ();
07366   double parent_height = get_boundingbox (false).elem (3);
07367   double fsz = get_fontsize ();
07368 
07369   fsz = convert_font_size (fsz, old_units, new_units, parent_height);
07370 
07371   set_fontsize (octave_value (fsz));
07372 }
07373 
07374 double
07375 uipanel::properties::get_fontsize_points (double box_pix_height) const
07376 {
07377   double fs = get_fontsize ();
07378   double parent_height = box_pix_height;
07379 
07380   if (fontunits_is ("normalized") && parent_height <= 0)
07381     parent_height = get_boundingbox (false).elem(3);
07382 
07383   return convert_font_size (fs, get_fontunits (), "points", parent_height);
07384 }
07385 
07386 // ---------------------------------------------------------------------
07387 
07388 octave_value
07389 uitoolbar::get_default (const caseless_str& name) const
07390 {
07391   octave_value retval = default_properties.lookup (name);
07392 
07393   if (retval.is_undefined ())
07394     {
07395       graphics_handle parent = get_parent ();
07396       graphics_object parent_obj = gh_manager::get_object (parent);
07397 
07398       retval = parent_obj.get_default (name);
07399     }
07400 
07401   return retval;
07402 }
07403 
07404 void
07405 uitoolbar::reset_default_properties (void)
07406 {
07407   ::reset_default_properties (default_properties);
07408 }
07409 
07410 // ---------------------------------------------------------------------
07411 
07412 octave_value
07413 base_graphics_object::get_default (const caseless_str& name) const
07414 {
07415   graphics_handle parent = get_parent ();
07416   graphics_object parent_obj = gh_manager::get_object (parent);
07417 
07418   return parent_obj.get_default (type () + name);
07419 }
07420 
07421 octave_value
07422 base_graphics_object::get_factory_default (const caseless_str& name) const
07423 {
07424   graphics_object parent_obj = gh_manager::get_object (0);
07425 
07426   return parent_obj.get_factory_default (type () + name);
07427 }
07428 
07429 // We use a random value for the handle to avoid issues with plots and
07430 // scalar values for the first argument.
07431 gh_manager::gh_manager (void)
07432   : handle_map (), handle_free_list (),
07433     next_handle (-1.0 - (rand () + 1.0) / (RAND_MAX + 2.0)),
07434     figure_list (), graphics_lock (),  event_queue (),
07435     callback_objects (), event_processing (0)
07436 {
07437   handle_map[0] = graphics_object (new root_figure ());
07438 
07439   // Make sure the default graphics toolkit is registered.
07440   gtk_manager::default_toolkit ();
07441 }
07442 
07443 void
07444 gh_manager::create_instance (void)
07445 {
07446   instance = new gh_manager ();
07447 
07448   if (instance)
07449     singleton_cleanup_list::add (cleanup_instance);
07450 }
07451 
07452 graphics_handle
07453 gh_manager::do_make_graphics_handle (const std::string& go_name,
07454                                      const graphics_handle& p,
07455                                      bool integer_figure_handle,
07456                                      bool do_createfcn,
07457                                      bool do_notify_toolkit)
07458 {
07459   graphics_handle h = get_handle (integer_figure_handle);
07460 
07461   base_graphics_object *go = 0;
07462 
07463   go = make_graphics_object_from_type (go_name, h, p);
07464 
07465   if (go)
07466     {
07467       graphics_object obj (go);
07468 
07469       handle_map[h] = obj;
07470       if (do_createfcn)
07471         go->get_properties ().execute_createfcn ();
07472 
07473       // Notify graphics toolkit.
07474       if (do_notify_toolkit)
07475         obj.initialize ();
07476     }
07477   else
07478     error ("gh_manager::do_make_graphics_handle: invalid object type '%s'",
07479            go_name.c_str ());
07480 
07481   return h;
07482 }
07483 
07484 graphics_handle
07485 gh_manager::do_make_figure_handle (double val, bool do_notify_toolkit)
07486 {
07487   graphics_handle h = val;
07488 
07489   base_graphics_object* go = new figure (h, 0);
07490   graphics_object obj (go);
07491 
07492   handle_map[h] = obj;
07493 
07494   // Notify graphics toolkit.
07495   if (do_notify_toolkit)
07496     obj.initialize ();
07497 
07498   return h;
07499 }
07500 
07501 void
07502 gh_manager::do_push_figure (const graphics_handle& h)
07503 {
07504   do_pop_figure (h);
07505 
07506   figure_list.push_front (h);
07507 }
07508 
07509 void
07510 gh_manager::do_pop_figure (const graphics_handle& h)
07511 {
07512   for (figure_list_iterator p = figure_list.begin ();
07513        p != figure_list.end ();
07514        p++)
07515     {
07516       if (*p == h)
07517         {
07518           figure_list.erase (p);
07519           break;
07520         }
07521     }
07522 }
07523 
07524 class
07525 callback_event : public base_graphics_event
07526 {
07527 public:
07528   callback_event (const graphics_handle& h, const std::string& name,
07529                   const octave_value& data = Matrix ())
07530       : base_graphics_event (), handle (h), callback_name (name),
07531         callback (), callback_data (data) { }
07532 
07533   callback_event (const graphics_handle& h, const octave_value& cb,
07534                   const octave_value& data = Matrix ())
07535       : base_graphics_event (), handle (h), callback_name (),
07536         callback (cb), callback_data (data) { }
07537 
07538   void execute (void)
07539     {
07540       if (callback.is_defined ())
07541         gh_manager::execute_callback (handle, callback, callback_data);
07542       else
07543         gh_manager::execute_callback (handle, callback_name, callback_data);
07544     }
07545 
07546 private:
07547   callback_event (void)
07548     : base_graphics_event (), handle (),
07549       callback_name (), callback_data ()
07550   { }
07551 
07552 private:
07553   graphics_handle handle;
07554   std::string callback_name;
07555   octave_value callback;
07556   octave_value callback_data;
07557 };
07558 
07559 class
07560 function_event : public base_graphics_event
07561 {
07562 public:
07563   function_event (graphics_event::event_fcn fcn, void* data = 0)
07564       : base_graphics_event (), function (fcn),
07565         function_data (data) { }
07566 
07567   void execute (void)
07568     {
07569       function (function_data);
07570     }
07571 
07572 private:
07573 
07574   graphics_event::event_fcn function;
07575 
07576   void* function_data;
07577 
07578   // function_event objects must be created with at least a function.
07579   function_event (void);
07580 
07581   // No copying!
07582 
07583   function_event (const function_event &);
07584 
07585   function_event & operator = (const function_event &);
07586 };
07587 
07588 class
07589 set_event : public base_graphics_event
07590 {
07591 public:
07592   set_event (const graphics_handle& h, const std::string& name,
07593              const octave_value& value, bool do_notify_toolkit = true)
07594       : base_graphics_event (), handle (h), property_name (name),
07595         property_value (value), notify_toolkit (do_notify_toolkit) { }
07596 
07597   void execute (void)
07598     {
07599       gh_manager::auto_lock guard;
07600 
07601       graphics_object go = gh_manager::get_object (handle);
07602 
07603       if (go)
07604         {
07605           property p = go.get_properties ().get_property (property_name);
07606 
07607           if (p.ok ())
07608             p.set (property_value, true, notify_toolkit);
07609         }
07610     }
07611 
07612 private:
07613   set_event (void)
07614     : base_graphics_event (), handle (), property_name (), property_value ()
07615   { }
07616 
07617 private:
07618   graphics_handle handle;
07619   std::string property_name;
07620   octave_value property_value;
07621   bool notify_toolkit;
07622 };
07623 
07624 graphics_event
07625 graphics_event::create_callback_event (const graphics_handle& h,
07626                                        const std::string& name,
07627                                        const octave_value& data)
07628 {
07629   graphics_event e;
07630 
07631   e.rep = new callback_event (h, name, data);
07632 
07633   return e;
07634 }
07635 
07636 graphics_event
07637 graphics_event::create_callback_event (const graphics_handle& h,
07638                                        const octave_value& cb,
07639                                        const octave_value& data)
07640 {
07641   graphics_event e;
07642 
07643   e.rep = new callback_event (h, cb, data);
07644 
07645   return e;
07646 }
07647 
07648 graphics_event
07649 graphics_event::create_function_event (graphics_event::event_fcn fcn,
07650                                        void *data)
07651 {
07652   graphics_event e;
07653 
07654   e.rep = new function_event (fcn, data);
07655 
07656   return e;
07657 }
07658 
07659 graphics_event
07660 graphics_event::create_set_event (const graphics_handle& h,
07661                                   const std::string& name,
07662                                   const octave_value& data,
07663                                   bool notify_toolkit)
07664 {
07665   graphics_event e;
07666 
07667   e.rep = new set_event (h, name, data, notify_toolkit);
07668 
07669   return e;
07670 }
07671 
07672 static void
07673 xset_gcbo (const graphics_handle& h)
07674 {
07675   graphics_object go = gh_manager::get_object (0);
07676   root_figure::properties& props =
07677       dynamic_cast<root_figure::properties&> (go.get_properties ());
07678 
07679   props.set_callbackobject (h.as_octave_value ());
07680 }
07681 
07682 void
07683 gh_manager::do_restore_gcbo (void)
07684 {
07685   gh_manager::auto_lock guard;
07686 
07687   callback_objects.pop_front ();
07688 
07689   xset_gcbo (callback_objects.empty ()
07690              ? graphics_handle ()
07691              : callback_objects.front ().get_handle ());
07692 }
07693 
07694 void
07695 gh_manager::do_execute_listener (const graphics_handle& h,
07696                                  const octave_value& l)
07697 {
07698   if (octave_thread::is_octave_thread ())
07699     gh_manager::execute_callback (h, l, octave_value ());
07700   else
07701     {
07702       gh_manager::auto_lock guard;
07703 
07704       do_post_event (graphics_event::create_callback_event (h, l));
07705     }
07706 }
07707 
07708 void
07709 gh_manager::do_execute_callback (const graphics_handle& h,
07710                                  const octave_value& cb_arg,
07711                                  const octave_value& data)
07712 {
07713   if (cb_arg.is_defined () && ! cb_arg.is_empty ())
07714     {
07715       octave_value_list args;
07716       octave_function *fcn = 0;
07717 
07718       args(0) = h.as_octave_value ();
07719       if (data.is_defined ())
07720         args(1) = data;
07721       else
07722         args(1) = Matrix ();
07723 
07724       unwind_protect_safe frame;
07725       frame.add_fcn (gh_manager::restore_gcbo);
07726 
07727       if (true)
07728         {
07729           gh_manager::auto_lock guard;
07730 
07731           callback_objects.push_front (get_object (h));
07732           xset_gcbo (h);
07733         }
07734 
07735       BEGIN_INTERRUPT_WITH_EXCEPTIONS;
07736 
07737       // Copy CB because "function_value" method is non-const.
07738 
07739       octave_value cb = cb_arg;
07740 
07741       if (cb.is_function () || cb.is_function_handle ())
07742         fcn = cb.function_value ();
07743       else if (cb.is_string ())
07744         {
07745           int status;
07746           std::string s = cb.string_value ();
07747 
07748           eval_string (s, false, status, 0);
07749         }
07750       else if (cb.is_cell () && cb.length () > 0
07751                && (cb.rows () == 1 || cb.columns () == 1)
07752                && (cb.cell_value ()(0).is_function ()
07753                    || cb.cell_value ()(0).is_function_handle ()))
07754         {
07755           Cell c = cb.cell_value ();
07756 
07757           fcn = c(0).function_value ();
07758           if (! error_state)
07759             {
07760               for (int i = 1; i < c.length () ; i++)
07761                 args(1+i) = c(i);
07762             }
07763         }
07764       else
07765         {
07766           std::string nm = cb.class_name ();
07767           error ("trying to execute non-executable object (class = %s)",
07768                  nm.c_str ());
07769         }
07770 
07771       if (fcn && ! error_state)
07772         feval (fcn, args);
07773 
07774       END_INTERRUPT_WITH_EXCEPTIONS;
07775     }
07776 }
07777 
07778 void
07779 gh_manager::do_post_event (const graphics_event& e)
07780 {
07781   event_queue.push_back (e);
07782 
07783   command_editor::add_event_hook (gh_manager::process_events);
07784 }
07785 
07786 void
07787 gh_manager::do_post_callback (const graphics_handle& h, const std::string name,
07788                               const octave_value& data)
07789 {
07790   gh_manager::auto_lock guard;
07791 
07792   graphics_object go = get_object (h);
07793 
07794   if (go.valid_object ())
07795     {
07796       if (callback_objects.empty ())
07797         do_post_event (graphics_event::create_callback_event (h, name, data));
07798       else
07799         {
07800           const graphics_object& current = callback_objects.front ();
07801 
07802           if (current.get_properties ().is_interruptible ())
07803             do_post_event (graphics_event::create_callback_event (h, name, data));
07804           else
07805             {
07806               caseless_str busy_action (go.get_properties ().get_busyaction ());
07807 
07808               if (busy_action.compare ("queue"))
07809                 do_post_event (graphics_event::create_callback_event (h, name, data));
07810               else
07811                 {
07812                   caseless_str cname (name);
07813 
07814                   if (cname.compare ("deletefcn")
07815                       || cname.compare ("createfcn")
07816                       || (go.isa ("figure")
07817                           && (cname.compare ("closerequestfcn")
07818                               || cname.compare ("resizefcn"))))
07819                     do_post_event (graphics_event::create_callback_event (h, name, data));
07820                 }
07821             }
07822         }
07823     }
07824 }
07825 
07826 void
07827 gh_manager::do_post_function (graphics_event::event_fcn fcn, void* fcn_data)
07828 {
07829   gh_manager::auto_lock guard;
07830 
07831   do_post_event (graphics_event::create_function_event (fcn, fcn_data));
07832 }
07833 
07834 void
07835 gh_manager::do_post_set (const graphics_handle& h, const std::string name,
07836                          const octave_value& value, bool notify_toolkit)
07837 {
07838   gh_manager::auto_lock guard;
07839 
07840   do_post_event (graphics_event::create_set_event (h, name, value,
07841                                                    notify_toolkit));
07842 }
07843 
07844 int
07845 gh_manager::do_process_events (bool force)
07846 {
07847   graphics_event e;
07848   bool old_Vdrawnow_requested = Vdrawnow_requested;
07849   bool events_executed = false;
07850 
07851   do
07852     {
07853       e = graphics_event ();
07854 
07855       gh_manager::lock ();
07856 
07857       if (! event_queue.empty ())
07858         {
07859           if (callback_objects.empty () || force)
07860             {
07861               e = event_queue.front ();
07862 
07863               event_queue.pop_front ();
07864             }
07865           else
07866             {
07867               const graphics_object& go = callback_objects.front ();
07868 
07869               if (go.get_properties ().is_interruptible ())
07870                 {
07871                   e = event_queue.front ();
07872 
07873                   event_queue.pop_front ();
07874                 }
07875             }
07876         }
07877 
07878       gh_manager::unlock ();
07879 
07880       if (e.ok ())
07881         {
07882           e.execute ();
07883           events_executed = true;
07884         }
07885     }
07886   while (e.ok ());
07887 
07888   gh_manager::lock ();
07889 
07890   if (event_queue.empty () && event_processing == 0)
07891     command_editor::remove_event_hook (gh_manager::process_events);
07892 
07893   gh_manager::unlock ();
07894 
07895   if (events_executed)
07896     flush_octave_stdout ();
07897 
07898   if (Vdrawnow_requested && ! old_Vdrawnow_requested)
07899     {
07900       feval ("drawnow");
07901 
07902       Vdrawnow_requested = false;
07903     }
07904 
07905   return 0;
07906 }
07907 
07908 void
07909 gh_manager::do_enable_event_processing (bool enable)
07910 {
07911   gh_manager::auto_lock guard;
07912 
07913   if (enable)
07914     {
07915       event_processing++;
07916 
07917       command_editor::add_event_hook (gh_manager::process_events);
07918     }
07919   else
07920     {
07921       event_processing--;
07922 
07923       if (event_queue.empty () && event_processing == 0)
07924         command_editor::remove_event_hook (gh_manager::process_events);
07925     }
07926 }
07927 
07928 property_list::plist_map_type
07929 root_figure::init_factory_properties (void)
07930 {
07931   property_list::plist_map_type plist_map;
07932 
07933   plist_map["figure"] = figure::properties::factory_defaults ();
07934   plist_map["axes"] = axes::properties::factory_defaults ();
07935   plist_map["line"] = line::properties::factory_defaults ();
07936   plist_map["text"] = text::properties::factory_defaults ();
07937   plist_map["image"] = image::properties::factory_defaults ();
07938   plist_map["patch"] = patch::properties::factory_defaults ();
07939   plist_map["surface"] = surface::properties::factory_defaults ();
07940   plist_map["hggroup"] = hggroup::properties::factory_defaults ();
07941   plist_map["uimenu"] = uimenu::properties::factory_defaults ();
07942   plist_map["uicontrol"] = uicontrol::properties::factory_defaults ();
07943   plist_map["uipanel"] = uipanel::properties::factory_defaults ();
07944   plist_map["uicontextmenu"] = uicontextmenu::properties::factory_defaults ();
07945   plist_map["uitoolbar"] = uitoolbar::properties::factory_defaults ();
07946   plist_map["uipushtool"] = uipushtool::properties::factory_defaults ();
07947   plist_map["uitoggletool"] = uitoggletool::properties::factory_defaults ();
07948 
07949   return plist_map;
07950 }
07951 
07952 // ---------------------------------------------------------------------
07953 
07954 DEFUN (ishandle, args, ,
07955   "-*- texinfo -*-\n\
07956 @deftypefn {Built-in Function} {} ishandle (@var{h})\n\
07957 Return true if @var{h} is a graphics handle and false otherwise.\n\
07958 @var{h} may also be a matrix of handles in which case a logical\n\
07959 array is returned that is true where the elements of @var{h} are\n\
07960 graphics handles and false where they are not.\n\
07961 @seealso{isfigure}\n\
07962 @end deftypefn")
07963 {
07964   gh_manager::auto_lock guard;
07965 
07966   octave_value retval;
07967 
07968   if (args.length () == 1)
07969     retval = is_handle (args(0));
07970   else
07971     print_usage ();
07972 
07973   return retval;
07974 }
07975 
07976 static bool
07977 is_handle_visible (const graphics_handle& h)
07978 {
07979   return h.ok () && gh_manager::is_handle_visible (h);
07980 }
07981 
07982 static bool
07983 is_handle_visible (double val)
07984 {
07985   return is_handle_visible (gh_manager::lookup (val));
07986 }
07987 
07988 static octave_value
07989 is_handle_visible (const octave_value& val)
07990 {
07991   octave_value retval = false;
07992 
07993   if (val.is_real_scalar () && is_handle_visible (val.double_value ()))
07994     retval = true;
07995   else if (val.is_numeric_type () && val.is_real_type ())
07996     {
07997       const NDArray handles = val.array_value ();
07998 
07999       if (! error_state)
08000         {
08001           boolNDArray result (handles.dims ());
08002 
08003           for (octave_idx_type i = 0; i < handles.numel (); i++)
08004             result.xelem (i) = is_handle_visible (handles (i));
08005 
08006           retval = result;
08007         }
08008     }
08009 
08010   return retval;
08011 }
08012 
08013 DEFUN (__is_handle_visible__, args, ,
08014   "-*- texinfo -*-\n\
08015 @deftypefn {Built-in Function} __is_handle_visible__ (@var{h})\n\
08016 Undocumented internal function.\n\
08017 @end deftypefn")
08018 {
08019   octave_value retval;
08020 
08021   if (args.length () == 1)
08022     retval = is_handle_visible (args(0));
08023   else
08024     print_usage ();
08025 
08026   return retval;
08027 }
08028 
08029 DEFUN (reset, args, ,
08030   "-*- texinfo -*-\n\
08031 @deftypefn {Built-in Function} {} reset (@var{h}, @var{property})\n\
08032 Remove any defaults set for the handle @var{h}.  The default figure\n\
08033 properties of \"position\", \"units\", \"windowstyle\" and\n\
08034 \"paperunits\" and the default axes properties of \"position\" and \"units\"\n\
08035 are not reset.\n\
08036 @end deftypefn")
08037 {
08038   int nargin = args.length ();
08039 
08040   if (nargin != 1)
08041     print_usage ();
08042   else
08043     {
08044       // get vector of graphics handles
08045       ColumnVector hcv (args(0).vector_value ());
08046 
08047       if (! error_state)
08048         {
08049           // loop over graphics objects
08050           for (octave_idx_type n = 0; n < hcv.length (); n++)
08051             gh_manager::get_object (hcv(n)).reset_default_properties ();
08052         }
08053     }
08054 
08055   return octave_value ();
08056 }
08057 
08058 DEFUN (set, args, nargout,
08059   "-*- texinfo -*-\n\
08060 @deftypefn  {Built-in Function} {} set (@var{h}, @var{property}, @var{value}, @dots{})\n\
08061 @deftypefnx {Built-in Function} {} set (@var{h}, @var{properties}, @var{values})\n\
08062 @deftypefnx {Built-in Function} {} set (@var{h}, @var{pv})\n\
08063 Set named property values for the graphics handle (or vector of graphics\n\
08064 handles) @var{h}.\n\
08065 There are three ways how to give the property names and values:\n\
08066 \n\
08067 @itemize\n\
08068 @item as a comma separated list of @var{property}, @var{value} pairs\n\
08069 \n\
08070 Here, each @var{property} is a string containing the property name, each\n\
08071 @var{value} is a value of the appropriate type for the property.\n\
08072 \n\
08073 @item as a cell array of strings @var{properties} containing property names\n\
08074 and a cell array @var{values} containing property values.\n\
08075 \n\
08076 In this case, the number of columns of @var{values} must match the number of\n\
08077 elements in @var{properties}.  The first column of @var{values} contains\n\
08078 values for the first entry in @var{properties}, etc.  The number of rows of\n\
08079 @var{values} must be 1 or match the number of elements of @var{h}.  In the\n\
08080 first case, each handle in @var{h} will be assigned the same values.  In the\n\
08081 latter case, the first handle in @var{h} will be assigned the values from\n\
08082 the first row of @var{values} and so on.\n\
08083 \n\
08084 @item as a structure array @var{pv}\n\
08085 \n\
08086 Here, the field names of @var{pv} represent the property names, and the field\n\
08087 values give the property values.  In contrast to the previous case, all\n\
08088 elements of @var{pv} will be set in all handles in @var{h} independent of\n\
08089 the dimensions of @var{pv}.\n\
08090 @end itemize\n\
08091 @end deftypefn")
08092 {
08093   gh_manager::auto_lock guard;
08094 
08095   octave_value retval;
08096 
08097   int nargin = args.length ();
08098 
08099   if (nargin > 0)
08100     {
08101       // get vector of graphics handles
08102       ColumnVector hcv (args(0).vector_value ());
08103 
08104       if (! error_state)
08105         {
08106           bool request_drawnow = false;
08107 
08108           // loop over graphics objects
08109           for (octave_idx_type n = 0; n < hcv.length (); n++)
08110             {
08111               graphics_object obj = gh_manager::get_object (hcv(n));
08112 
08113               if (obj)
08114                 {
08115                   if (nargin == 3 && args(1).is_cellstr ()
08116                       && args(2).is_cell ())
08117                     {
08118                       if (args(2).cell_value ().rows () == 1)
08119                         {
08120                           obj.set (args(1).cellstr_value (),
08121                                    args(2).cell_value (), 0);
08122                         }
08123                       else if (hcv.length () == args(2).cell_value ().rows ())
08124                         {
08125                           obj.set (args(1).cellstr_value (),
08126                                    args(2).cell_value (), n);
08127                         }
08128                       else
08129                         {
08130                           error("set: number of graphics handles must match number of value rows (%d != %d)",
08131                                 hcv.length (), args(2).cell_value ().rows ());
08132                           break;
08133 
08134                         }
08135                     }
08136                   else if (nargin == 2 && args(1).is_map ())
08137                     {
08138                       obj.set (args(1).map_value ());
08139                     }
08140                   else if (nargin == 1)
08141                     {
08142                       if (nargout != 0)
08143                         retval = obj.values_as_struct ();
08144                       else
08145                         {
08146                           std::string s = obj.values_as_string ();
08147                           if (! error_state)
08148                             octave_stdout << s;
08149                         }
08150                     }
08151                   else
08152                     {
08153                       obj.set (args.splice (0, 1));
08154                       request_drawnow = true;
08155                     }
08156                 }
08157               else
08158                 {
08159                   error ("set: invalid handle (= %g)", hcv(n));
08160                   break;
08161                 }
08162 
08163               if (error_state)
08164                 break;
08165 
08166               request_drawnow = true;
08167            }
08168 
08169           if (! error_state && request_drawnow)
08170             Vdrawnow_requested = true;
08171         }
08172       else
08173         error ("set: expecting graphics handle as first argument");
08174     }
08175   else
08176     print_usage ();
08177 
08178   return retval;
08179 }
08180 
08181 static std::string
08182 get_graphics_object_type (const double val)
08183 {
08184   std::string retval;
08185 
08186   graphics_object obj = gh_manager::get_object (val);
08187 
08188   if (obj)
08189     retval = obj.type ();
08190   else
08191     error ("get: invalid handle (= %g)", val);
08192 
08193   return retval;
08194 }
08195 
08196 DEFUN (get, args, ,
08197   "-*- texinfo -*-\n\
08198 @deftypefn {Built-in Function} {} get (@var{h}, @var{p})\n\
08199 Return the named property @var{p} from the graphics handle @var{h}.\n\
08200 If @var{p} is omitted, return the complete property list for @var{h}.\n\
08201 If @var{h} is a vector, return a cell array including the property\n\
08202 values or lists respectively.\n\
08203 @end deftypefn")
08204 {
08205   gh_manager::auto_lock guard;
08206 
08207   octave_value retval;
08208 
08209   Cell vals;
08210 
08211   int nargin = args.length ();
08212 
08213   bool use_cell_format = false;
08214 
08215   if (nargin == 1 || nargin == 2)
08216     {
08217       if (args(0).is_empty())
08218         {
08219           retval = Matrix ();
08220           return retval;
08221         }
08222 
08223       ColumnVector hcv (args(0).vector_value ());
08224 
08225       if (! error_state)
08226         {
08227           octave_idx_type len = hcv.length ();
08228 
08229           if (nargin == 1 && len > 1)
08230             {
08231               std::string t0 = get_graphics_object_type (hcv(0));
08232 
08233               if (! error_state)
08234                 {
08235                   for (octave_idx_type n = 1; n < len; n++)
08236                     {
08237                       std::string t = get_graphics_object_type (hcv(n));
08238 
08239                       if (error_state)
08240                         break;
08241 
08242                       if (t != t0)
08243                         {
08244                           error ("get: vector of handles must all have same type");
08245                           break;
08246                         }
08247                     }
08248 
08249                 }
08250             }
08251 
08252           if (! error_state)
08253             {
08254               if (nargin > 1 && args(1).is_cellstr ())
08255                 {
08256                   Array<std::string> plist = args(1).cellstr_value ();
08257 
08258                   if (! error_state)
08259                     {
08260                       octave_idx_type plen = plist.numel ();
08261 
08262                       use_cell_format = true;
08263 
08264                       vals.resize (dim_vector (len, plen));
08265 
08266                       for (octave_idx_type n = 0; ! error_state && n < len; n++)
08267                         {
08268                           graphics_object obj = gh_manager::get_object (hcv(n));
08269 
08270                           if (obj)
08271                             {
08272                               for (octave_idx_type m = 0; ! error_state && m < plen; m++)
08273                                 {
08274                                   caseless_str property = plist(m);
08275 
08276                                   vals(n, m) = obj.get (property);
08277                                 }
08278                             }
08279                           else
08280                             {
08281                               error ("get: invalid handle (= %g)", hcv(n));
08282                               break;
08283                             }
08284                         }
08285                     }
08286                   else
08287                     error ("get: expecting property name or cell array of property names as second argument");
08288                 }
08289               else
08290                 {
08291                   caseless_str property;
08292 
08293                   if (nargin > 1)
08294                     {
08295                       property = args(1).string_value ();
08296 
08297                       if (error_state)
08298                         error ("get: expecting property name or cell array of property names as second argument");
08299                     }
08300 
08301                   vals.resize (dim_vector (len, 1));
08302 
08303                   if (! error_state)
08304                     {
08305                       for (octave_idx_type n = 0; ! error_state && n < len; n++)
08306                         {
08307                           graphics_object obj = gh_manager::get_object (hcv(n));
08308 
08309                           if (obj)
08310                             {
08311                               if (nargin == 1)
08312                                 vals(n) = obj.get ();
08313                               else
08314                                 vals(n) = obj.get (property);
08315                             }
08316                           else
08317                             {
08318                               error ("get: invalid handle (= %g)", hcv(n));
08319                               break;
08320                             }
08321                         }
08322                     }
08323                 }
08324             }
08325         }
08326       else
08327         error ("get: expecting graphics handle as first argument");
08328     }
08329   else
08330     print_usage ();
08331 
08332   if (! error_state)
08333     {
08334       if (use_cell_format)
08335         retval = vals;
08336       else
08337         {
08338           octave_idx_type len = vals.numel ();
08339 
08340           if (len == 0)
08341             retval = Matrix ();
08342           else if (len == 1)
08343             retval = vals(0);
08344           else if (len > 1 && nargin == 1)
08345             {
08346               OCTAVE_LOCAL_BUFFER (octave_scalar_map, tmp, len);
08347 
08348               for (octave_idx_type n = 0; n < len; n++)
08349                 tmp[n] = vals(n).scalar_map_value ();
08350 
08351               retval = octave_map::cat (0, len, tmp);
08352             }
08353           else
08354             retval = vals;
08355         }
08356     }
08357 
08358   return retval;
08359 }
08360 
08361 /*
08362 %!assert (get (findobj (0, 'Tag', 'nonexistenttag'), 'nonexistentproperty'), [])
08363 */
08364 
08365 // Return all properties from the graphics handle @var{h}.
08366 // If @var{h} is a vector, return a cell array including the
08367 // property values or lists respectively.
08368 
08369 DEFUN (__get__, args, ,
08370   "-*- texinfo -*-\n\
08371 @deftypefn {Built-in Function} {} __get__ (@var{h})\n\
08372 Undocumented internal function.\n\
08373 @end deftypefn")
08374 {
08375   gh_manager::auto_lock guard;
08376 
08377   octave_value retval;
08378 
08379   Cell vals;
08380 
08381   int nargin = args.length ();
08382 
08383   if (nargin == 1)
08384     {
08385       ColumnVector hcv (args(0).vector_value ());
08386 
08387       if (! error_state)
08388         {
08389           octave_idx_type len = hcv.length ();
08390 
08391           vals.resize (dim_vector (len, 1));
08392 
08393           for (octave_idx_type n = 0; n < len; n++)
08394             {
08395               graphics_object obj = gh_manager::get_object (hcv(n));
08396 
08397               if (obj)
08398                 vals(n) = obj.get (true);
08399               else
08400                 {
08401                   error ("get: invalid handle (= %g)", hcv(n));
08402                   break;
08403                 }
08404             }
08405         }
08406       else
08407         error ("get: expecting graphics handle as first argument");
08408     }
08409   else
08410     print_usage ();
08411 
08412   if (! error_state)
08413     {
08414       octave_idx_type len = vals.numel ();
08415 
08416       if (len > 1)
08417         retval = vals;
08418       else if (len == 1)
08419         retval = vals(0);
08420     }
08421 
08422   return retval;
08423 }
08424 
08425 static octave_value
08426 make_graphics_object (const std::string& go_name,
08427                       bool integer_figure_handle,
08428                       const octave_value_list& args)
08429 {
08430   octave_value retval;
08431 
08432   double val = octave_NaN;
08433 
08434   octave_value_list xargs = args.splice (0, 1);
08435 
08436   caseless_str p ("parent");
08437 
08438   for (int i = 0; i < xargs.length (); i++)
08439     if (xargs(i).is_string ()
08440         && p.compare (xargs(i).string_value ()))
08441       {
08442         if (i < (xargs.length () - 1))
08443           {
08444             val = xargs(i+1).double_value ();
08445 
08446             if (! error_state)
08447               {
08448                 xargs = xargs.splice (i, 2);
08449                 break;
08450               }
08451           }
08452         else
08453           error ("__go_%s__: missing value for parent property",
08454                  go_name.c_str ());
08455       }
08456 
08457   if (! error_state && xisnan (val))
08458     val = args(0).double_value ();
08459 
08460   if (! error_state)
08461     {
08462       graphics_handle parent = gh_manager::lookup (val);
08463 
08464       if (parent.ok ())
08465         {
08466           graphics_handle h
08467             = gh_manager::make_graphics_handle (go_name, parent,
08468                                                 integer_figure_handle,
08469                                                 false, false);
08470 
08471           if (! error_state)
08472             {
08473               adopt (parent, h);
08474 
08475               xset (h, xargs);
08476               xcreatefcn (h);
08477               xinitialize (h);
08478 
08479               retval = h.value ();
08480 
08481               if (! error_state)
08482                 Vdrawnow_requested = true;
08483             }
08484           else
08485             error ("__go%s__: unable to create graphics handle",
08486                    go_name.c_str ());
08487         }
08488       else
08489         error ("__go_%s__: invalid parent", go_name.c_str ());
08490     }
08491   else
08492     error ("__go_%s__: invalid parent", go_name.c_str ());
08493 
08494   return retval;
08495 }
08496 
08497 DEFUN (__go_figure__, args, ,
08498    "-*- texinfo -*-\n\
08499 @deftypefn {Built-in Function} {} __go_figure__ (@var{fignum})\n\
08500 Undocumented internal function.\n\
08501 @end deftypefn")
08502 {
08503   gh_manager::auto_lock guard;
08504 
08505   octave_value retval;
08506 
08507   if (args.length () > 0)
08508     {
08509       double val = args(0).double_value ();
08510 
08511       if (! error_state)
08512         {
08513           if (is_figure (val))
08514             {
08515               graphics_handle h = gh_manager::lookup (val);
08516 
08517               xset (h, args.splice (0, 1));
08518 
08519               retval = h.value ();
08520             }
08521           else
08522             {
08523               bool int_fig_handle = true;
08524 
08525               octave_value_list xargs = args.splice (0, 1);
08526 
08527               graphics_handle h = octave_NaN;
08528 
08529               if (xisnan (val))
08530                 {
08531                   caseless_str p ("integerhandle");
08532 
08533                   for (int i = 0; i < xargs.length (); i++)
08534                     {
08535                       if (xargs(i).is_string ()
08536                           && p.compare (xargs(i).string_value ()))
08537                         {
08538                           if (i < (xargs.length () - 1))
08539                             {
08540                               std::string pval = xargs(i+1).string_value ();
08541 
08542                               if (! error_state)
08543                                 {
08544                                   caseless_str on ("on");
08545                                   int_fig_handle = on.compare (pval);
08546                                   xargs = xargs.splice (i, 2);
08547                                   break;
08548                                 }
08549                             }
08550                         }
08551                     }
08552 
08553                   h = gh_manager::make_graphics_handle ("figure", 0,
08554                                                         int_fig_handle,
08555                                                         false, false);
08556 
08557                   if (! int_fig_handle)
08558                     {
08559                       // We need to intiailize the integerhandle
08560                       // property without calling the set_integerhandle
08561                       // method, because doing that will generate a new
08562                       // handle value...
08563 
08564                       graphics_object go = gh_manager::get_object (h);
08565                       go.get_properties ().init_integerhandle ("off");
08566                     }
08567                 }
08568               else if (val > 0 && D_NINT (val) == val)
08569                 h = gh_manager::make_figure_handle (val, false);
08570 
08571               if (! error_state && h.ok ())
08572                 {
08573                   adopt (0, h);
08574 
08575                   gh_manager::push_figure (h);
08576 
08577                   xset (h, xargs);
08578                   xcreatefcn (h);
08579                   xinitialize (h);
08580 
08581                   retval = h.value ();
08582                 }
08583               else
08584                 error ("__go_figure__: failed to create figure handle");
08585             }
08586         }
08587       else
08588         error ("__go_figure__: expecting figure number to be double value");
08589     }
08590   else
08591     print_usage ();
08592 
08593   return retval;
08594 }
08595 
08596 #define GO_BODY(TYPE) \
08597   gh_manager::auto_lock guard; \
08598  \
08599   octave_value retval; \
08600  \
08601   if (args.length () > 0) \
08602     retval = make_graphics_object (#TYPE, false, args);  \
08603   else \
08604     print_usage (); \
08605  \
08606   return retval
08607 
08608 int
08609 calc_dimensions (const graphics_object& go)
08610 {
08611 
08612   int nd = 2;
08613 
08614   if (go.isa ("surface"))
08615     nd = 3;
08616 
08617   if ((go.isa ("line") || go.isa ("patch")) && ! go.get("zdata").is_empty ())
08618     nd = 3;
08619 
08620   Matrix kids = go.get_properties().get_children ();
08621 
08622   for (octave_idx_type i = 0; i < kids.length (); i++)
08623     {
08624       graphics_handle hnd = gh_manager::lookup (kids(i));
08625 
08626       if (hnd.ok ())
08627         {
08628           const graphics_object& kid = gh_manager::get_object(hnd);
08629 
08630           if (kid.valid_object())
08631             nd = calc_dimensions (kid);
08632 
08633           if (nd == 3)
08634             break;
08635         }
08636     }
08637 
08638   return nd;
08639 }
08640 
08641 DEFUN (__calc_dimensions__, args, ,
08642   "-*- texinfo -*-\n\
08643 @deftypefn {Built-in Function} {} __calc_dimensions__ (@var{axes})\n\
08644 Internal function.  Determine the number of dimensions in a graphics\n\
08645 object, whether 2 or 3.\n\
08646 @end deftypefn")
08647 {
08648   gh_manager::auto_lock guard;
08649 
08650   octave_value retval;
08651 
08652   int nargin = args.length ();
08653 
08654   if (nargin == 1)
08655     {
08656       double h = args(0).double_value ();
08657 
08658       if (! error_state)
08659         retval = calc_dimensions (gh_manager::get_object (h));
08660       else
08661         error ("__calc_dimensions__: expecting graphics handle as only argument");
08662     }
08663   else
08664     print_usage ();
08665 
08666   return retval;
08667 }
08668 
08669 DEFUN (__go_axes__, args, ,
08670   "-*- texinfo -*-\n\
08671 @deftypefn {Built-in Function} {} __go_axes__ (@var{parent})\n\
08672 Undocumented internal function.\n\
08673 @end deftypefn")
08674 {
08675   GO_BODY (axes);
08676 }
08677 
08678 DEFUN (__go_line__, args, ,
08679   "-*- texinfo -*-\n\
08680 @deftypefn {Built-in Function} {} __go_line__ (@var{parent})\n\
08681 Undocumented internal function.\n\
08682 @end deftypefn")
08683 {
08684   GO_BODY (line);
08685 }
08686 
08687 DEFUN (__go_text__, args, ,
08688   "-*- texinfo -*-\n\
08689 @deftypefn {Built-in Function} {} __go_text__ (@var{parent})\n\
08690 Undocumented internal function.\n\
08691 @end deftypefn")
08692 {
08693   GO_BODY (text);
08694 }
08695 
08696 DEFUN (__go_image__, args, ,
08697   "-*- texinfo -*-\n\
08698 @deftypefn {Built-in Function} {} __go_image__ (@var{parent})\n\
08699 Undocumented internal function.\n\
08700 @end deftypefn")
08701 {
08702   GO_BODY (image);
08703 }
08704 
08705 DEFUN (__go_surface__, args, ,
08706   "-*- texinfo -*-\n\
08707 @deftypefn {Built-in Function} {} __go_surface__ (@var{parent})\n\
08708 Undocumented internal function.\n\
08709 @end deftypefn")
08710 {
08711   GO_BODY (surface);
08712 }
08713 
08714 DEFUN (__go_patch__, args, ,
08715   "-*- texinfo -*-\n\
08716 @deftypefn {Built-in Function} {} __go_patch__ (@var{parent})\n\
08717 Undocumented internal function.\n\
08718 @end deftypefn")
08719 {
08720   GO_BODY (patch);
08721 }
08722 
08723 DEFUN (__go_hggroup__, args, ,
08724   "-*- texinfo -*-\n\
08725 @deftypefn {Built-in Function} {} __go_hggroup__ (@var{parent})\n\
08726 Undocumented internal function.\n\
08727 @end deftypefn")
08728 {
08729   GO_BODY (hggroup);
08730 }
08731 
08732 DEFUN (__go_uimenu__, args, ,
08733   "-*- texinfo -*-\n\
08734 @deftypefn {Built-in Function} {} __go_uimenu__ (@var{parent})\n\
08735 Undocumented internal function.\n\
08736 @end deftypefn")
08737 {
08738   GO_BODY (uimenu);
08739 }
08740 
08741 DEFUN (__go_uicontrol__, args, ,
08742   "-*- texinfo -*-\n\
08743 @deftypefn {Built-in Function} {} __go_uicontrol__ (@var{parent})\n\
08744 Undocumented internal function.\n\
08745 @end deftypefn")
08746 {
08747   GO_BODY (uicontrol);
08748 }
08749 
08750 DEFUN (__go_uipanel__, args, ,
08751   "-*- texinfo -*-\n\
08752 @deftypefn {Built-in Function} {} __go_uipanel__ (@var{parent})\n\
08753 Undocumented internal function.\n\
08754 @end deftypefn")
08755 {
08756   GO_BODY (uipanel);
08757 }
08758 
08759 DEFUN (__go_uicontextmenu__, args, ,
08760   "-*- texinfo -*-\n\
08761 @deftypefn {Built-in Function} {} __go_uicontextmenu__ (@var{parent})\n\
08762 Undocumented internal function.\n\
08763 @end deftypefn")
08764 {
08765   GO_BODY (uicontextmenu);
08766 }
08767 
08768 DEFUN (__go_uitoolbar__, args, ,
08769   "-*- texinfo -*-\n\
08770 @deftypefn {Built-in Function} {} __go_uitoolbar__ (@var{parent})\n\
08771 Undocumented internal function.\n\
08772 @end deftypefn")
08773 {
08774   GO_BODY (uitoolbar);
08775 }
08776 
08777 DEFUN (__go_uipushtool__, args, ,
08778   "-*- texinfo -*-\n\
08779 @deftypefn {Built-in Function} {} __go_uipushtool__ (@var{parent})\n\
08780 Undocumented internal function.\n\
08781 @end deftypefn")
08782 {
08783   GO_BODY (uipushtool);
08784 }
08785 
08786 DEFUN (__go_uitoggletool__, args, ,
08787   "-*- texinfo -*-\n\
08788 @deftypefn {Built-in Function} {} __go_uitoggletool__ (@var{parent})\n\
08789 Undocumented internal function.\n\
08790 @end deftypefn")
08791 {
08792   GO_BODY (uitoggletool);
08793 }
08794 
08795 DEFUN (__go_delete__, args, ,
08796   "-*- texinfo -*-\n\
08797 @deftypefn {Built-in Function} {} __go_delete__ (@var{h})\n\
08798 Undocumented internal function.\n\
08799 @end deftypefn")
08800 {
08801   gh_manager::auto_lock guard;
08802 
08803   octave_value_list retval;
08804 
08805   if (args.length () == 1)
08806     {
08807       graphics_handle h = octave_NaN;
08808 
08809       const NDArray vals = args (0).array_value ();
08810 
08811       if (! error_state)
08812         {
08813           // Check is all the handles to delete are valid first
08814           // as callbacks might delete one of the handles we
08815           // later want to delete
08816           for (octave_idx_type i = 0; i < vals.numel (); i++)
08817             {
08818               h = gh_manager::lookup (vals.elem (i));
08819 
08820               if (! h.ok ())
08821                 {
08822                   error ("delete: invalid graphics object (= %g)",
08823                          vals.elem (i));
08824                   break;
08825                 }
08826             }
08827 
08828           if (! error_state)
08829             delete_graphics_objects (vals);
08830         }
08831       else
08832         error ("delete: invalid graphics object");
08833     }
08834   else
08835     print_usage ();
08836 
08837   return retval;
08838 }
08839 
08840 DEFUN (__go_axes_init__, args, ,
08841   "-*- texinfo -*-\n\
08842 @deftypefn {Built-in Function} {} __go_axes_init__ (@var{h}, @var{mode})\n\
08843 Undocumented internal function.\n\
08844 @end deftypefn")
08845 {
08846   gh_manager::auto_lock guard;
08847 
08848   octave_value retval;
08849 
08850   int nargin = args.length ();
08851 
08852   std::string mode = "";
08853 
08854   if (nargin == 2)
08855     {
08856       mode = args(1).string_value ();
08857 
08858       if (error_state)
08859         return retval;
08860     }
08861 
08862   if (nargin == 1 || nargin == 2)
08863     {
08864       graphics_handle h = octave_NaN;
08865 
08866       double val = args(0).double_value ();
08867 
08868       if (! error_state)
08869         {
08870           h = gh_manager::lookup (val);
08871 
08872           if (h.ok ())
08873             {
08874               graphics_object obj = gh_manager::get_object (h);
08875 
08876               obj.set_defaults (mode);
08877 
08878               h = gh_manager::lookup (val);
08879               if (! h.ok ())
08880                 error ("__go_axes_init__: axis deleted during initialization (= %g)", val);
08881             }
08882           else
08883             error ("__go_axes_init__: invalid graphics object (= %g)", val);
08884         }
08885       else
08886         error ("__go_axes_init__: invalid graphics object");
08887     }
08888   else
08889     print_usage ();
08890 
08891   return retval;
08892 }
08893 
08894 DEFUN (__go_handles__, args, ,
08895    "-*- texinfo -*-\n\
08896 @deftypefn {Built-in Function} {} __go_handles__ (@var{show_hidden})\n\
08897 Undocumented internal function.\n\
08898 @end deftypefn")
08899 {
08900   gh_manager::auto_lock guard;
08901 
08902   bool show_hidden = false;
08903 
08904   if (args.length () > 0)
08905     show_hidden = args(0).bool_value ();
08906 
08907   return octave_value (gh_manager::handle_list (show_hidden));
08908 }
08909 
08910 DEFUN (__go_figure_handles__, args, ,
08911    "-*- texinfo -*-\n\
08912 @deftypefn {Built-in Function} {} __go_figure_handles__ (@var{show_hidden})\n\
08913 Undocumented internal function.\n\
08914 @end deftypefn")
08915 {
08916   gh_manager::auto_lock guard;
08917 
08918   bool show_hidden = false;
08919 
08920   if (args.length () > 0)
08921     show_hidden = args(0).bool_value ();
08922 
08923   return octave_value (gh_manager::figure_handle_list (show_hidden));
08924 }
08925 
08926 DEFUN (__go_execute_callback__, args, ,
08927    "-*- texinfo -*-\n\
08928 @deftypefn  {Built-in Function} {} __go_execute_callback__ (@var{h}, @var{name})\n\
08929 @deftypefnx {Built-in Function} {} __go_execute_callback__ (@var{h}, @var{name}, @var{param})\n\
08930 Undocumented internal function.\n\
08931 @end deftypefn")
08932 {
08933   octave_value retval;
08934 
08935   int nargin = args.length ();
08936 
08937   if (nargin == 2 || nargin == 3)
08938     {
08939       double val = args(0).double_value ();
08940 
08941       if (! error_state)
08942         {
08943           graphics_handle h = gh_manager::lookup (val);
08944 
08945           if (h.ok ())
08946             {
08947               std::string name = args(1).string_value ();
08948 
08949               if (! error_state)
08950                 {
08951                   if (nargin == 2)
08952                     gh_manager::execute_callback (h, name);
08953                   else
08954                     gh_manager::execute_callback (h, name, args(2));
08955                 }
08956               else
08957                 error ("__go_execute_callback__: invalid callback name");
08958             }
08959           else
08960             error ("__go_execute_callback__: invalid graphics object (= %g)",
08961                    val);
08962         }
08963       else
08964         error ("__go_execute_callback__: invalid graphics object");
08965     }
08966   else
08967     print_usage ();
08968 
08969   return retval;
08970 }
08971 
08972 DEFUN (__image_pixel_size__, args, ,
08973    "-*- texinfo -*-\n\
08974 @deftypefn {Built-in Function} {@var{px}, @var{py}} __image_pixel_size__ (@var{h})\n\
08975 Internal function: returns the pixel size of the image in normalized units.\n\
08976 @end deftypefn")
08977 {
08978   octave_value retval;
08979 
08980   int nargin = args.length ();
08981 
08982   if (nargin == 1)
08983     {
08984       double h = args(0).double_value ();
08985 
08986       if (! error_state)
08987         {
08988           graphics_object fobj = gh_manager::get_object (h);
08989           if (fobj &&  fobj.isa ("image"))
08990             {
08991               image::properties& ip =
08992                 dynamic_cast<image::properties&> (fobj.get_properties ());
08993 
08994               Matrix dp =  Matrix (1, 2, 0);
08995               dp(0, 0) = ip.pixel_xsize ();
08996               dp(0, 1) = ip.pixel_ysize ();
08997               retval = dp;
08998             }
08999           else
09000             error ("__image_pixel_size__: object is not an image");
09001         }
09002       else
09003         error ("__image_pixel_size__: argument is not a handle");
09004     }
09005   else
09006     print_usage ();
09007 
09008   return retval;
09009 }
09010 
09011 gtk_manager *gtk_manager::instance = 0;
09012 
09013 void
09014 gtk_manager::create_instance (void)
09015 {
09016   instance = new gtk_manager ();
09017 
09018   if (instance)
09019     singleton_cleanup_list::add (cleanup_instance);
09020 }
09021 
09022 graphics_toolkit
09023 gtk_manager::do_get_toolkit (void) const
09024 {
09025   graphics_toolkit retval;
09026 
09027   const_loaded_toolkits_iterator pl = loaded_toolkits.find (dtk);
09028 
09029   if (pl == loaded_toolkits.end ())
09030     {
09031       const_available_toolkits_iterator pa = available_toolkits.find (dtk);
09032 
09033       if (pa != available_toolkits.end ())
09034         {
09035           octave_value_list args;
09036           args(0) = dtk;
09037           feval ("graphics_toolkit", args);
09038 
09039           if (! error_state)
09040             pl = loaded_toolkits.find (dtk);
09041 
09042           if (error_state || pl == loaded_toolkits.end ())
09043             error ("failed to load %s graphics toolkit", dtk.c_str ());
09044           else
09045             retval = pl->second;
09046         }
09047       else
09048         error ("default graphics toolkit '%s' is not available!",
09049                dtk.c_str ());
09050     }
09051   else
09052     retval = pl->second;
09053 
09054   return retval;
09055 }
09056 
09057 DEFUN (available_graphics_toolkits, , ,
09058    "-*- texinfo -*-\n\
09059 @deftypefn {Built-in Function} {} available_graphics_toolkits ()\n\
09060 Return a cell array of registered graphics toolkits.\n\
09061 @seealso{graphics_toolkit, register_graphics_toolkit}\n\
09062 @end deftypefn")
09063 {
09064   gh_manager::auto_lock guard;
09065 
09066   return octave_value (gtk_manager::available_toolkits_list ());
09067 }
09068 
09069 DEFUN (register_graphics_toolkit, args, ,
09070    "-*- texinfo -*-\n\
09071 @deftypefn {Built-in Function} {} register_graphics_toolkit (@var{toolkit})\n\
09072 List @var{toolkit} as an available graphics toolkit.\n\
09073 @seealso{available_graphics_toolkits}\n\
09074 @end deftypefn")
09075 {
09076   octave_value retval;
09077 
09078   gh_manager::auto_lock guard;
09079 
09080   if (args.length () == 1)
09081     {
09082       std::string name = args(0).string_value ();
09083 
09084       if (! error_state)
09085         gtk_manager::register_toolkit (name);
09086       else
09087         error ("register_graphics_toolkit: expecting character string");
09088     }
09089   else
09090     print_usage ();
09091 
09092   return retval;
09093 }
09094 
09095 DEFUN (loaded_graphics_toolkits, , ,
09096    "-*- texinfo -*-\n\
09097 @deftypefn {Built-in Function} {} loaded_graphics_toolkits ()\n\
09098 Return a cell array of the currently loaded graphics toolkits.\n\
09099 @seealso{available_graphics_toolkits}\n\
09100 @end deftypefn")
09101 {
09102   gh_manager::auto_lock guard;
09103 
09104   return octave_value (gtk_manager::loaded_toolkits_list ());
09105 }
09106 
09107 DEFUN (drawnow, args, ,
09108    "-*- texinfo -*-\n\
09109 @deftypefn  {Built-in Function} {} drawnow ()\n\
09110 @deftypefnx {Built-in Function} {} drawnow (\"expose\")\n\
09111 @deftypefnx {Built-in Function} {} drawnow (@var{term}, @var{file}, @var{mono}, @var{debug_file})\n\
09112 Update figure windows and their children.  The event queue is flushed and\n\
09113 any callbacks generated are executed.  With the optional argument\n\
09114 @code{\"expose\"}, only graphic objects are updated and no other events or\n\
09115 callbacks are processed.\n\
09116 The third calling form of @code{drawnow} is for debugging and is\n\
09117 undocumented.\n\
09118 @end deftypefn")
09119 {
09120   static int drawnow_executing = 0;
09121 
09122   octave_value retval;
09123 
09124   gh_manager::lock ();
09125 
09126   unwind_protect frame;
09127   frame.protect_var (Vdrawnow_requested, false);
09128 
09129   frame.protect_var (drawnow_executing);
09130 
09131   if (++drawnow_executing <= 1)
09132     {
09133       if (args.length () == 0 || args.length () == 1)
09134         {
09135           Matrix hlist = gh_manager::figure_handle_list (true);
09136 
09137           for (int i = 0; ! error_state && i < hlist.length (); i++)
09138             {
09139               graphics_handle h = gh_manager::lookup (hlist(i));
09140 
09141               if (h.ok () && h != 0)
09142                 {
09143                   graphics_object go = gh_manager::get_object (h);
09144                   figure::properties& fprops = dynamic_cast <figure::properties&> (go.get_properties ());
09145 
09146                   if (fprops.is_modified ())
09147                     {
09148                       if (fprops.is_visible ())
09149                         {
09150                           gh_manager::unlock ();
09151 
09152                           fprops.get_toolkit ().redraw_figure (go);
09153 
09154                           gh_manager::lock ();
09155                         }
09156 
09157                       fprops.set_modified (false);
09158                     }
09159                 }
09160             }
09161 
09162           bool do_events = true;
09163 
09164           if (args.length () == 1)
09165             {
09166               caseless_str val (args(0).string_value ());
09167 
09168               if (! error_state && val.compare ("expose"))
09169                 do_events = false;
09170               else
09171                 {
09172                   error ("drawnow: invalid argument, expected 'expose' as argument");
09173                   return retval;
09174                 }
09175             }
09176 
09177           if (do_events)
09178             {
09179               gh_manager::unlock ();
09180 
09181               gh_manager::process_events ();
09182 
09183               gh_manager::lock ();
09184             }
09185         }
09186       else if (args.length () >= 2 && args.length () <= 4)
09187         {
09188           std::string term, file, debug_file;
09189           bool mono;
09190 
09191           term = args(0).string_value ();
09192 
09193           if (! error_state)
09194             {
09195               file = args(1).string_value ();
09196 
09197               if (! error_state)
09198                 {
09199                   size_t pos = file.find_first_not_of ("|");
09200                   if (pos > 0)
09201                     file = file.substr (pos);
09202                   else
09203                     {
09204                       pos = file.find_last_of (file_ops::dir_sep_chars ());
09205 
09206                       if (pos != std::string::npos)
09207                         {
09208                           std::string dirname = file.substr (0, pos+1);
09209 
09210                           file_stat fs (dirname);
09211 
09212                           if (! (fs && fs.is_dir ()))
09213                             {
09214                               error ("drawnow: nonexistent directory '%s'",
09215                                      dirname.c_str ());
09216 
09217                               return retval;
09218                             }
09219                         }
09220                     }
09221 
09222                   mono = (args.length () >= 3 ? args(2).bool_value () : false);
09223 
09224                   if (! error_state)
09225                     {
09226                       debug_file = (args.length () > 3 ? args(3).string_value ()
09227                                     : "");
09228 
09229                       if (! error_state)
09230                         {
09231                           graphics_handle h = gcf ();
09232 
09233                           if (h.ok ())
09234                             {
09235                               graphics_object go = gh_manager::get_object (h);
09236 
09237                               gh_manager::unlock ();
09238 
09239                               go.get_toolkit ()
09240                                 .print_figure (go, term, file, mono, debug_file);
09241 
09242                               gh_manager::lock ();
09243                             }
09244                           else
09245                             error ("drawnow: nothing to draw");
09246                         }
09247                       else
09248                         error ("drawnow: invalid DEBUG_FILE, expected a string value");
09249                     }
09250                   else
09251                     error ("drawnow: invalid colormode MONO, expected a boolean value");
09252                 }
09253               else
09254                 error ("drawnow: invalid FILE, expected a string value");
09255             }
09256           else
09257             error ("drawnow: invalid terminal TERM, expected a string value");
09258         }
09259       else
09260         print_usage ();
09261     }
09262 
09263   gh_manager::unlock ();
09264 
09265   return retval;
09266 }
09267 
09268 DEFUN (addlistener, args, ,
09269    "-*- texinfo -*-\n\
09270 @deftypefn {Built-in Function} {} addlistener (@var{h}, @var{prop}, @var{fcn})\n\
09271 Register @var{fcn} as listener for the property @var{prop} of the graphics\n\
09272 object @var{h}.  Property listeners are executed (in order of registration)\n\
09273 when the property is set.  The new value is already available when the\n\
09274 listeners are executed.\n\
09275 \n\
09276 @var{prop} must be a string naming a valid property in @var{h}.\n\
09277 \n\
09278 @var{fcn} can be a function handle, a string or a cell array whose first\n\
09279 element is a function handle.  If @var{fcn} is a function handle, the\n\
09280 corresponding function should accept at least 2 arguments, that will be\n\
09281 set to the object handle and the empty matrix respectively.  If @var{fcn}\n\
09282 is a string, it must be any valid octave expression.  If @var{fcn} is a cell\n\
09283 array, the first element must be a function handle with the same signature\n\
09284 as described above.  The next elements of the cell array are passed\n\
09285 as additional arguments to the function.\n\
09286 \n\
09287 Example:\n\
09288 \n\
09289 @example\n\
09290 @group\n\
09291 function my_listener (h, dummy, p1)\n\
09292   fprintf (\"my_listener called with p1=%s\\n\", p1);\n\
09293 endfunction\n\
09294 \n\
09295 addlistener (gcf, \"position\", @{@@my_listener, \"my string\"@})\n\
09296 @end group\n\
09297 @end example\n\
09298 \n\
09299 @end deftypefn")
09300 {
09301   gh_manager::auto_lock guard;
09302 
09303   octave_value retval;
09304 
09305   if (args.length () >= 3 && args.length () <= 4)
09306     {
09307       double h = args(0).double_value ();
09308 
09309       if (! error_state)
09310         {
09311           std::string pname = args(1).string_value ();
09312 
09313           if (! error_state)
09314             {
09315               graphics_handle gh = gh_manager::lookup (h);
09316 
09317               if (gh.ok ())
09318                 {
09319                   graphics_object go = gh_manager::get_object (gh);
09320 
09321                   go.add_property_listener (pname, args(2), POSTSET);
09322 
09323                   if (args.length () == 4)
09324                     {
09325                       caseless_str persistent = args(3).string_value ();
09326                       if (persistent.compare ("persistent"))
09327                         go.add_property_listener (pname, args(2), PERSISTENT);
09328                     }
09329                 }
09330               else
09331                 error ("addlistener: invalid graphics object (= %g)",
09332                        h);
09333             }
09334           else
09335             error ("addlistener: invalid property name, expected a string value");
09336         }
09337       else
09338         error ("addlistener: invalid handle");
09339     }
09340   else
09341     print_usage ();
09342 
09343   return retval;
09344 }
09345 
09346 DEFUN (dellistener, args, ,
09347    "-*- texinfo -*-\n\
09348 @deftypefn {Built-in Function} {} dellistener (@var{h}, @var{prop}, @var{fcn})\n\
09349 Remove the registration of @var{fcn} as a listener for the property\n\
09350 @var{prop} of the graphics object @var{h}.  The function @var{fcn} must\n\
09351 be the same variable (not just the same value), as was passed to the\n\
09352 original call to @code{addlistener}.\n\
09353 \n\
09354 If @var{fcn} is not defined then all listener functions of @var{prop}\n\
09355 are removed.\n\
09356 \n\
09357 Example:\n\
09358 \n\
09359 @example\n\
09360 @group\n\
09361 function my_listener (h, dummy, p1)\n\
09362   fprintf (\"my_listener called with p1=%s\\n\", p1);\n\
09363 endfunction\n\
09364 \n\
09365 c = @{@@my_listener, \"my string\"@};\n\
09366 addlistener (gcf, \"position\", c);\n\
09367 dellistener (gcf, \"position\", c);\n\
09368 @end group\n\
09369 @end example\n\
09370 \n\
09371 @end deftypefn")
09372 {
09373   gh_manager::auto_lock guard;
09374 
09375   octave_value retval;
09376 
09377   if (args.length () == 3 || args.length () == 2)
09378     {
09379       double h = args(0).double_value ();
09380 
09381       if (! error_state)
09382         {
09383           std::string pname = args(1).string_value ();
09384 
09385           if (! error_state)
09386             {
09387               graphics_handle gh = gh_manager::lookup (h);
09388 
09389               if (gh.ok ())
09390                 {
09391                   graphics_object go = gh_manager::get_object (gh);
09392 
09393                   if (args.length () == 2)
09394                     go.delete_property_listener (pname, octave_value (), POSTSET);
09395                   else
09396                     {
09397                       caseless_str persistent = args(2).string_value ();
09398                       if (persistent.compare ("persistent"))
09399                         {
09400                           go.delete_property_listener (pname, octave_value (), PERSISTENT);
09401                           go.delete_property_listener (pname, octave_value (), POSTSET);
09402                         }
09403                       else
09404                         go.delete_property_listener (pname, args(2), POSTSET);
09405                     }
09406                 }
09407               else
09408                 error ("dellistener: invalid graphics object (= %g)",
09409                        h);
09410             }
09411           else
09412             error ("dellistener: invalid property name, expected a string value");
09413         }
09414       else
09415         error ("dellistener: invalid handle");
09416     }
09417   else
09418     print_usage ();
09419 
09420   return retval;
09421 }
09422 
09423 DEFUN (addproperty, args, ,
09424   "-*- texinfo -*-\n\
09425 @deftypefn  {Built-in Function} {} addproperty (@var{name}, @var{h}, @var{type})\n\
09426 @deftypefnx {Built-in Function} {} addproperty (@var{name}, @var{h}, @var{type}, @var{arg}, @dots{})\n\
09427 Create a new property named @var{name} in graphics object @var{h}.\n\
09428 @var{type} determines the type of the property to create.  @var{args}\n\
09429 usually contains the default value of the property, but additional\n\
09430 arguments might be given, depending on the type of the property.\n\
09431 \n\
09432 The supported property types are:\n\
09433 \n\
09434 @table @code\n\
09435 @item string\n\
09436 A string property.  @var{arg} contains the default string value.\n\
09437 \n\
09438 @item any\n\
09439 An un-typed property.  This kind of property can hold any octave\n\
09440 value.  @var{args} contains the default value.\n\
09441 \n\
09442 @item radio\n\
09443 A string property with a limited set of accepted values.  The first\n\
09444 argument must be a string with all accepted values separated by\n\
09445 a vertical bar ('|').  The default value can be marked by enclosing\n\
09446 it with a '@{' '@}' pair.  The default value may also be given as\n\
09447 an optional second string argument.\n\
09448 \n\
09449 @item boolean\n\
09450 A boolean property.  This property type is equivalent to a radio\n\
09451 property with \"on|off\" as accepted values.  @var{arg} contains\n\
09452 the default property value.\n\
09453 \n\
09454 @item double\n\
09455 A scalar double property.  @var{arg} contains the default value.\n\
09456 \n\
09457 @item handle\n\
09458 A handle property.  This kind of property holds the handle of a\n\
09459 graphics object.  @var{arg} contains the default handle value.\n\
09460 When no default value is given, the property is initialized to\n\
09461 the empty matrix.\n\
09462 \n\
09463 @item data\n\
09464 A data (matrix) property.  @var{arg} contains the default data\n\
09465 value.  When no default value is given, the data is initialized to\n\
09466 the empty matrix.\n\
09467 \n\
09468 @item color\n\
09469 A color property.  @var{arg} contains the default color value.\n\
09470 When no default color is given, the property is set to black.\n\
09471 An optional second string argument may be given to specify an\n\
09472 additional set of accepted string values (like a radio property).\n\
09473 @end table\n\
09474 \n\
09475 @var{type} may also be the concatenation of a core object type and\n\
09476 a valid property name for that object type.  The property created\n\
09477 then has the same characteristics as the referenced property (type,\n\
09478 possible values, hidden state@dots{}).  This allows to clone an existing\n\
09479 property into the graphics object @var{h}.\n\
09480 \n\
09481 Examples:\n\
09482 \n\
09483 @example\n\
09484 @group\n\
09485 addproperty (\"my_property\", gcf, \"string\", \"a string value\");\n\
09486 addproperty (\"my_radio\", gcf, \"radio\", \"val_1|val_2|@{val_3@}\");\n\
09487 addproperty (\"my_style\", gcf, \"linelinestyle\", \"--\");\n\
09488 @end group\n\
09489 @end example\n\
09490 \n\
09491 @end deftypefn")
09492 {
09493   gh_manager::auto_lock guard;
09494 
09495   octave_value retval;
09496 
09497   if (args.length () >= 3)
09498     {
09499       std::string name = args(0).string_value ();
09500 
09501       if (! error_state)
09502         {
09503           double h = args(1).double_value ();
09504 
09505           if (! error_state)
09506             {
09507               graphics_handle gh = gh_manager::lookup (h);
09508 
09509               if (gh.ok ())
09510                 {
09511                   graphics_object go = gh_manager::get_object (gh);
09512 
09513                   std::string type = args(2).string_value ();
09514 
09515                   if (! error_state)
09516                     {
09517                       if (! go.get_properties ().has_property (name))
09518                         {
09519                           property p = property::create (name, gh, type,
09520                                                          args.splice (0, 3));
09521 
09522                           if (! error_state)
09523                             go.get_properties ().insert_property (name, p);
09524                         }
09525                       else
09526                         error ("addproperty: a '%s' property already exists in the graphics object",
09527                                name.c_str ());
09528                     }
09529                   else
09530                     error ("addproperty: invalid property TYPE, expected a string value");
09531                 }
09532               else
09533                 error ("addproperty: invalid graphics object (= %g)", h);
09534             }
09535           else
09536             error ("addproperty: invalid handle value");
09537         }
09538       else
09539         error ("addproperty: invalid property NAME, expected a string value");
09540     }
09541   else
09542     print_usage ();
09543 
09544   return retval;
09545 }
09546 
09547 octave_value
09548 get_property_from_handle (double handle, const std::string& property,
09549                           const std::string& func)
09550 {
09551   gh_manager::auto_lock guard;
09552 
09553   graphics_object obj = gh_manager::get_object (handle);
09554   octave_value retval;
09555 
09556   if (obj)
09557     retval = obj.get (caseless_str (property));
09558   else
09559     error ("%s: invalid handle (= %g)", func.c_str(), handle);
09560 
09561   return retval;
09562 }
09563 
09564 bool
09565 set_property_in_handle (double handle, const std::string& property,
09566                         const octave_value& arg, const std::string& func)
09567 {
09568   gh_manager::auto_lock guard;
09569 
09570   graphics_object obj = gh_manager::get_object (handle);
09571   int ret = false;
09572 
09573   if (obj)
09574     {
09575       obj.set (caseless_str (property), arg);
09576 
09577       if (! error_state)
09578         ret = true;
09579     }
09580   else
09581     error ("%s: invalid handle (= %g)", func.c_str(), handle);
09582 
09583   return ret;
09584 }
09585 
09586 static bool
09587 compare_property_values (const octave_value& o1, const octave_value& o2)
09588 {
09589   octave_value_list args (2);
09590 
09591   args(0) = o1;
09592   args(1) = o2;
09593 
09594   octave_value_list result = feval ("isequal", args, 1);
09595 
09596   if (! error_state && result.length () > 0)
09597     return result(0).bool_value ();
09598 
09599   return false;
09600 }
09601 
09602 static std::map<uint32_t, bool> waitfor_results;
09603 
09604 static void
09605 cleanup_waitfor_id (uint32_t id)
09606 {
09607   waitfor_results.erase (id);
09608 }
09609 
09610 static void
09611 do_cleanup_waitfor_listener (const octave_value& listener,
09612                              listener_mode mode = POSTSET)
09613 {
09614   Cell c = listener.cell_value ();
09615 
09616   if (c.numel () >= 4)
09617     {
09618       double h = c(2).double_value ();
09619 
09620       if (! error_state)
09621         {
09622           caseless_str pname = c(3).string_value ();
09623 
09624           if (! error_state)
09625             {
09626               gh_manager::auto_lock guard;
09627 
09628               graphics_handle handle = gh_manager::lookup (h);
09629 
09630               if (handle.ok ())
09631                 {
09632                   graphics_object go = gh_manager::get_object (handle);
09633 
09634                   if (go.get_properties ().has_property (pname))
09635                     {
09636                       go.get_properties ()
09637                         .delete_listener (pname, listener, mode);
09638                       if (mode == POSTSET)
09639                         go.get_properties ()
09640                           .delete_listener (pname, listener, PERSISTENT);
09641                     }
09642                 }
09643             }
09644         }
09645     }
09646 }
09647 
09648 static void
09649 cleanup_waitfor_postset_listener(const octave_value& listener)
09650 { do_cleanup_waitfor_listener (listener, POSTSET); }
09651 
09652 static void
09653 cleanup_waitfor_predelete_listener(const octave_value& listener)
09654 { do_cleanup_waitfor_listener (listener, PREDELETE); }
09655 
09656 static octave_value_list
09657 waitfor_listener (const octave_value_list& args, int)
09658 {
09659   if (args.length () > 3)
09660     {
09661       uint32_t id = args(2).uint32_scalar_value ().value ();
09662 
09663       if (! error_state)
09664         {
09665           if (args.length () > 5)
09666             {
09667               double h = args(0).double_value ();
09668 
09669               if (! error_state)
09670                 {
09671                   caseless_str pname = args(4).string_value ();
09672 
09673                   if (! error_state)
09674                     {
09675                       gh_manager::auto_lock guard;
09676 
09677                       graphics_handle handle = gh_manager::lookup (h);
09678 
09679                       if (handle.ok ())
09680                         {
09681                           graphics_object go = gh_manager::get_object (handle);
09682                           octave_value pvalue = go.get (pname);
09683 
09684                           if (compare_property_values (pvalue, args(5)))
09685                             waitfor_results[id] = true;
09686                         }
09687                     }
09688                 }
09689             }
09690           else
09691             waitfor_results[id] = true;
09692         }
09693     }
09694 
09695   return octave_value_list ();
09696 }
09697 
09698 static octave_value_list
09699 waitfor_del_listener (const octave_value_list& args, int)
09700 {
09701   if (args.length () > 2)
09702     {
09703       uint32_t id = args(2).uint32_scalar_value ().value ();
09704 
09705       if (! error_state)
09706         waitfor_results[id] = true;
09707     }
09708 
09709   return octave_value_list ();
09710 }
09711 
09712 DEFUN (waitfor, args, ,
09713   "-*- texinfo -*-\n\
09714 @deftypefn  {Built-in Function} {} waitfor (@var{h})\n\
09715 @deftypefnx {Built-in Function} {} waitfor (@var{h}, @var{prop})\n\
09716 @deftypefnx {Built-in Function} {} waitfor (@var{h}, @var{prop}, @var{value})\n\
09717 @deftypefnx {Built-in Function} {} waitfor (@dots{}, \"timeout\", @var{timeout})\n\
09718 Suspend the execution of the current program until a condition is\n\
09719 satisfied on the graphics handle @var{h}.  While the program is suspended\n\
09720 graphics events are still being processed normally, allowing callbacks to\n\
09721 modify the state of graphics objects.  This function is reentrant and can be\n\
09722 called from a callback, while another @code{waitfor} call is pending at\n\
09723 top-level.\n\
09724 \n\
09725 In the first form, program execution is suspended until the graphics object\n\
09726 @var{h} is destroyed.  If the graphics handle is invalid, the function\n\
09727 returns immediately.\n\
09728 \n\
09729 In the second form, execution is suspended until the graphics object is\n\
09730 destroyed or the property named @var{prop} is modified.  If the graphics\n\
09731 handle is invalid or the property does not exist, the function returns\n\
09732 immediately.\n\
09733 \n\
09734 In the third form, execution is suspended until the graphics object is\n\
09735 destroyed or the property named @var{prop} is set to @var{value}.  The\n\
09736 function @code{isequal} is used to compare property values.  If the graphics\n\
09737 handle is invalid, the property does not exist or the property is already\n\
09738 set to @var{value}, the function returns immediately.\n\
09739 \n\
09740 An optional timeout can be specified using the property @code{timeout}.\n\
09741 This timeout value is the number of seconds to wait for the condition to be\n\
09742 true.  @var{timeout} must be at least 1. If a smaller value is specified, a\n\
09743 warning is issued and a value of 1 is used instead.  If the timeout value is\n\
09744 not an integer, it is truncated towards 0.\n\
09745 \n\
09746 To define a condition on a property named @code{timeout}, use the string\n\
09747 @code{\\timeout} instead.\n\
09748 \n\
09749 In all cases, typing CTRL-C stops program execution immediately.\n\
09750 @seealso{isequal}\n\
09751 @end deftypefn")
09752 {
09753   if (args.length () > 0)
09754     {
09755       double h = args(0).double_value ();
09756 
09757       if (! error_state)
09758         {
09759           caseless_str pname;
09760 
09761           unwind_protect frame;
09762 
09763           static uint32_t id_counter = 0;
09764           uint32_t id = 0;
09765 
09766           int max_arg_index = 0;
09767           int timeout_index = -1;
09768 
09769           int timeout = 0;
09770 
09771           if (args.length () > 1)
09772             {
09773               pname = args(1).string_value ();
09774               if (! error_state
09775                   && ! pname.empty ()
09776                   && ! pname.compare ("timeout"))
09777                 {
09778                   if (pname.compare ("\\timeout"))
09779                     pname = "timeout";
09780 
09781                   static octave_value wf_listener;
09782 
09783                   if (! wf_listener.is_defined ())
09784                     wf_listener =
09785                       octave_value (new octave_builtin (waitfor_listener,
09786                                                         "waitfor_listener"));
09787 
09788                   max_arg_index++;
09789                   if (args.length () > 2)
09790                     {
09791                       if (args(2).is_string ())
09792                         {
09793                           caseless_str s = args(2).string_value ();
09794 
09795                           if (! error_state)
09796                             {
09797                               if (s.compare ("timeout"))
09798                                 timeout_index = 2;
09799                               else
09800                                 max_arg_index++;
09801                             }
09802                         }
09803                       else
09804                         max_arg_index++;
09805                     }
09806 
09807                   Cell listener (1, max_arg_index >= 2 ? 5 : 4);
09808 
09809                   id = id_counter++;
09810                   frame.add_fcn (cleanup_waitfor_id, id);
09811                   waitfor_results[id] = false;
09812 
09813                   listener(0) = wf_listener;
09814                   listener(1) = octave_uint32 (id);
09815                   listener(2) = h;
09816                   listener(3) = pname;
09817 
09818                   if (max_arg_index >= 2)
09819                     listener(4) = args(2);
09820 
09821                   octave_value ov_listener (listener);
09822 
09823                   gh_manager::auto_lock guard;
09824 
09825                   graphics_handle handle = gh_manager::lookup (h);
09826 
09827                   if (handle.ok ())
09828                     {
09829                       graphics_object go = gh_manager::get_object (handle);
09830 
09831                       if (max_arg_index >= 2
09832                           && compare_property_values (go.get (pname),
09833                                                       args(2)))
09834                         waitfor_results[id] = true;
09835                       else
09836                         {
09837 
09838                           frame.add_fcn (cleanup_waitfor_postset_listener,
09839                                          ov_listener);
09840                           go.add_property_listener (pname, ov_listener,
09841                                                     POSTSET);
09842                           go.add_property_listener (pname, ov_listener,
09843                                                     PERSISTENT);
09844 
09845                           if (go.get_properties ()
09846                               .has_dynamic_property (pname))
09847                             {
09848                               static octave_value wf_del_listener;
09849 
09850                               if (! wf_del_listener.is_defined ())
09851                                 wf_del_listener =
09852                                   octave_value (new octave_builtin
09853                                                 (waitfor_del_listener,
09854                                                  "waitfor_del_listener"));
09855 
09856                               Cell del_listener (1, 4);
09857 
09858                               del_listener(0) = wf_del_listener;
09859                               del_listener(1) = octave_uint32 (id);
09860                               del_listener(2) = h;
09861                               del_listener(3) = pname;
09862 
09863                               octave_value ov_del_listener (del_listener);
09864 
09865                               frame.add_fcn (cleanup_waitfor_predelete_listener,
09866                                              ov_del_listener);
09867                               go.add_property_listener (pname, ov_del_listener,
09868                                                         PREDELETE);
09869                             }
09870                         }
09871                     }
09872                 }
09873               else if (error_state || pname.empty ())
09874                 error ("waitfor: invalid property name, expected a non-empty string value");
09875             }
09876 
09877           if (! error_state
09878               && timeout_index < 0
09879               && args.length () > (max_arg_index + 1))
09880             {
09881               caseless_str s = args(max_arg_index + 1).string_value ();
09882 
09883               if (! error_state)
09884                 {
09885                   if (s.compare ("timeout"))
09886                     timeout_index = max_arg_index + 1;
09887                   else
09888                     error ("waitfor: invalid parameter '%s'", s.c_str ());
09889                 }
09890               else
09891                 error ("waitfor: invalid parameter, expected 'timeout'");
09892             }
09893 
09894           if (! error_state && timeout_index >= 0)
09895             {
09896               if (args.length () > (timeout_index + 1))
09897                 {
09898                   timeout = static_cast<int>
09899                     (args(timeout_index + 1).scalar_value ());
09900 
09901                   if (! error_state)
09902                     {
09903                       if (timeout < 1)
09904                         {
09905                           warning ("waitfor: the timeout value must be >= 1, using 1 instead");
09906                           timeout = 1;
09907                         }
09908                     }
09909                   else
09910                     error ("waitfor: invalid timeout value, expected a value >= 1");
09911                 }
09912               else
09913                 error ("waitfor: missing timeout value");
09914             }
09915 
09916           // FIXME: There is still a "hole" in the following loop. The code
09917           //        assumes that an object handle is unique, which is a fair
09918           //        assumptions, except for figures. If a figure is destroyed
09919           //        then recreated with the same figure ID, within the same
09920           //        run of event hooks, then the figure destruction won't be
09921           //        caught and the loop will not stop. This is an unlikely
09922           //        possibility in practice, though.
09923           //
09924           //        Using deletefcn callback is also unreliable as it could be
09925           //        modified during a callback execution and the waitfor loop
09926           //        would not stop.
09927           //
09928           //        The only "good" implementation would require object
09929           //        listeners, similar to property listeners.
09930 
09931           time_t start = 0;
09932 
09933           if (timeout > 0)
09934             start = time (0);
09935 
09936           while (! error_state)
09937             {
09938               if (true)
09939                 {
09940                   gh_manager::auto_lock guard;
09941 
09942                   graphics_handle handle = gh_manager::lookup (h);
09943 
09944                   if (handle.ok ())
09945                     {
09946                       if (! pname.empty () && waitfor_results[id])
09947                         break;
09948                     }
09949                   else
09950                     break;
09951                 }
09952 
09953               octave_usleep (100000);
09954 
09955               OCTAVE_QUIT;
09956 
09957               command_editor::run_event_hooks ();
09958 
09959               if (timeout > 0)
09960                 {
09961                   if (start + timeout < time (0))
09962                     break;
09963                 }
09964             }
09965         }
09966       else
09967         error ("waitfor: invalid handle value.");
09968     }
09969   else
09970     print_usage ();
09971 
09972   return octave_value ();
09973 }
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Defines