GNU Octave  3.8.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
graphics.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 2007-2013 John W. Eaton
4 
5 This file is part of Octave.
6 
7 Octave is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 3 of the License, or (at your
10 option) any later version.
11 
12 Octave is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Octave; see the file COPYING. If not, see
19 <http://www.gnu.org/licenses/>.
20 
21 */
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 #include <cctype>
28 #include <cfloat>
29 #include <cstdlib>
30 #include <ctime>
31 
32 #include <algorithm>
33 #include <list>
34 #include <map>
35 #include <set>
36 #include <string>
37 #include <sstream>
38 
39 #include "cmd-edit.h"
40 #include "file-ops.h"
41 #include "file-stat.h"
42 #include "oct-locbuf.h"
43 #include "singleton-cleanup.h"
44 
45 #include "builtins.h"
46 #include "cutils.h"
47 #include "defun.h"
48 #include "display.h"
49 #include "error.h"
50 #include "graphics.h"
51 #include "input.h"
52 #include "ov.h"
53 #include "oct-obj.h"
54 #include "oct-map.h"
55 #include "ov-fcn-handle.h"
56 #include "pager.h"
57 #include "parse.h"
58 #include "toplev.h"
59 #include "txt-eng-ft.h"
60 #include "unwind-prot.h"
61 
62 // forward declarations
63 static octave_value xget (const graphics_handle& h, const caseless_str& name);
64 
65 static void
66 gripe_set_invalid (const std::string& pname)
67 {
68  error ("set: invalid value for %s property", pname.c_str ());
69 }
70 
71 // Check to see that PNAME matches just one of PNAMES uniquely.
72 // Return the full name of the match, or an empty caseless_str object
73 // if there is no match, or the match is ambiguous.
74 
75 static caseless_str
76 validate_property_name (const std::string& who, const std::string& what,
77  const std::set<std::string>& pnames,
78  const caseless_str& pname)
79 {
80  size_t len = pname.length ();
81  std::set<std::string> matches;
82 
83  for (std::set<std::string>::const_iterator p = pnames.begin ();
84  p != pnames.end (); p++)
85  {
86  if (pname.compare (*p, len))
87  {
88  if (len == p->length ())
89  {
90  // Exact match.
91  return pname;
92  }
93 
94  matches.insert (*p);
95  }
96  }
97 
98  size_t num_matches = matches.size ();
99 
100  if (num_matches == 0)
101  {
102  error ("%s: unknown %s property %s",
103  who.c_str (), what.c_str (), pname.c_str ());
104  }
105  else if (num_matches > 1)
106  {
107  string_vector sv (matches);
108 
109  std::ostringstream os;
110 
111  sv.list_in_columns (os);
112 
113  std::string match_list = os.str ();
114 
115  error ("%s: ambiguous %s property name %s; possible matches:\n\n%s",
116  who.c_str (), what.c_str (), pname.c_str (), match_list.c_str ());
117  }
118  else if (num_matches == 1)
119  {
120  // Exact match was handled above.
121 
122  std::string possible_match = *(matches.begin ());
123 
124  warning_with_id ("Octave:abbreviated-property-match",
125  "%s: allowing %s to match %s property %s",
126  who.c_str (), pname.c_str (), what.c_str (),
127  possible_match.c_str ());
128 
129  return possible_match;
130  }
131 
132  return caseless_str ();
133 }
134 
135 static Matrix
137 {
138  Matrix cmap (64, 3, 0.0);
139 
140  // Produce X in the same manner as linspace so that
141  // jet_colormap and jet.m produce *exactly* the same result.
142  double delta = 1.0 / 63.0;
143 
144  for (octave_idx_type i = 0; i < 64; i++)
145  {
146  // This is the jet colormap. It would be nice to be able
147  // to feval the jet function but since there is a static
148  // property object that includes a colormap_property
149  // object, we need to initialize this before main is even
150  // called, so calling an interpreted function is not
151  // possible.
152 
153  double x = i*delta;
154 
155  if (x >= 3.0/8.0 && x < 5.0/8.0)
156  cmap(i,0) = 4.0 * x - 3.0/2.0;
157  else if (x >= 5.0/8.0 && x < 7.0/8.0)
158  cmap(i,0) = 1.0;
159  else if (x >= 7.0/8.0)
160  cmap(i,0) = -4.0 * x + 9.0/2.0;
161 
162  if (x >= 1.0/8.0 && x < 3.0/8.0)
163  cmap(i,1) = 4.0 * x - 1.0/2.0;
164  else if (x >= 3.0/8.0 && x < 5.0/8.0)
165  cmap(i,1) = 1.0;
166  else if (x >= 5.0/8.0 && x < 7.0/8.0)
167  cmap(i,1) = -4.0 * x + 7.0/2.0;
168 
169  if (x < 1.0/8.0)
170  cmap(i,2) = 4.0 * x + 1.0/2.0;
171  else if (x >= 1.0/8.0 && x < 3.0/8.0)
172  cmap(i,2) = 1.0;
173  else if (x >= 3.0/8.0 && x < 5.0/8.0)
174  cmap(i,2) = -4.0 * x + 5.0/2.0;
175  }
176 
177  return cmap;
178 }
179 
180 static double
182 {
183  return display_info::depth ();
184 }
185 
186 static Matrix
188 {
189  Matrix retval (1, 4, 1.0);
190 
191  retval(2) = display_info::width ();
192  retval(3) = display_info::height ();
193 
194  return retval;
195 }
196 
197 static double
199 {
200  return (display_info::x_dpi () + display_info::y_dpi ()) / 2;
201 }
202 
203 static Matrix
205 {
206  Matrix retval (7, 3, 0.0);
207 
208  retval(0,2) = 1.0;
209 
210  retval(1,1) = 0.5;
211 
212  retval(2,0) = 1.0;
213 
214  retval(3,1) = 0.75;
215  retval(3,2) = 0.75;
216 
217  retval(4,0) = 0.75;
218  retval(4,2) = 0.75;
219 
220  retval(5,0) = 0.75;
221  retval(5,1) = 0.75;
222 
223  retval(6,0) = 0.25;
224  retval(6,1) = 0.25;
225  retval(6,2) = 0.25;
226 
227  return retval;
228 }
229 
230 static Matrix
231 default_lim (bool logscale = false)
232 {
233  Matrix m (1, 2, 0);
234 
235  if (logscale)
236  {
237  m(0) = 0.1;
238  m(1) = 1.0;
239  }
240  else
241  m(1) = 1;
242 
243  return m;
244 }
245 
246 static Matrix
248 {
249  Matrix retval (1, 2);
250 
251  retval(0) = 0;
252  retval(1) = 1;
253 
254  return retval;
255 }
256 
257 static Matrix
259 {
260  Matrix m (1, 4, 0.0);
261  m(0) = 0.13;
262  m(1) = 0.11;
263  m(2) = 0.775;
264  m(3) = 0.815;
265  return m;
266 }
267 
268 static Matrix
270 {
271  Matrix m (1, 4, 0.0);
272  m(2) = m(3) = 1.0;
273  return m;
274 }
275 
276 static Matrix
278 {
279  Matrix m (1, 6, 0.0);
280  m(0) = 0.0;
281  m(1) = 0.2;
282  m(2) = 0.4;
283  m(3) = 0.6;
284  m(4) = 0.8;
285  m(5) = 1.0;
286  return m;
287 }
288 
289 static Matrix
291 {
292  Matrix m (1, 2, 0.0);
293  m(0) = 0.01;
294  m(1) = 0.025;
295  return m;
296 }
297 
298 static Matrix
300 {
301  Matrix m (1, 4, 0.0);
302  m(0) = 300;
303  m(1) = 200;
304  m(2) = 560;
305  m(3) = 420;
306  return m;
307 }
308 
309 static Matrix
311 {
312  Matrix m (1, 2, 0.0);
313  m(0) = 8.5;
314  m(1) = 11.0;
315  return m;
316 }
317 
318 static Matrix
320 {
321  Matrix m (1, 4, 0.0);
322  m(0) = 0.25;
323  m(1) = 2.50;
324  m(2) = 8.00;
325  m(3) = 6.00;
326  return m;
327 }
328 
329 static Matrix
331 {
332  Matrix retval (1, 4, 0.0);
333 
334  retval(0) = 0;
335  retval(1) = 0;
336  retval(2) = 80;
337  retval(3) = 30;
338 
339  return retval;
340 }
341 
342 static Matrix
344 {
345  Matrix retval (1, 2, 0.0);
346 
347  retval(0) = 0.01;
348  retval(1) = 0.1;
349 
350  return retval;
351 }
352 
353 static Matrix
355 {
356  Matrix retval (1, 4, 0.0);
357 
358  retval(0) = 0;
359  retval(1) = 0;
360  retval(2) = 0.5;
361  retval(3) = 0.5;
362 
363  return retval;
364 }
365 
366 static double
367 convert_font_size (double font_size, const caseless_str& from_units,
368  const caseless_str& to_units, double parent_height = 0)
369 {
370  // Simple case where from_units == to_units
371 
372  if (from_units.compare (to_units))
373  return font_size;
374 
375  // Converts the given fontsize using the following transformation:
376  // <old_font_size> => points => <new_font_size>
377 
378  double points_size = 0;
379  double res = 0;
380 
381  if (from_units.compare ("points"))
382  points_size = font_size;
383  else
384  {
385  res = xget (0, "screenpixelsperinch").double_value ();
386 
387  if (from_units.compare ("pixels"))
388  points_size = font_size * 72.0 / res;
389  else if (from_units.compare ("inches"))
390  points_size = font_size * 72.0;
391  else if (from_units.compare ("centimeters"))
392  points_size = font_size * 72.0 / 2.54;
393  else if (from_units.compare ("normalized"))
394  points_size = font_size * parent_height * 72.0 / res;
395  }
396 
397  double new_font_size = 0;
398 
399  if (to_units.compare ("points"))
400  new_font_size = points_size;
401  else
402  {
403  if (res <= 0)
404  res = xget (0, "screenpixelsperinch").double_value ();
405 
406  if (to_units.compare ("pixels"))
407  new_font_size = points_size * res / 72.0;
408  else if (to_units.compare ("inches"))
409  new_font_size = points_size / 72.0;
410  else if (to_units.compare ("centimeters"))
411  new_font_size = points_size * 2.54 / 72.0;
412  else if (to_units.compare ("normalized"))
413  {
414  // Avoid setting font size to (0/0) = NaN
415 
416  if (parent_height > 0)
417  new_font_size = points_size * res / (parent_height * 72.0);
418  }
419  }
420 
421  return new_font_size;
422 }
423 
424 static Matrix
425 convert_position (const Matrix& pos, const caseless_str& from_units,
426  const caseless_str& to_units, const Matrix& parent_dim)
427 {
428  Matrix retval (1, pos.numel ());
429  double res = 0;
430  bool is_rectangle = (pos.numel () == 4);
431  bool is_2d = (pos.numel () == 2);
432 
433  if (from_units.compare ("pixels"))
434  retval = pos;
435  else if (from_units.compare ("normalized"))
436  {
437  retval(0) = pos(0) * parent_dim(0) + 1;
438  retval(1) = pos(1) * parent_dim(1) + 1;
439  if (is_rectangle)
440  {
441  retval(2) = pos(2) * parent_dim(0);
442  retval(3) = pos(3) * parent_dim(1);
443  }
444  else if (! is_2d)
445  retval(2) = 0;
446  }
447  else if (from_units.compare ("characters"))
448  {
449  if (res <= 0)
450  res = xget (0, "screenpixelsperinch").double_value ();
451 
452  double f = 0.0;
453 
454  // FIXME: this assumes the system font is Helvetica 10pt
455  // (for which "x" requires 6x12 pixels at 74.951 pixels/inch)
456  f = 12.0 * res / 74.951;
457 
458  if (f > 0)
459  {
460  retval(0) = 0.5 * pos(0) * f;
461  retval(1) = pos(1) * f;
462  if (is_rectangle)
463  {
464  retval(2) = 0.5 * pos(2) * f;
465  retval(3) = pos(3) * f;
466  }
467  else if (! is_2d)
468  retval(2) = 0;
469  }
470  }
471  else
472  {
473  if (res <= 0)
474  res = xget (0, "screenpixelsperinch").double_value ();
475 
476  double f = 0.0;
477 
478  if (from_units.compare ("points"))
479  f = res / 72.0;
480  else if (from_units.compare ("inches"))
481  f = res;
482  else if (from_units.compare ("centimeters"))
483  f = res / 2.54;
484 
485  if (f > 0)
486  {
487  retval(0) = pos(0) * f + 1;
488  retval(1) = pos(1) * f + 1;
489  if (is_rectangle)
490  {
491  retval(2) = pos(2) * f;
492  retval(3) = pos(3) * f;
493  }
494  else if (! is_2d)
495  retval(2) = 0;
496  }
497  }
498 
499  if (! to_units.compare ("pixels"))
500  {
501  if (to_units.compare ("normalized"))
502  {
503  retval(0) = (retval(0) - 1) / parent_dim(0);
504  retval(1) = (retval(1) - 1) / parent_dim(1);
505  if (is_rectangle)
506  {
507  retval(2) /= parent_dim(0);
508  retval(3) /= parent_dim(1);
509  }
510  else if (! is_2d)
511  retval(2) = 0;
512  }
513  else if (to_units.compare ("characters"))
514  {
515  if (res <= 0)
516  res = xget (0, "screenpixelsperinch").double_value ();
517 
518  double f = 0.0;
519 
520  f = 12.0 * res / 74.951;
521 
522  if (f > 0)
523  {
524  retval(0) = 2 * retval(0) / f;
525  retval(1) = retval(1) / f;
526  if (is_rectangle)
527  {
528  retval(2) = 2 * retval(2) / f;
529  retval(3) = retval(3) / f;
530  }
531  else if (! is_2d)
532  retval(2) = 0;
533  }
534  }
535  else
536  {
537  if (res <= 0)
538  res = xget (0, "screenpixelsperinch").double_value ();
539 
540  double f = 0.0;
541 
542  if (to_units.compare ("points"))
543  f = res / 72.0;
544  else if (to_units.compare ("inches"))
545  f = res;
546  else if (to_units.compare ("centimeters"))
547  f = res / 2.54;
548 
549  if (f > 0)
550  {
551  retval(0) = (retval(0) - 1) / f;
552  retval(1) = (retval(1) - 1) / f;
553  if (is_rectangle)
554  {
555  retval(2) /= f;
556  retval(3) /= f;
557  }
558  else if (! is_2d)
559  retval(2) = 0;
560  }
561  }
562  }
563  else if (! is_rectangle && ! is_2d)
564  retval(2) = 0;
565 
566  return retval;
567 }
568 
569 static Matrix
571  const caseless_str& from_units,
572  const caseless_str& to_units)
573 {
575  graphics_object ax = go.get_ancestor ("axes");
576 
577  Matrix retval;
578 
579  if (ax.valid_object ())
580  {
581  const axes::properties& ax_props =
582  dynamic_cast<const axes::properties&> (ax.get_properties ());
583  graphics_xform ax_xform = ax_props.get_transform ();
584  bool is_rectangle = (pos.numel () == 4);
585  Matrix ax_bbox = ax_props.get_boundingbox (true),
586  ax_size = ax_bbox.extract_n (0, 2, 1, 2);
587 
588  if (from_units.compare ("data"))
589  {
590  if (is_rectangle)
591  {
592  ColumnVector v1 = ax_xform.transform (pos(0), pos(1), 0),
593  v2 = ax_xform.transform (pos(0) + pos(2),
594  pos(1) + pos(3), 0);
595 
596  retval.resize (1, 4);
597 
598  retval(0) = v1(0) - ax_bbox(0) + 1;
599  retval(1) = ax_bbox(1) + ax_bbox(3) - v1(1) + 1;
600  retval(2) = v2(0) - v1(0);
601  retval(3) = v1(1) - v2(1);
602  }
603  else
604  {
605  ColumnVector v = ax_xform.transform (pos(0), pos(1), pos(2));
606 
607  retval.resize (1, 3);
608 
609  retval(0) = v(0) - ax_bbox(0) + 1;
610  retval(1) = ax_bbox(1) + ax_bbox(3) - v(1) + 1;
611  retval(2) = 0;
612  }
613  }
614  else
615  retval = convert_position (pos, from_units, "pixels", ax_size);
616 
617  if (! to_units.compare ("pixels"))
618  {
619  if (to_units.compare ("data"))
620  {
621  if (is_rectangle)
622  {
623  ColumnVector v1, v2;
624  v1 = ax_xform.untransform (
625  retval(0) + ax_bbox(0) - 1,
626  ax_bbox(1) + ax_bbox(3) - retval(1) + 1);
627  v2 = ax_xform.untransform (
628  retval(0) + retval(2) + ax_bbox(0) - 1,
629  ax_bbox(1) + ax_bbox(3) - (retval(1) + retval(3)) + 1);
630 
631  retval.resize (1, 4);
632 
633  retval(0) = v1(0);
634  retval(1) = v1(1);
635  retval(2) = v2(0) - v1(0);
636  retval(3) = v2(1) - v1(1);
637  }
638  else
639  {
640  ColumnVector v;
641  v = ax_xform.untransform (
642  retval(0) + ax_bbox(0) - 1,
643  ax_bbox(1) + ax_bbox(3) - retval(1) + 1);
644 
645  retval.resize (1, 3);
646 
647  retval(0) = v(0);
648  retval(1) = v(1);
649  retval(2) = v(2);
650  }
651  }
652  else
653  retval = convert_position (retval, "pixels", to_units, ax_size);
654  }
655  }
656 
657  return retval;
658 }
659 
660 // This function always returns the screensize in pixels
661 static Matrix
663 {
665  Matrix sz = obj.get ("screensize").matrix_value ();
666  return convert_position (sz, obj.get ("units").string_value (), "pixels",
667  sz.extract_n (0, 2, 1, 2)).extract_n (0, 2, 1, 2);
668 }
669 
670 static void
671 convert_cdata_2 (bool is_scaled, double clim_0, double clim_1,
672  const double *cmapv, double x, octave_idx_type lda,
673  octave_idx_type nc, octave_idx_type i, double *av)
674 {
675  if (is_scaled)
676  x = xround ((nc - 1) * (x - clim_0) / (clim_1 - clim_0));
677  else
678  x = xround (x - 1);
679 
680  if (xisnan (x))
681  {
682  av[i] = x;
683  av[i+lda] = x;
684  av[i+2*lda] = x;
685  }
686  else
687  {
688  if (x < 0)
689  x = 0;
690  else if (x >= nc)
691  x = (nc - 1);
692 
693  octave_idx_type idx = static_cast<octave_idx_type> (x);
694 
695  av[i] = cmapv[idx];
696  av[i+lda] = cmapv[idx+nc];
697  av[i+2*lda] = cmapv[idx+2*nc];
698  }
699 }
700 
701 template <class T>
702 void
703 convert_cdata_1 (bool is_scaled, double clim_0, double clim_1,
704  const double *cmapv, const T *cv, octave_idx_type lda,
705  octave_idx_type nc, double *av)
706 {
707  for (octave_idx_type i = 0; i < lda; i++)
708  convert_cdata_2 (is_scaled, clim_0, clim_1, cmapv, cv[i], lda, nc, i, av);
709 }
710 
711 static octave_value
712 convert_cdata (const base_properties& props, const octave_value& cdata,
713  bool is_scaled, int cdim)
714 {
715  dim_vector dv (cdata.dims ());
716 
717  if (dv.length () == cdim && dv(cdim-1) == 3)
718  return cdata;
719 
720  Matrix cmap (1, 3, 0.0);
721  Matrix clim (1, 2, 0.0);
722 
724  graphics_object fig = go.get_ancestor ("figure");
725 
726  if (fig.valid_object ())
727  {
728  Matrix _cmap = fig.get (caseless_str ("colormap")).matrix_value ();
729 
730  if (! error_state)
731  cmap = _cmap;
732  }
733 
734  if (is_scaled)
735  {
736  graphics_object ax = go.get_ancestor ("axes");
737 
738  if (ax.valid_object ())
739  {
740  Matrix _clim = ax.get (caseless_str ("clim")).matrix_value ();
741 
742  if (! error_state)
743  clim = _clim;
744  }
745  }
746 
747  dv.resize (cdim);
748  dv(cdim-1) = 3;
749 
750  NDArray a (dv);
751 
752  octave_idx_type lda = a.numel () / static_cast<octave_idx_type> (3);
753  octave_idx_type nc = cmap.rows ();
754 
755  double *av = a.fortran_vec ();
756  const double *cmapv = cmap.data ();
757 
758  double clim_0 = clim(0);
759  double clim_1 = clim(1);
760 
761 #define CONVERT_CDATA_1(ARRAY_T, VAL_FN) \
762  do \
763  { \
764  ARRAY_T tmp = cdata. VAL_FN ## array_value (); \
765  \
766  convert_cdata_1 (is_scaled, clim_0, clim_1, cmapv, \
767  tmp.data (), lda, nc, av); \
768  } \
769  while (0)
770 
771  if (cdata.is_uint8_type ())
772  CONVERT_CDATA_1 (uint8NDArray, uint8_);
773  else if (cdata.is_single_type ())
774  CONVERT_CDATA_1 (FloatNDArray, float_);
775  else if (cdata.is_double_type ())
777  else
778  error ("unsupported type for cdata (= %s)", cdata.type_name ().c_str ());
779 
780 #undef CONVERT_CDATA_1
781 
782  return octave_value (a);
783 }
784 
785 template<class T>
786 static void
787 get_array_limits (const Array<T>& m, double& emin, double& emax,
788  double& eminp, double& emaxp)
789 {
790  const T *data = m.data ();
791  octave_idx_type n = m.numel ();
792 
793  for (octave_idx_type i = 0; i < n; i++)
794  {
795  double e = double (data[i]);
796 
797  // Don't need to test for NaN here as NaN>x and NaN<x is always false
798  if (! xisinf (e))
799  {
800  if (e < emin)
801  emin = e;
802 
803  if (e > emax)
804  emax = e;
805 
806  if (e > 0 && e < eminp)
807  eminp = e;
808 
809  if (e < 0 && e > emaxp)
810  emaxp = e;
811  }
812  }
813 }
814 
815 static bool
817  caseless_str& rest)
818 {
819  int len = name.length ();
820  int offset = 0;
821  bool result = false;
822 
823  if (len >= 4)
824  {
825  caseless_str pfx = name.substr (0, 4);
826 
827  if (pfx.compare ("axes") || pfx.compare ("line")
828  || pfx.compare ("text"))
829  offset = 4;
830  else if (len >= 5)
831  {
832  pfx = name.substr (0, 5);
833 
834  if (pfx.compare ("image") || pfx.compare ("patch"))
835  offset = 5;
836  else if (len >= 6)
837  {
838  pfx = name.substr (0, 6);
839 
840  if (pfx.compare ("figure") || pfx.compare ("uimenu"))
841  offset = 6;
842  else if (len >= 7)
843  {
844  pfx = name.substr (0, 7);
845 
846  if (pfx.compare ("surface") || pfx.compare ("hggroup")
847  || pfx.compare ("uipanel"))
848  offset = 7;
849  else if (len >= 9)
850  {
851  pfx = name.substr (0, 9);
852 
853  if (pfx.compare ("uicontrol")
854  || pfx.compare ("uitoolbar"))
855  offset = 9;
856  else if (len >= 10)
857  {
858  pfx = name.substr (0, 10);
859 
860  if (pfx.compare ("uipushtool"))
861  offset = 10;
862  else if (len >= 12)
863  {
864  pfx = name.substr (0, 12);
865 
866  if (pfx.compare ("uitoggletool"))
867  offset = 12;
868  else if (len >= 13)
869  {
870  pfx = name.substr (0, 13);
871 
872  if (pfx.compare ("uicontextmenu"))
873  offset = 13;
874  }
875  }
876  }
877  }
878  }
879  }
880  }
881 
882  if (offset > 0)
883  {
884  go_name = pfx;
885  rest = name.substr (offset);
886  result = true;
887  }
888  }
889 
890  return result;
891 }
892 
893 static base_graphics_object*
895  const graphics_handle& h = graphics_handle (),
896  const graphics_handle& p = graphics_handle ())
897 {
898  base_graphics_object *go = 0;
899 
900  if (type.compare ("figure"))
901  go = new figure (h, p);
902  else if (type.compare ("axes"))
903  go = new axes (h, p);
904  else if (type.compare ("line"))
905  go = new line (h, p);
906  else if (type.compare ("text"))
907  go = new text (h, p);
908  else if (type.compare ("image"))
909  go = new image (h, p);
910  else if (type.compare ("patch"))
911  go = new patch (h, p);
912  else if (type.compare ("surface"))
913  go = new surface (h, p);
914  else if (type.compare ("hggroup"))
915  go = new hggroup (h, p);
916  else if (type.compare ("uimenu"))
917  go = new uimenu (h, p);
918  else if (type.compare ("uicontrol"))
919  go = new uicontrol (h, p);
920  else if (type.compare ("uipanel"))
921  go = new uipanel (h, p);
922  else if (type.compare ("uicontextmenu"))
923  go = new uicontextmenu (h, p);
924  else if (type.compare ("uitoolbar"))
925  go = new uitoolbar (h, p);
926  else if (type.compare ("uipushtool"))
927  go = new uipushtool (h, p);
928  else if (type.compare ("uitoggletool"))
929  go = new uitoggletool (h, p);
930  return go;
931 }
932 
933 // ---------------------------------------------------------------------
934 
935 bool
936 base_property::set (const octave_value& v, bool do_run, bool do_notify_toolkit)
937 {
938  if (do_set (v))
939  {
940 
941  // Notify graphics toolkit.
942  if (id >= 0 && do_notify_toolkit)
943  {
945  if (go)
946  go.update (id);
947  }
948 
949  // run listeners
950  if (do_run && ! error_state)
952 
953  return true;
954  }
955 
956  return false;
957 }
958 
959 
960 void
962 {
963  const octave_value_list& l = listeners[mode];
964 
965  for (int i = 0; i < l.length (); i++)
966  {
968 
969  if (error_state)
970  break;
971  }
972 }
973 
974 radio_values::radio_values (const std::string& opt_string)
975  : default_val (), possible_vals ()
976 {
977  size_t beg = 0;
978  size_t len = opt_string.length ();
979  bool done = len == 0;
980 
981  while (! done)
982  {
983  size_t end = opt_string.find ('|', beg);
984 
985  if (end == std::string::npos)
986  {
987  end = len;
988  done = true;
989  }
990 
991  std::string t = opt_string.substr (beg, end-beg);
992 
993  // Might want more error checking here...
994  if (t[0] == '{')
995  {
996  t = t.substr (1, t.length () - 2);
997  default_val = t;
998  }
999  else if (beg == 0) // ensure default value
1000  default_val = t;
1001 
1002  possible_vals.insert (t);
1003 
1004  beg = end + 1;
1005  }
1006 }
1007 
1008 std::string
1010 {
1011  std::string retval;
1012  for (std::set<caseless_str>::const_iterator it = possible_vals.begin ();
1013  it != possible_vals.end (); it++)
1014  {
1015  if (retval == "")
1016  {
1017  if (*it == default_value ())
1018  retval = "{" + *it + "}";
1019  else
1020  retval = *it;
1021  }
1022  else
1023  {
1024  if (*it == default_value ())
1025  retval += " | {" + *it + "}";
1026  else
1027  retval += " | " + *it;
1028  }
1029  }
1030  if (retval != "")
1031  retval = "[ " + retval + " ]";
1032  return retval;
1033 }
1034 
1035 Cell
1037 {
1038  octave_idx_type i = 0;
1039  Cell retval (nelem (), 1);
1040  for (std::set<caseless_str>::const_iterator it = possible_vals.begin ();
1041  it != possible_vals.end (); it++)
1042  retval(i++) = std::string (*it);
1043  return retval;
1044 }
1045 
1046 bool
1047 color_values::str2rgb (std::string str)
1048 {
1049  double tmp_rgb[3] = {0, 0, 0};
1050  bool retval = true;
1051  unsigned int len = str.length ();
1052 
1053  std::transform (str.begin (), str.end (), str.begin (), tolower);
1054 
1055  if (str.compare (0, len, "blue", 0, len) == 0)
1056  tmp_rgb[2] = 1;
1057  else if (str.compare (0, len, "black", 0, len) == 0
1058  || str.compare (0, len, "k", 0, len) == 0)
1059  tmp_rgb[0] = tmp_rgb[1] = tmp_rgb[2] = 0;
1060  else if (str.compare (0, len, "red", 0, len) == 0)
1061  tmp_rgb[0] = 1;
1062  else if (str.compare (0, len, "green", 0, len) == 0)
1063  tmp_rgb[1] = 1;
1064  else if (str.compare (0, len, "yellow", 0, len) == 0)
1065  tmp_rgb[0] = tmp_rgb[1] = 1;
1066  else if (str.compare (0, len, "magenta", 0, len) == 0)
1067  tmp_rgb[0] = tmp_rgb[2] = 1;
1068  else if (str.compare (0, len, "cyan", 0, len) == 0)
1069  tmp_rgb[1] = tmp_rgb[2] = 1;
1070  else if (str.compare (0, len, "white", 0, len) == 0
1071  || str.compare (0, len, "w", 0, len) == 0)
1072  tmp_rgb[0] = tmp_rgb[1] = tmp_rgb[2] = 1;
1073  else
1074  retval = false;
1075 
1076  if (retval)
1077  {
1078  for (int i = 0; i < 3; i++)
1079  xrgb(i) = tmp_rgb[i];
1080  }
1081 
1082  return retval;
1083 }
1084 
1085 bool
1087 {
1088  if (val.is_string ())
1089  {
1090  std::string s = val.string_value ();
1091 
1092  if (! s.empty ())
1093  {
1094  std::string match;
1095 
1096  if (radio_val.contains (s, match))
1097  {
1098  if (current_type != radio_t || match != current_val)
1099  {
1100  if (s.length () != match.length ())
1101  warning_with_id ("Octave:abbreviated-property-match",
1102  "%s: allowing %s to match %s value %s",
1103  "set", s.c_str (), get_name ().c_str (),
1104  match.c_str ());
1105  current_val = match;
1107  return true;
1108  }
1109  }
1110  else
1111  {
1112  color_values col (s);
1113  if (! error_state)
1114  {
1115  if (current_type != color_t || col != color_val)
1116  {
1117  color_val = col;
1119  return true;
1120  }
1121  }
1122  else
1123  error ("invalid value for color property \"%s\" (value = %s)",
1124  get_name ().c_str (), s.c_str ());
1125  }
1126  }
1127  else
1128  error ("invalid value for color property \"%s\"",
1129  get_name ().c_str ());
1130  }
1131  else if (val.is_numeric_type ())
1132  {
1133  Matrix m = val.matrix_value ();
1134 
1135  if (m.numel () == 3)
1136  {
1137  color_values col (m(0), m(1), m(2));
1138  if (! error_state)
1139  {
1140  if (current_type != color_t || col != color_val)
1141  {
1142  color_val = col;
1144  return true;
1145  }
1146  }
1147  }
1148  else
1149  error ("invalid value for color property \"%s\"",
1150  get_name ().c_str ());
1151  }
1152  else
1153  error ("invalid value for color property \"%s\"",
1154  get_name ().c_str ());
1155 
1156  return false;
1157 }
1158 
1159 bool
1161 {
1162  if (val.is_string ())
1163  {
1164  std::string s = val.string_value ();
1165  std::string match;
1166 
1167  if (! s.empty () && radio_val.contains (s, match))
1168  {
1169  if (current_type != radio_t || match != current_val)
1170  {
1171  if (s.length () != match.length ())
1172  warning_with_id ("Octave:abbreviated-property-match",
1173  "%s: allowing %s to match %s value %s",
1174  "set", s.c_str (), get_name ().c_str (),
1175  match.c_str ());
1176  current_val = match;
1178  return true;
1179  }
1180  }
1181  else
1182  error ("invalid value for double_radio property \"%s\"",
1183  get_name ().c_str ());
1184  }
1185  else if (val.is_scalar_type () && val.is_real_type ())
1186  {
1187  double new_dval = val.double_value ();
1188 
1189  if (current_type != double_t || new_dval != dval)
1190  {
1191  dval = new_dval;
1193  return true;
1194  }
1195  }
1196  else
1197  error ("invalid value for double_radio property \"%s\"",
1198  get_name ().c_str ());
1199 
1200  return false;
1201 }
1202 
1203 bool
1205 {
1206  bool xok = false;
1207 
1208  // check value type
1209  if (type_constraints.size () > 0)
1210  {
1211  if (type_constraints.find (v.class_name ()) != type_constraints.end ())
1212  xok = true;
1213 
1214  // check if complex is allowed (it's also of class "double", so
1215  // checking that alone is not enough to ensure real type)
1216  if (type_constraints.find ("real") != type_constraints.end ()
1217  && v.is_complex_type ())
1218  xok = false;
1219  }
1220  else
1221  xok = v.is_numeric_type ();
1222 
1223  if (xok)
1224  {
1225  if (size_constraints.size () == 0)
1226  return true;
1227 
1228  dim_vector vdims = v.dims ();
1229  int vlen = vdims.length ();
1230 
1231  xok = false;
1232 
1233  // check dimensional size constraints until a match is found
1234  for (std::list<dim_vector>::const_iterator it = size_constraints.begin ();
1235  ! xok && it != size_constraints.end (); ++it)
1236  {
1237  dim_vector itdims = (*it);
1238 
1239  if (itdims.length () == vlen)
1240  {
1241  xok = true;
1242 
1243  for (int i = 0; xok && i < vlen; i++)
1244  {
1245  if (itdims(i) > 0)
1246  {
1247  if (itdims(i) != vdims(i))
1248  xok = false;
1249  }
1250  else if (v.is_empty ())
1251  break;
1252  }
1253  }
1254  }
1255  }
1256 
1257  return xok;
1258 }
1259 
1260 bool
1262 {
1263  if (data.type_name () == v.type_name ())
1264  {
1265  if (data.dims () == v.dims ())
1266  {
1267 
1268 #define CHECK_ARRAY_EQUAL(T,F,A) \
1269  { \
1270  if (data.numel () == 1) \
1271  return data.F ## scalar_value () == \
1272  v.F ## scalar_value (); \
1273  else \
1274  { \
1275  /* Keep copy of array_value to allow sparse/bool arrays */ \
1276  /* that are converted, to not be deallocated early */ \
1277  const A m1 = data.F ## array_value (); \
1278  const T* d1 = m1.data (); \
1279  const A m2 = v.F ## array_value (); \
1280  const T* d2 = m2.data ();\
1281  \
1282  bool flag = true; \
1283  \
1284  for (int i = 0; flag && i < data.numel (); i++) \
1285  if (d1[i] != d2[i]) \
1286  flag = false; \
1287  \
1288  return flag; \
1289  } \
1290  }
1291 
1292  if (data.is_double_type () || data.is_bool_type ())
1293  CHECK_ARRAY_EQUAL (double, , NDArray)
1294  else if (data.is_single_type ())
1295  CHECK_ARRAY_EQUAL (float, float_, FloatNDArray)
1296  else if (data.is_int8_type ())
1298  else if (data.is_int16_type ())
1300  else if (data.is_int32_type ())
1302  else if (data.is_int64_type ())
1304  else if (data.is_uint8_type ())
1306  else if (data.is_uint16_type ())
1308  else if (data.is_uint32_type ())
1310  else if (data.is_uint64_type ())
1312  }
1313  }
1314 
1315  return false;
1316 }
1317 
1318 void
1320 {
1321  xmin = xminp = octave_Inf;
1322  xmax = xmaxp = -octave_Inf;
1323 
1324  if (! data.is_empty ())
1325  {
1326  if (data.is_integer_type ())
1327  {
1328  if (data.is_int8_type ())
1330  xmin, xmax, xminp, xmaxp);
1331  else if (data.is_uint8_type ())
1333  xmin, xmax, xminp, xmaxp);
1334  else if (data.is_int16_type ())
1336  xmin, xmax, xminp, xmaxp);
1337  else if (data.is_uint16_type ())
1339  xmin, xmax, xminp, xmaxp);
1340  else if (data.is_int32_type ())
1342  xmin, xmax, xminp, xmaxp);
1343  else if (data.is_uint32_type ())
1345  xmin, xmax, xminp, xmaxp);
1346  else if (data.is_int64_type ())
1348  xmin, xmax, xminp, xmaxp);
1349  else if (data.is_uint64_type ())
1351  xmin, xmax, xminp, xmaxp);
1352  }
1353  else
1355  }
1356 }
1357 
1358 bool
1360 {
1361  double dv = v.double_value ();
1362 
1363  if (! error_state)
1364  {
1366 
1367  if (xisnan (gh.value ()) || gh.ok ())
1368  {
1369  if (current_val != gh)
1370  {
1371  current_val = gh;
1372  return true;
1373  }
1374  }
1375  else
1376  error ("set: invalid graphics handle (= %g) for property \"%s\"",
1377  dv, get_name ().c_str ());
1378  }
1379  else
1380  error ("set: invalid graphics handle for property \"%s\"",
1381  get_name ().c_str ());
1382 
1383  return false;
1384 }
1385 
1386 Matrix
1387 children_property::do_get_children (bool return_hidden) const
1388 {
1389  Matrix retval (children_list.size (), 1);
1390  octave_idx_type k = 0;
1391 
1393 
1394  root_figure::properties& props =
1395  dynamic_cast<root_figure::properties&> (go.get_properties ());
1396 
1397  if (! props.is_showhiddenhandles ())
1398  {
1399  for (const_children_list_iterator p = children_list.begin ();
1400  p != children_list.end (); p++)
1401  {
1402  graphics_handle kid = *p;
1403 
1405  {
1406  if (! return_hidden)
1407  retval(k++) = *p;
1408  }
1409  else if (return_hidden)
1410  retval(k++) = *p;
1411  }
1412 
1413  retval.resize (k, 1);
1414  }
1415  else
1416  {
1417  for (const_children_list_iterator p = children_list.begin ();
1418  p != children_list.end (); p++)
1419  retval(k++) = *p;
1420  }
1421 
1422  return retval;
1423 }
1424 
1425 void
1427 {
1428  for (children_list_iterator p = children_list.begin ();
1429  p != children_list.end (); p++)
1430  {
1432 
1433  if (go.valid_object ())
1434  gh_manager::free (*p);
1435 
1436  }
1437 
1438  if (clear)
1439  children_list.clear ();
1440 }
1441 
1442 bool
1444 {
1445  // case 1: function handle
1446  // case 2: cell array with first element being a function handle
1447  // case 3: string corresponding to known function name
1448  // case 4: evaluatable string
1449  // case 5: empty matrix
1450 
1451  if (v.is_function_handle ())
1452  return true;
1453  else if (v.is_string ())
1454  // complete validation will be done at execution-time
1455  return true;
1456  else if (v.is_cell () && v.length () > 0
1457  && (v.rows () == 1 || v.columns () == 1)
1458  && v.cell_value ()(0).is_function_handle ())
1459  return true;
1460  else if (v.is_empty ())
1461  return true;
1462 
1463  return false;
1464 }
1465 
1466 // If TRUE, we are executing any callback function, or the functions it
1467 // calls. Used to determine handle visibility inside callback
1468 // functions.
1469 static bool executing_callback = false;
1470 
1471 void
1473 {
1474  unwind_protect frame;
1475 
1476  // We are executing the callback function associated with this
1477  // callback property. When set to true, we avoid recursive calls to
1478  // callback routines.
1479  frame.protect_var (executing);
1480 
1481  // We are executing a callback function, so allow handles that have
1482  // their handlevisibility property set to "callback" to be visible.
1483  frame.protect_var (executing_callback);
1484 
1485  if (! executing)
1486  {
1487  executing = true;
1488  executing_callback = true;
1489 
1490  if (callback.is_defined () && ! callback.is_empty ())
1492  }
1493 }
1494 
1495 // Used to cache dummy graphics objects from which dynamic
1496 // properties can be cloned.
1497 static std::map<caseless_str, graphics_object> dprop_obj_map;
1498 
1499 property
1500 property::create (const std::string& name, const graphics_handle& h,
1501  const caseless_str& type, const octave_value_list& args)
1502 {
1503  property retval;
1504 
1505  if (type.compare ("string"))
1506  {
1507  std::string val = (args.length () > 0 ? args(0).string_value () : "");
1508 
1509  if (! error_state)
1510  retval = property (new string_property (name, h, val));
1511  }
1512  else if (type.compare ("any"))
1513  {
1514  octave_value val = args.length () > 0 ? args(0)
1515  : octave_value (Matrix ());
1516 
1517  retval = property (new any_property (name, h, val));
1518  }
1519  else if (type.compare ("radio"))
1520  {
1521  if (args.length () > 0)
1522  {
1523  std::string vals = args(0).string_value ();
1524 
1525  if (! error_state)
1526  {
1527  retval = property (new radio_property (name, h, vals));
1528 
1529  if (args.length () > 1)
1530  retval.set (args(1));
1531  }
1532  else
1533  error ("addproperty: invalid argument for radio property, expected a string value");
1534  }
1535  else
1536  error ("addproperty: missing possible values for radio property");
1537  }
1538  else if (type.compare ("double"))
1539  {
1540  double d = (args.length () > 0 ? args(0).double_value () : 0);
1541 
1542  if (! error_state)
1543  retval = property (new double_property (name, h, d));
1544  }
1545  else if (type.compare ("handle"))
1546  {
1547  double hh = (args.length () > 0 ? args(0).double_value () : octave_NaN);
1548 
1549  if (! error_state)
1550  {
1551  graphics_handle gh (hh);
1552 
1553  retval = property (new handle_property (name, h, gh));
1554  }
1555  }
1556  else if (type.compare ("boolean"))
1557  {
1558  retval = property (new bool_property (name, h, false));
1559 
1560  if (args.length () > 0)
1561  retval.set (args(0));
1562  }
1563  else if (type.compare ("data"))
1564  {
1565  retval = property (new array_property (name, h, Matrix ()));
1566 
1567  if (args.length () > 0)
1568  {
1569  retval.set (args(0));
1570 
1571  // FIXME: additional argument could define constraints,
1572  // but is this really useful?
1573  }
1574  }
1575  else if (type.compare ("color"))
1576  {
1577  color_values cv (0, 0, 0);
1578  radio_values rv;
1579 
1580  if (args.length () > 1)
1581  rv = radio_values (args(1).string_value ());
1582 
1583  if (! error_state)
1584  {
1585  retval = property (new color_property (name, h, cv, rv));
1586 
1587  if (! error_state)
1588  {
1589  if (args.length () > 0 && ! args(0).is_empty ())
1590  retval.set (args(0));
1591  else
1592  retval.set (rv.default_value ());
1593  }
1594  }
1595  }
1596  else
1597  {
1598  caseless_str go_name, go_rest;
1599 
1600  if (lookup_object_name (type, go_name, go_rest))
1601  {
1602  graphics_object go;
1603 
1604  std::map<caseless_str, graphics_object>::const_iterator it =
1605  dprop_obj_map.find (go_name);
1606 
1607  if (it == dprop_obj_map.end ())
1608  {
1609  base_graphics_object *bgo =
1611 
1612  if (bgo)
1613  {
1614  go = graphics_object (bgo);
1615 
1616  dprop_obj_map[go_name] = go;
1617  }
1618  }
1619  else
1620  go = it->second;
1621 
1622  if (go.valid_object ())
1623  {
1624  property prop = go.get_properties ().get_property (go_rest);
1625 
1626  if (! error_state)
1627  {
1628  retval = prop.clone ();
1629 
1630  retval.set_parent (h);
1631  retval.set_name (name);
1632 
1633  if (args.length () > 0)
1634  retval.set (args(0));
1635  }
1636  }
1637  else
1638  error ("addproperty: invalid object type (= %s)",
1639  go_name.c_str ());
1640  }
1641  else
1642  error ("addproperty: unsupported type for dynamic property (= %s)",
1643  type.c_str ());
1644  }
1645 
1646  return retval;
1647 }
1648 
1649 static void
1651 {
1653 
1654  if (go)
1655  {
1656  Matrix children = go.get_properties ().get_all_children ();
1657 
1658  for (int k = 0; k < children.numel (); k++)
1659  finalize_r (children(k));
1660 
1661  go.finalize ();
1662  }
1663 }
1664 
1665 static void
1667 {
1669 
1670  if (go)
1671  {
1672  Matrix children = go.get_properties ().get_all_children ();
1673 
1674  go.initialize ();
1675 
1676  for (int k = 0; k < children.numel (); k++)
1677  initialize_r (children(k));
1678  }
1679 }
1680 
1681 void
1683 {
1684  if (toolkit)
1686 
1687  toolkit = b;
1689  __plot_stream__ = Matrix ();
1690 
1691  if (toolkit)
1693 
1694  mark_modified ();
1695 }
1696 
1697 // ---------------------------------------------------------------------
1698 
1699 void
1701 {
1702  size_t offset = 0;
1703 
1704  size_t len = name.length ();
1705 
1706  if (len > 4)
1707  {
1708  caseless_str pfx = name.substr (0, 4);
1709 
1710  if (pfx.compare ("axes") || pfx.compare ("line")
1711  || pfx.compare ("text"))
1712  offset = 4;
1713  else if (len > 5)
1714  {
1715  pfx = name.substr (0, 5);
1716 
1717  if (pfx.compare ("image") || pfx.compare ("patch"))
1718  offset = 5;
1719  else if (len > 6)
1720  {
1721  pfx = name.substr (0, 6);
1722 
1723  if (pfx.compare ("figure") || pfx.compare ("uimenu"))
1724  offset = 6;
1725  else if (len > 7)
1726  {
1727  pfx = name.substr (0, 7);
1728 
1729  if (pfx.compare ("surface") || pfx.compare ("hggroup")
1730  || pfx.compare ("uipanel"))
1731  offset = 7;
1732  else if (len > 9)
1733  {
1734  pfx = name.substr (0, 9);
1735 
1736  if (pfx.compare ("uicontrol")
1737  || pfx.compare ("uitoolbar"))
1738  offset = 9;
1739  else if (len > 10)
1740  {
1741  pfx = name.substr (0, 10);
1742 
1743  if (pfx.compare ("uipushtool"))
1744  offset = 10;
1745  else if (len > 12)
1746  {
1747  pfx = name.substr (0, 12);
1748 
1749  if (pfx.compare ("uitoogletool"))
1750  offset = 12;
1751  else if (len > 13)
1752  {
1753  pfx = name.substr (0, 13);
1754 
1755  if (pfx.compare ("uicontextmenu"))
1756  offset = 13;
1757  }
1758  }
1759  }
1760  }
1761  }
1762  }
1763  }
1764 
1765  if (offset > 0)
1766  {
1767  // FIXME: should we validate property names and values here?
1768 
1769  std::string pname = name.substr (offset);
1770 
1771  std::transform (pfx.begin (), pfx.end (), pfx.begin (), tolower);
1772  std::transform (pname.begin (), pname.end (), pname.begin (),
1773  tolower);
1774 
1775  bool has_property = false;
1776  if (pfx == "axes")
1777  has_property = axes::properties::has_core_property (pname);
1778  else if (pfx == "line")
1779  has_property = line::properties::has_core_property (pname);
1780  else if (pfx == "text")
1781  has_property = text::properties::has_core_property (pname);
1782  else if (pfx == "image")
1783  has_property = image::properties::has_core_property (pname);
1784  else if (pfx == "patch")
1785  has_property = patch::properties::has_core_property (pname);
1786  else if (pfx == "figure")
1787  has_property = figure::properties::has_core_property (pname);
1788  else if (pfx == "surface")
1789  has_property = surface::properties::has_core_property (pname);
1790  else if (pfx == "hggroup")
1791  has_property = hggroup::properties::has_core_property (pname);
1792  else if (pfx == "uimenu")
1793  has_property = uimenu::properties::has_core_property (pname);
1794  else if (pfx == "uicontrol")
1795  has_property = uicontrol::properties::has_core_property (pname);
1796  else if (pfx == "uipanel")
1797  has_property = uipanel::properties::has_core_property (pname);
1798  else if (pfx == "uicontextmenu")
1799  has_property = uicontextmenu::properties::has_core_property (pname);
1800  else if (pfx == "uitoolbar")
1801  has_property = uitoolbar::properties::has_core_property (pname);
1802  else if (pfx == "uipushtool")
1803  has_property = uipushtool::properties::has_core_property (pname);
1804 
1805  if (has_property)
1806  {
1807  bool remove = false;
1808  if (val.is_string ())
1809  {
1810  std::string tval = val.string_value ();
1811 
1812  remove = (tval.compare ("remove") == 0);
1813  }
1814 
1815  pval_map_type& pval_map = plist_map[pfx];
1816 
1817  if (remove)
1818  {
1819  pval_map_iterator p = pval_map.find (pname);
1820 
1821  if (p != pval_map.end ())
1822  pval_map.erase (p);
1823  }
1824  else
1825  pval_map[pname] = val;
1826  }
1827  else
1828  error ("invalid %s property '%s'", pfx.c_str (), pname.c_str ());
1829  }
1830  }
1831 
1832  if (! error_state && offset == 0)
1833  error ("invalid default property specification");
1834 }
1835 
1838 {
1839  octave_value retval;
1840 
1841  size_t offset = 0;
1842 
1843  size_t len = name.length ();
1844 
1845  if (len > 4)
1846  {
1847  caseless_str pfx = name.substr (0, 4);
1848 
1849  if (pfx.compare ("axes") || pfx.compare ("line")
1850  || pfx.compare ("text"))
1851  offset = 4;
1852  else if (len > 5)
1853  {
1854  pfx = name.substr (0, 5);
1855 
1856  if (pfx.compare ("image") || pfx.compare ("patch"))
1857  offset = 5;
1858  else if (len > 6)
1859  {
1860  pfx = name.substr (0, 6);
1861 
1862  if (pfx.compare ("figure") || pfx.compare ("uimenu"))
1863  offset = 6;
1864  else if (len > 7)
1865  {
1866  pfx = name.substr (0, 7);
1867 
1868  if (pfx.compare ("surface") || pfx.compare ("hggroup")
1869  || pfx.compare ("uipanel"))
1870  offset = 7;
1871  else if (len > 9)
1872  {
1873  pfx = name.substr (0, 9);
1874 
1875  if (pfx.compare ("uicontrol")
1876  || pfx.compare ("uitoolbar"))
1877  offset = 9;
1878  else if (len > 10)
1879  {
1880  pfx = name.substr (0, 10);
1881 
1882  if (pfx.compare ("uipushtool"))
1883  offset = 10;
1884  else if (len > 12)
1885  {
1886  pfx = name.substr (0, 12);
1887 
1888  if (pfx.compare ("uitoggletool"))
1889  offset = 12;
1890  else if (len > 13)
1891  {
1892  pfx = name.substr (0, 13);
1893 
1894  if (pfx.compare ("uicontextmenu"))
1895  offset = 13;
1896  }
1897  }
1898  }
1899  }
1900  }
1901  }
1902  }
1903 
1904  if (offset > 0)
1905  {
1906  std::string pname = name.substr (offset);
1907 
1908  std::transform (pfx.begin (), pfx.end (), pfx.begin (), tolower);
1909  std::transform (pname.begin (), pname.end (), pname.begin (),
1910  tolower);
1911 
1912  plist_map_const_iterator p = find (pfx);
1913 
1914  if (p != end ())
1915  {
1916  const pval_map_type& pval_map = p->second;
1917 
1918  pval_map_const_iterator q = pval_map.find (pname);
1919 
1920  if (q != pval_map.end ())
1921  retval = q->second;
1922  }
1923  }
1924  }
1925 
1926  return retval;
1927 }
1928 
1930 property_list::as_struct (const std::string& prefix_arg) const
1931 {
1933 
1934  for (plist_map_const_iterator p = begin (); p != end (); p++)
1935  {
1936  std::string prefix = prefix_arg + p->first;
1937 
1938  const pval_map_type pval_map = p->second;
1939 
1940  for (pval_map_const_iterator q = pval_map.begin ();
1941  q != pval_map.end ();
1942  q++)
1943  m.assign (prefix + q->first, q->second);
1944  }
1945 
1946  return m;
1947 }
1948 
1949 // Set properties given as a cs-list of name, value pairs.
1950 
1951 void
1953 {
1954  int nargin = args.length ();
1955 
1956  if (nargin == 0)
1957  error ("graphics_object::set: Nothing to set");
1958  else if (nargin % 2 == 0)
1959  {
1960  for (int i = 0; i < nargin; i += 2)
1961  {
1962  caseless_str name = args(i).string_value ();
1963 
1964  if (! error_state)
1965  {
1966  octave_value val = args(i+1);
1967 
1968  set_value_or_default (name, val);
1969 
1970  if (error_state)
1971  break;
1972  }
1973  else
1974  error ("set: expecting argument %d to be a property name", i);
1975  }
1976  }
1977  else
1978  error ("set: invalid number of arguments");
1979 }
1980 
1981 /*
1982 ## test set with name, value pairs
1983 %!test
1984 %! hf = figure ("visible", "off");
1985 %! h = plot (1:10, 10:-1:1);
1986 %! set (h, "linewidth", 10, "marker", "x");
1987 %! lw = get (h, "linewidth");
1988 %! mk = get (h, "marker");
1989 %! close (hf);
1990 %! assert (lw, 10);
1991 %! assert (mk, "x");
1992 */
1993 
1994 // Set properties given in two cell arrays containing names and values.
1995 void
1997  const Cell& values, octave_idx_type row)
1998 {
1999  if (names.numel () != values.columns ())
2000  {
2001  error ("set: number of names must match number of value columns (%d != %d)",
2002  names.numel (), values.columns ());
2003  }
2004 
2005  octave_idx_type k = names.columns ();
2006 
2007  for (octave_idx_type column = 0; column < k; column++)
2008  {
2009  caseless_str name = names(column);
2010  octave_value val = values(row, column);
2011 
2012  set_value_or_default (name, val);
2013 
2014  if (error_state)
2015  break;
2016  }
2017 }
2018 
2019 /*
2020 ## test set with cell array arguments
2021 %!test
2022 %! hf = figure ("visible", "off");
2023 %! h = plot (1:10, 10:-1:1);
2024 %! set (h, {"linewidth", "marker"}, {10, "x"});
2025 %! lw = get (h, "linewidth");
2026 %! mk = get (h, "marker");
2027 %! close (hf);
2028 %! assert (lw, 10);
2029 %! assert (mk, "x");
2030 
2031 ## test set with multiple handles and cell array arguments
2032 %!test
2033 %! hf = figure ("visible", "off");
2034 %! unwind_protect
2035 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2036 %! set (h, {"linewidth", "marker"}, {10, "x"; 5, "o"});
2037 %! assert (get (h, "linewidth"), {10; 5});
2038 %! assert (get (h, "marker"), {"x"; "o"});
2039 %! set (h, {"linewidth", "marker"}, {10, "x"});
2040 %! assert (get (h, "linewidth"), {10; 10});
2041 %! assert (get (h, "marker"), {"x"; "x"});
2042 %! unwind_protect_cleanup
2043 %! close (hf);
2044 %! end_unwind_protect;
2045 
2046 %!error <set: number of graphics handles must match number of value rows>
2047 %! hf = figure ("visible", "off");
2048 %! unwind_protect
2049 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2050 %! set (h, {"linewidth", "marker"}, {10, "x"; 5, "o"; 7, "."});
2051 %! unwind_protect_cleanup
2052 %! close (hf);
2053 %! end_unwind_protect
2054 
2055 %!error <set: number of names must match number of value columns>
2056 %! hf = figure ("visible", "off");
2057 %! unwind_protect
2058 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2059 %! set (h, {"linewidth"}, {10, "x"; 5, "o"});
2060 %! unwind_protect_cleanup
2061 %! close (hf);
2062 %! end_unwind_protect
2063 */
2064 
2065 // Set properties given in a struct array
2066 void
2068 {
2069  for (octave_idx_type p = 0; p < m.nfields (); p++)
2070  {
2071  caseless_str name = m.keys ()[p];
2072 
2073  octave_value val = octave_value (m.contents (name).elem (m.numel () - 1));
2074 
2075  set_value_or_default (name, val);
2076 
2077  if (error_state)
2078  break;
2079  }
2080 }
2081 
2082 /*
2083 ## test set ticklabels for compatibility
2084 %!test
2085 %! hf = figure ("visible", "off");
2086 %! set (gca (), "xticklabel", [0, 0.2, 0.4, 0.6, 0.8, 1]);
2087 %! xticklabel = get (gca (), "xticklabel");
2088 %! close (hf);
2089 %! assert (class (xticklabel), "char");
2090 %! assert (size (xticklabel), [6, 3]);
2091 
2092 %!test
2093 %! hf = figure ("visible", "off");
2094 %! set (gca (), "xticklabel", "0|0.2|0.4|0.6|0.8|1");
2095 %! xticklabel = get (gca (), "xticklabel");
2096 %! close (hf);
2097 %! assert (class (xticklabel), "char");
2098 %! assert (size (xticklabel), [6, 3]);
2099 
2100 %!test
2101 %! hf = figure ("visible", "off");
2102 %! set (gca (), "xticklabel", ["0 "; "0.2"; "0.4"; "0.6"; "0.8"; "1 "]);
2103 %! xticklabel = get (gca (), "xticklabel");
2104 %! close (hf);
2105 %! assert (class (xticklabel), "char");
2106 %! assert (size (xticklabel), [6, 3]);
2107 
2108 %!test
2109 %! hf = figure ("visible", "off");
2110 %! set (gca (), "xticklabel", {"0", "0.2", "0.4", "0.6", "0.8", "1"});
2111 %! xticklabel = get (gca (), "xticklabel");
2112 %! close (hf);
2113 %! assert (class (xticklabel), "cell");
2114 %! assert (size (xticklabel), [6, 1]);
2115 */
2116 
2117 /*
2118 ## test set with struct arguments
2119 %!test
2120 %! hf = figure ("visible", "off");
2121 %! unwind_protect
2122 %! h = plot (1:10, 10:-1:1);
2123 %! set (h, struct ("linewidth", 10, "marker", "x"));
2124 %! assert (get (h, "linewidth"), 10);
2125 %! assert (get (h, "marker"), "x");
2126 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2127 %! set (h, struct ("linewidth", {5, 10}));
2128 %! assert (get (h, "linewidth"), {10; 10});
2129 %! unwind_protect_cleanup
2130 %! close (hf);
2131 %! end_unwind_protect
2132 
2133 ## test ordering
2134 %!test
2135 %! markchanged = @(h, foobar, name) set (h, "userdata", [get(h,"userdata"); {name}]);
2136 %! hf = figure ("visible", "off");
2137 %! unwind_protect
2138 %! h = line ();
2139 %! set (h, "userdata", {});
2140 %! addlistener (h, "color", {markchanged, "color"});
2141 %! addlistener (h, "linewidth", {markchanged, "linewidth"});
2142 %! ## "linewidth" first
2143 %! props.linewidth = 2;
2144 %! props.color = "r";
2145 %! set (h, props);
2146 %! assert (get (h, "userdata"), fieldnames (props));
2147 %! clear props;
2148 %! clf ();
2149 %! h = line ();
2150 %! set (h, "userdata", {});
2151 %! addlistener (h, "color", {markchanged, "color"});
2152 %! addlistener (h, "linewidth", {markchanged, "linewidth"});
2153 %! ## "color" first
2154 %! props.color = "r";
2155 %! props.linewidth = 2;
2156 %! set (h, props);
2157 %! assert (get (h, "userdata"), fieldnames (props));
2158 %! unwind_protect_cleanup
2159 %! close (hf);
2160 %! end_unwind_protect
2161 */
2162 
2163 // Set a property to a value or to its (factory) default value.
2164 
2165 void
2167  const octave_value& val)
2168 {
2169  if (val.is_string ())
2170  {
2171  std::string tval = val.string_value ();
2172 
2173  octave_value default_val;
2174 
2175  if (tval.compare ("default") == 0)
2176  {
2177  default_val = get_default (name);
2178 
2179  if (error_state)
2180  return;
2181 
2182  rep->set (name, default_val);
2183  }
2184  else if (tval.compare ("factory") == 0)
2185  {
2186  default_val = get_factory_default (name);
2187 
2188  if (error_state)
2189  return;
2190 
2191  rep->set (name, default_val);
2192  }
2193  else
2194  {
2195  // Matlab specifically uses "\default" to escape string setting
2196  if (tval.compare ("\\default") == 0)
2197  rep->set (name, "default");
2198  else if (tval.compare ("\\factory") == 0)
2199  rep->set (name, "factory");
2200  else
2201  rep->set (name, val);
2202  }
2203  }
2204  else
2205  rep->set (name, val);
2206 }
2207 
2208 /*
2209 ## test setting of default values
2210 %!test
2211 %! old_lw = get (0, "defaultlinelinewidth");
2212 %! unwind_protect
2213 %! hf = figure ("visible", "off");
2214 %! h = plot (1:10, 10:-1:1);
2215 %! set (0, "defaultlinelinewidth", 20);
2216 %! set (h, "linewidth", "default");
2217 %! assert (get (h, "linewidth"), 20);
2218 %! set (h, "linewidth", "factory");
2219 %! assert (get (h, "linewidth"), 0.5);
2220 %! unwind_protect_cleanup
2221 %! close (hf);
2222 %! set (0, "defaultlinelinewidth", old_lw);
2223 %! end_unwind_protect
2224 */
2225 
2226 static double
2228 {
2229  static double maxrand = RAND_MAX + 2.0;
2230 
2231  return (rand () + 1.0) / maxrand;
2232 }
2233 
2235 gh_manager::do_get_handle (bool integer_figure_handle)
2236 {
2237  graphics_handle retval;
2238 
2239  if (integer_figure_handle)
2240  {
2241  // Figure handles are positive integers corresponding to the
2242  // figure number.
2243 
2244  // We always want the lowest unused figure number.
2245 
2246  retval = 1;
2247 
2248  while (handle_map.find (retval) != handle_map.end ())
2249  retval++;
2250  }
2251  else
2252  {
2253  // Other graphics handles are negative integers plus some random
2254  // fractional part. To avoid running out of integers, we
2255  // recycle the integer part but tack on a new random part each
2256  // time.
2257 
2258  free_list_iterator p = handle_free_list.begin ();
2259 
2260  if (p != handle_free_list.end ())
2261  {
2262  retval = *p;
2263  handle_free_list.erase (p);
2264  }
2265  else
2266  {
2267  retval = graphics_handle (next_handle);
2268 
2269  next_handle = std::ceil (next_handle) - 1.0 - make_handle_fraction ();
2270  }
2271  }
2272 
2273  return retval;
2274 }
2275 
2276 void
2278 {
2279  if (h.ok ())
2280  {
2281  if (h.value () != 0)
2282  {
2283  iterator p = handle_map.find (h);
2284 
2285  if (p != handle_map.end ())
2286  {
2287  base_properties& bp = p->second.get_properties ();
2288 
2289  bp.set_beingdeleted (true);
2290 
2291  bp.delete_children ();
2292 
2293  octave_value val = bp.get_deletefcn ();
2294 
2295  bp.execute_deletefcn ();
2296 
2297  // Notify graphics toolkit.
2298  p->second.finalize ();
2299 
2300  // Note: this will be valid only for first explicitly
2301  // deleted object. All its children will then have an
2302  // unknown graphics toolkit.
2303 
2304  // Graphics handles for non-figure objects are negative
2305  // integers plus some random fractional part. To avoid
2306  // running out of integers, we recycle the integer part
2307  // but tack on a new random part each time.
2308 
2309  handle_map.erase (p);
2310 
2311  if (h.value () < 0)
2312  handle_free_list.insert
2313  (std::ceil (h.value ()) - make_handle_fraction ());
2314  }
2315  else
2316  error ("graphics_handle::free: invalid object %g", h.value ());
2317  }
2318  else
2319  error ("graphics_handle::free: can't delete root figure");
2320  }
2321 }
2322 
2323 void
2325  const graphics_handle& new_gh)
2326 {
2327  iterator p = handle_map.find (old_gh);
2328 
2329  if (p != handle_map.end ())
2330  {
2331  graphics_object go = p->second;
2332 
2333  handle_map.erase (p);
2334 
2335  handle_map[new_gh] = go;
2336 
2337  if (old_gh.value () < 0)
2338  handle_free_list.insert (std::ceil (old_gh.value ())
2339  - make_handle_fraction ());
2340  }
2341  else
2342  error ("graphics_handle::free: invalid object %g", old_gh.value ());
2343 
2344  for (figure_list_iterator q = figure_list.begin ();
2345  q != figure_list.end (); q++)
2346  {
2347  if (*q == old_gh)
2348  {
2349  *q = new_gh;
2350  break;
2351  }
2352  }
2353 }
2354 
2356 
2357 static void
2358 xset (const graphics_handle& h, const caseless_str& name,
2359  const octave_value& val)
2360 {
2362  obj.set (name, val);
2363 }
2364 
2365 static void
2366 xset (const graphics_handle& h, const octave_value_list& args)
2367 {
2368  if (args.length () > 0)
2369  {
2371  obj.set (args);
2372  }
2373 }
2374 
2375 static octave_value
2376 xget (const graphics_handle& h, const caseless_str& name)
2377 {
2379  return obj.get (name);
2380 }
2381 
2382 static graphics_handle
2383 reparent (const octave_value& ov, const std::string& who,
2384  const std::string& property, const graphics_handle& new_parent,
2385  bool adopt = true)
2386 {
2388 
2389  double val = ov.double_value ();
2390 
2391  if (! error_state)
2392  {
2393  h = gh_manager::lookup (val);
2394 
2395  if (h.ok ())
2396  {
2398 
2399  graphics_handle parent_h = obj.get_parent ();
2400 
2401  graphics_object parent_obj = gh_manager::get_object (parent_h);
2402 
2403  parent_obj.remove_child (h);
2404 
2405  if (adopt)
2406  obj.set ("parent", new_parent.value ());
2407  else
2408  obj.reparent (new_parent);
2409  }
2410  else
2411  error ("%s: invalid graphics handle (= %g) for %s",
2412  who.c_str (), val, property.c_str ());
2413  }
2414  else
2415  error ("%s: expecting %s to be a graphics handle",
2416  who.c_str (), property.c_str ());
2417 
2418  return h;
2419 }
2420 
2421 // This function is NOT equivalent to the scripting language function gcf.
2423 gcf (void)
2424 {
2425  octave_value val = xget (0, "currentfigure");
2426 
2427  return val.is_empty () ? octave_NaN : val.double_value ();
2428 }
2429 
2430 // This function is NOT equivalent to the scripting language function gca.
2432 gca (void)
2433 {
2434  octave_value val = xget (gcf (), "currentaxes");
2435 
2436  return val.is_empty () ? octave_NaN : val.double_value ();
2437 }
2438 
2439 static void
2441 {
2442  if (h.ok ())
2443  {
2445 
2446  // Don't do recursive deleting, due to callbacks
2447  if (! obj.get_properties ().is_beingdeleted ())
2448  {
2449  graphics_handle parent_h = obj.get_parent ();
2450 
2451  graphics_object parent_obj =
2452  gh_manager::get_object (parent_h);
2453 
2454  // NOTE: free the handle before removing it from its
2455  // parent's children, such that the object's
2456  // state is correct when the deletefcn callback
2457  // is executed
2458 
2459  gh_manager::free (h);
2460 
2461  // A callback function might have already deleted
2462  // the parent
2463  if (parent_obj.valid_object ())
2464  parent_obj.remove_child (h);
2465 
2466  Vdrawnow_requested = true;
2467  }
2468  }
2469 }
2470 
2471 static void
2473 {
2475 }
2476 
2477 static void
2479 {
2480  for (octave_idx_type i = 0; i < vals.numel (); i++)
2481  delete_graphics_object (vals.elem (i));
2482 }
2483 
2484 static void
2486 {
2487  octave_value closerequestfcn = xget (handle, "closerequestfcn");
2488 
2489  OCTAVE_SAFE_CALL (gh_manager::execute_callback, (handle, closerequestfcn));
2490 }
2491 
2492 static void
2494 {
2495  // Remove the deletefcn and closerequestfcn callbacks and delete the
2496  // object directly.
2497 
2498  xset (handle, "deletefcn", Matrix ());
2499  xset (handle, "closerequestfcn", Matrix ());
2500 
2501  delete_graphics_object (handle);
2502 }
2503 
2504 void
2506 {
2507  // FIXME: should we process or discard pending events?
2508 
2509  event_queue.clear ();
2510 
2511  // Don't use figure_list_iterator because we'll be removing elements
2512  // from the list elsewhere.
2513 
2514  Matrix hlist = do_figure_handle_list (true);
2515 
2516  for (octave_idx_type i = 0; i < hlist.numel (); i++)
2517  {
2518  graphics_handle h = gh_manager::lookup (hlist(i));
2519 
2520  if (h.ok ())
2521  close_figure (h);
2522  }
2523 
2524  // They should all be closed now. If not, force them to close.
2525 
2526  hlist = do_figure_handle_list (true);
2527 
2528  for (octave_idx_type i = 0; i < hlist.numel (); i++)
2529  {
2530  graphics_handle h = gh_manager::lookup (hlist(i));
2531 
2532  if (h.ok ())
2533  force_close_figure (h);
2534  }
2535 
2536  // None left now, right?
2537 
2538  hlist = do_figure_handle_list (true);
2539 
2540  assert (hlist.numel () == 0);
2541 
2542  // Clear all callback objects from our list.
2543 
2544  callback_objects.clear ();
2545 }
2546 
2547 static void
2549 {
2550  graphics_object parent_obj = gh_manager::get_object (p);
2551  parent_obj.adopt (h);
2552 }
2553 
2554 static bool
2556 {
2557  return h.ok ();
2558 }
2559 
2560 static bool
2561 is_handle (double val)
2562 {
2564 
2565  return h.ok ();
2566 }
2567 
2568 static octave_value
2570 {
2571  octave_value retval = false;
2572 
2573  if (val.is_real_scalar () && is_handle (val.double_value ()))
2574  retval = true;
2575  else if (val.is_numeric_type () && val.is_real_type ())
2576  {
2577  const NDArray handles = val.array_value ();
2578 
2579  if (! error_state)
2580  {
2581  boolNDArray result (handles.dims ());
2582 
2583  for (octave_idx_type i = 0; i < handles.numel (); i++)
2584  result.xelem (i) = is_handle (handles (i));
2585 
2586  retval = result;
2587  }
2588  }
2589 
2590  return retval;
2591 }
2592 
2593 static bool
2594 is_figure (double val)
2595 {
2597 
2598  return obj && obj.isa ("figure");
2599 }
2600 
2601 static void
2603 {
2606 }
2607 
2608 static void
2610 {
2612 
2613  if (go)
2614  go.initialize ();
2615 }
2616 
2617 // ---------------------------------------------------------------------
2618 
2619 void
2621 {
2623 
2624  update (go, id);
2625 }
2626 
2627 bool
2629 {
2631 
2632  return initialize (go);
2633 }
2634 
2635 void
2637 {
2639 
2640  finalize (go);
2641 }
2642 
2643 // ---------------------------------------------------------------------
2644 
2645 void
2648 {
2649  std::string go_name = graphics_object_name ();
2650 
2651  property_list::plist_map_const_iterator p = defaults.find (go_name);
2652 
2653  if (p != defaults.end ())
2654  {
2655  const property_list::pval_map_type pval_map = p->second;
2656 
2657  for (property_list::pval_map_const_iterator q = pval_map.begin ();
2658  q != pval_map.end ();
2659  q++)
2660  {
2661  std::string pname = q->first;
2662 
2663  obj.set (pname, q->second);
2664 
2665  if (error_state)
2666  {
2667  error ("error setting default property %s", pname.c_str ());
2668  break;
2669  }
2670  }
2671  }
2672 }
2673 
2676 {
2677  octave_value retval;
2678 
2679  std::map<caseless_str, property, cmp_caseless_str>::const_iterator it =
2680  all_props.find (name);
2681 
2682  if (it != all_props.end ())
2683  retval = it->second.get ();
2684  else
2685  error ("get: unknown property \"%s\"", name.c_str ());
2686 
2687  return retval;
2688 }
2689 
2692 {
2694 
2695  for (std::map<caseless_str, property, cmp_caseless_str>::const_iterator
2696  it = all_props.begin (); it != all_props.end (); ++it)
2697  if (all || ! it->second.is_hidden ())
2698  m.assign (it->second.get_name (), it->second.get ());
2699 
2700  return m;
2701 }
2702 
2703 std::set<std::string>
2705 {
2706  return dynamic_properties;
2707 }
2708 
2709 bool
2710 base_properties::has_dynamic_property (const std::string& pname)
2711 {
2712  const std::set<std::string>& dynprops = dynamic_property_names ();
2713 
2714  if (dynprops.find (pname) != dynprops.end ())
2715  return true;
2716  else
2717  return all_props.find (pname) != all_props.end ();
2718 }
2719 
2720 void
2722  const octave_value& val)
2723 {
2724  std::map<caseless_str, property, cmp_caseless_str>::iterator it =
2725  all_props.find (pname);
2726 
2727  if (it != all_props.end ())
2728  it->second.set (val);
2729  else
2730  error ("set: unknown property \"%s\"", pname.c_str ());
2731 
2732  if (! error_state)
2733  {
2734  dynamic_properties.insert (pname);
2735 
2736  mark_modified ();
2737  }
2738 }
2739 
2740 property
2742 {
2743  std::map<caseless_str, property, cmp_caseless_str>::const_iterator it =
2744  all_props.find (name);
2745 
2746  if (it == all_props.end ())
2747  {
2748  error ("get_property: unknown property \"%s\"", name.c_str ());
2749  return property ();
2750  }
2751  else
2752  return it->second;
2753 }
2754 
2755 void
2757 {
2758  double hnp = val.double_value ();
2759 
2760  graphics_handle new_parent = octave_NaN;
2761 
2762  if (! error_state)
2763  {
2764  if (hnp == __myhandle__)
2765  error ("set: can not set object parent to be object itself");
2766  else
2767  {
2768  new_parent = gh_manager::lookup (hnp);
2769 
2770  if (new_parent.ok ())
2771  {
2772  // Remove child from current parent
2773  graphics_object old_parent_obj;
2774  old_parent_obj = gh_manager::get_object (get_parent ());
2775  old_parent_obj.remove_child (__myhandle__);
2776 
2777  // Check new parent's parent is not this child to avoid recursion
2778  graphics_object new_parent_obj;
2779  new_parent_obj = gh_manager::get_object (new_parent);
2780  if (new_parent_obj.get_parent () == __myhandle__)
2781  {
2782  // new parent's parent gets child's original parent
2783  new_parent_obj.get_properties ().set_parent (get_parent ().as_octave_value ());
2784  }
2785 
2786  // Set parent property to new_parent and do adoption
2787  parent = new_parent.as_octave_value ();
2788  ::adopt (parent.handle_value (), __myhandle__);
2789  }
2790  else
2791  error ("set: invalid graphics handle (= %g) for parent", hnp);
2792  }
2793  }
2794  else
2795  error ("set: expecting parent to be a graphics handle");
2796 }
2797 
2798 void
2800 {
2801  __modified__ = "on";
2803  if (parent_obj)
2804  parent_obj.mark_modified ();
2805 }
2806 
2807 void
2809 {
2811 
2812  if (parent_obj)
2813  parent_obj.override_defaults (obj);
2814 }
2815 
2816 void
2817 base_properties::update_axis_limits (const std::string& axis_type) const
2818 {
2819  graphics_object obj = gh_manager::get_object (__myhandle__);
2820 
2821  if (obj)
2822  obj.update_axis_limits (axis_type);
2823 }
2824 
2825 void
2826 base_properties::update_axis_limits (const std::string& axis_type,
2827  const graphics_handle& h) const
2828 {
2829  graphics_object obj = gh_manager::get_object (__myhandle__);
2830 
2831  if (obj)
2832  obj.update_axis_limits (axis_type, h);
2833 }
2834 
2835 bool
2837 {
2838  return (handlevisibility.is ("on")
2839  || (executing_callback && ! handlevisibility.is ("off")));
2840 }
2841 
2844 {
2846 
2847  if (go)
2848  return go.get_toolkit ();
2849  else
2850  return graphics_toolkit ();
2851 }
2852 
2853 void
2855 {
2856  Matrix kids = get_children ();
2857 
2858  for (int i = 0; i < kids.numel (); i++)
2859  {
2860  graphics_object go = gh_manager::get_object (kids(i));
2861 
2862  if (go.valid_object ())
2864  }
2865 }
2866 
2867 void
2868 base_properties::update_autopos (const std::string& elem_type)
2869 {
2871 
2872  if (parent_obj.valid_object ())
2873  parent_obj.get_properties ().update_autopos (elem_type);
2874 }
2875 
2876 void
2878  listener_mode mode)
2879 {
2880  property p = get_property (nm);
2881 
2882  if (! error_state && p.ok ())
2883  p.add_listener (v, mode);
2884 }
2885 
2886 void
2888  const octave_value& v, listener_mode mode)
2889 {
2890  property p = get_property (nm);
2891 
2892  if (! error_state && p.ok ())
2893  p.delete_listener (v, mode);
2894 }
2895 
2896 // ---------------------------------------------------------------------
2897 
2898 void
2899 base_graphics_object::update_axis_limits (const std::string& axis_type)
2900 {
2901  if (valid_object ())
2902  {
2904 
2905  if (parent_obj)
2906  parent_obj.update_axis_limits (axis_type);
2907  }
2908  else
2909  error ("base_graphics_object::update_axis_limits: invalid graphics object");
2910 }
2911 
2912 void
2913 base_graphics_object::update_axis_limits (const std::string& axis_type,
2914  const graphics_handle& h)
2915 {
2916  if (valid_object ())
2917  {
2919 
2920  if (parent_obj)
2921  parent_obj.update_axis_limits (axis_type, h);
2922  }
2923  else
2924  error ("base_graphics_object::update_axis_limits: invalid graphics object");
2925 }
2926 
2927 void
2929 {
2930  octave_map m = get (true).map_value ();
2931 
2932  for (octave_map::const_iterator pa = m.begin (); pa != m.end (); pa++)
2933  {
2934  // FIXME: there has to be a better way. I think we want to
2935  // ask whether it is OK to delete the listener for the given
2936  // property. How can we know in advance that it will be OK?
2937 
2938  unwind_protect frame;
2939 
2940  frame.protect_var (error_state);
2942  frame.protect_var (Vdebug_on_error);
2944 
2945  discard_error_messages = true;
2946  Vdebug_on_error = false;
2947  Vdebug_on_warning = false;
2948 
2949  property p = get_properties ().get_property (pa->first);
2950 
2951  if (! error_state && p.ok ())
2952  p.delete_listener ();
2953  }
2954 }
2955 
2956 std::string
2958 {
2959  std::string retval;
2960 
2961  if (valid_object ())
2962  {
2963  octave_map m = get ().map_value ();
2964 
2965  for (octave_map::const_iterator pa = m.begin (); pa != m.end (); pa++)
2966  {
2967  if (pa->first != "children")
2968  {
2969  property p = get_properties ().get_property (pa->first);
2970 
2971  if (p.ok () && ! p.is_hidden ())
2972  {
2973  retval += "\n\t" + std::string (pa->first) + ": ";
2974  if (p.is_radio ())
2975  retval += p.values_as_string ();
2976  }
2977  }
2978  }
2979  if (retval != "")
2980  retval += "\n";
2981  }
2982  else
2983  error ("base_graphics_object::values_as_string: invalid graphics object");
2984 
2985  return retval;
2986 }
2987 
2990 {
2991  octave_scalar_map retval;
2992 
2993  if (valid_object ())
2994  {
2995  octave_scalar_map m = get ().scalar_map_value ();
2996 
2998  pa != m.end (); pa++)
2999  {
3000  if (pa->first != "children")
3001  {
3002  property p = get_properties ().get_property (pa->first);
3003 
3004  if (p.ok () && ! p.is_hidden ())
3005  {
3006  if (p.is_radio ())
3007  retval.assign (p.get_name (), p.values_as_cell ());
3008  else
3009  retval.assign (p.get_name (), Cell ());
3010  }
3011  }
3012  }
3013  }
3014  else
3015  error ("base_graphics_object::values_as_struct: invalid graphics object");
3016 
3017  return retval;
3018 }
3019 
3021 graphics_object::get_ancestor (const std::string& obj_type) const
3022 {
3023  if (valid_object ())
3024  {
3025  if (isa (obj_type))
3026  return *this;
3027  else
3028  return gh_manager::get_object (get_parent ()).get_ancestor (obj_type);
3029  }
3030  else
3031  return graphics_object ();
3032 }
3033 
3034 // ---------------------------------------------------------------------
3035 
3036 #include "graphics-props.cc"
3037 
3038 // ---------------------------------------------------------------------
3039 
3040 void
3042 {
3043  graphics_handle val (v);
3044 
3045  if (error_state)
3046  return;
3047 
3048  if (xisnan (val.value ()) || is_handle (val))
3049  {
3050  currentfigure = val;
3051 
3052  if (val.ok ())
3054  }
3055  else
3056  gripe_set_invalid ("currentfigure");
3057 }
3058 
3059 void
3061 {
3062  graphics_handle val (v);
3063 
3064  if (error_state)
3065  return;
3066 
3067  if (xisnan (val.value ()))
3068  {
3069  if (! cbo_stack.empty ())
3070  {
3071  val = cbo_stack.front ();
3072 
3073  cbo_stack.pop_front ();
3074  }
3075 
3076  callbackobject = val;
3077  }
3078  else if (is_handle (val))
3079  {
3080  if (get_callbackobject ().ok ())
3081  cbo_stack.push_front (get_callbackobject ());
3082 
3083  callbackobject = val;
3084  }
3085  else
3086  gripe_set_invalid ("callbackobject");
3087 }
3088 
3089 void
3091 {
3092  if (! error_state)
3093  {
3094  if (integerhandle.set (val, true))
3095  {
3096  bool int_fig_handle = integerhandle.is_on ();
3097 
3098  graphics_object this_go = gh_manager::get_object (__myhandle__);
3099 
3100  graphics_handle old_myhandle = __myhandle__;
3101 
3102  __myhandle__ = gh_manager::get_handle (int_fig_handle);
3103 
3104  gh_manager::renumber_figure (old_myhandle, __myhandle__);
3105 
3107 
3108  base_properties& props = parent_go.get_properties ();
3109 
3110  props.renumber_child (old_myhandle, __myhandle__);
3111 
3112  Matrix kids = get_children ();
3113 
3114  for (octave_idx_type i = 0; i < kids.numel (); i++)
3115  {
3116  graphics_object kid = gh_manager::get_object (kids(i));
3117 
3118  kid.get_properties ().renumber_parent (__myhandle__);
3119  }
3120 
3122 
3123  if (__myhandle__ == cf)
3124  xset (0, "currentfigure", __myhandle__.value ());
3125 
3126  this_go.update (integerhandle.get_id ());
3127 
3128  mark_modified ();
3129  }
3130  }
3131 }
3132 
3133 // FIXME: This should update monitorpositions and pointerlocation, but
3134 // as these properties are yet used, and so it doesn't matter that they
3135 // aren't set yet.
3136 void
3138 {
3139  caseless_str xunits = get_units ();
3140 
3141  Matrix ss = default_screensize ();
3142 
3143  double dpi = get_screenpixelsperinch ();
3144 
3145  if (xunits.compare ("inches"))
3146  {
3147  ss(0) = 0;
3148  ss(1) = 0;
3149  ss(2) /= dpi;
3150  ss(3) /= dpi;
3151  }
3152  else if (xunits.compare ("centimeters"))
3153  {
3154  ss(0) = 0;
3155  ss(1) = 0;
3156  ss(2) *= 2.54 / dpi;
3157  ss(3) *= 2.54 / dpi;
3158  }
3159  else if (xunits.compare ("normalized"))
3160  {
3161  ss = Matrix (1, 4, 1.0);
3162  ss(0) = 0;
3163  ss(1) = 0;
3164  }
3165  else if (xunits.compare ("points"))
3166  {
3167  ss(0) = 0;
3168  ss(1) = 0;
3169  ss(2) *= 72 / dpi;
3170  ss(3) *= 72 / dpi;
3171  }
3172 
3173  set_screensize (ss);
3174 }
3175 
3176 Matrix
3178 {
3179  Matrix screen_size = screen_size_pixels ();
3180  Matrix pos = Matrix (1, 4, 0);
3181  pos(2) = screen_size(0);
3182  pos(3) = screen_size(1);
3183  return pos;
3184 }
3185 
3186 /*
3187 %!test
3188 %! old_units = get (0, "units");
3189 %! unwind_protect
3190 %! set (0, "units", "pixels");
3191 %! sz = get (0, "screensize") - [1, 1, 0, 0];
3192 %! dpi = get (0, "screenpixelsperinch");
3193 %! set (0, "units", "inches");
3194 %! assert (get (0, "screensize"), sz / dpi, 0.5 / dpi);
3195 %! set (0, "units", "centimeters");
3196 %! assert (get (0, "screensize"), sz / dpi * 2.54, 0.5 / dpi * 2.54);
3197 %! set (0, "units", "points");
3198 %! assert (get (0, "screensize"), sz / dpi * 72, 0.5 / dpi * 72);
3199 %! set (0, "units", "normalized");
3200 %! assert (get (0, "screensize"), [0.0, 0.0, 1.0, 1.0]);
3201 %! set (0, "units", "pixels");
3202 %! assert (get (0, "screensize"), sz + [1, 1, 0, 0]);
3203 %! unwind_protect_cleanup
3204 %! set (0, "units", old_units);
3205 %! end_unwind_protect
3206 */
3207 
3208 void
3210 {
3212 
3214 
3215  xset (0, "currentfigure", cf.value ());
3216 
3218 }
3219 
3222 
3223 static void
3225 {
3226  property_list new_defaults;
3227 
3228  for (property_list::plist_map_const_iterator p = default_properties.begin ();
3229  p != default_properties.end (); p++)
3230  {
3231  const property_list::pval_map_type pval_map = p->second;
3232  std::string prefix = p->first;
3233 
3234  for (property_list::pval_map_const_iterator q = pval_map.begin ();
3235  q != pval_map.end ();
3236  q++)
3237  {
3238  std::string s = q->first;
3239 
3240  if (prefix == "axes" && (s == "position" || s == "units"))
3241  new_defaults.set (prefix + s, q->second);
3242  else if (prefix == "figure" && (s == "position" || s == "units"
3243  || s == "windowstyle"
3244  || s == "paperunits"))
3245  new_defaults.set (prefix + s, q->second);
3246  }
3247  }
3248 
3249  default_properties = new_defaults;
3250 }
3251 
3252 void
3254 {
3256 }
3257 
3258 // ---------------------------------------------------------------------
3259 
3260 void
3262 {
3263  graphics_handle val (v);
3264 
3265  if (error_state)
3266  return;
3267 
3268  if (xisnan (val.value ()) || is_handle (val))
3269  currentaxes = val;
3270  else
3271  gripe_set_invalid ("currentaxes");
3272 }
3273 
3274 void
3276 {
3278 
3279  if (gh == currentaxes.handle_value ())
3280  {
3281  graphics_handle new_currentaxes;
3282 
3283  Matrix kids = get_children ();
3284 
3285  for (octave_idx_type i = 0; i < kids.numel (); i++)
3286  {
3287  graphics_handle kid = kids(i);
3288 
3290 
3291  if (go.isa ("axes"))
3292  {
3293  new_currentaxes = kid;
3294  break;
3295  }
3296  }
3297 
3298  currentaxes = new_currentaxes;
3299  }
3300 }
3301 
3302 void
3304 {
3305  std::string s = val.string_value ();
3306 
3307  if (! error_state)
3308  {
3309  if (s == "on")
3310  xset (0, "currentfigure", __myhandle__.value ());
3311 
3312  visible = val;
3313  }
3314 }
3315 
3316 Matrix
3317 figure::properties::get_boundingbox (bool internal, const Matrix&) const
3318 {
3319  Matrix screen_size = screen_size_pixels ();
3320  Matrix pos = (internal ?
3321  get_position ().matrix_value () :
3322  get_outerposition ().matrix_value ());
3323 
3324  pos = convert_position (pos, get_units (), "pixels", screen_size);
3325 
3326  pos(0)--;
3327  pos(1)--;
3328  pos(1) = screen_size(1) - pos(1) - pos(3);
3329 
3330  return pos;
3331 }
3332 
3333 void
3335  bool do_notify_toolkit)
3336 {
3337  Matrix screen_size = screen_size_pixels ();
3338  Matrix pos = bb;
3339 
3340  pos(1) = screen_size(1) - pos(1) - pos(3);
3341  pos(1)++;
3342  pos(0)++;
3343  pos = convert_position (pos, "pixels", get_units (), screen_size);
3344 
3345  if (internal)
3346  set_position (pos, do_notify_toolkit);
3347  else
3348  set_outerposition (pos, do_notify_toolkit);
3349 }
3350 
3351 Matrix
3353 {
3354  Matrix bb = get_boundingbox (true);
3355  Matrix pos (1, 2, 0);
3356 
3357  pos(0) = x;
3358  pos(1) = y;
3359 
3360  pos(1) = bb(3) - pos(1);
3361  pos(0)++;
3362  pos = convert_position (pos, "pixels", get_units (),
3363  bb.extract_n (0, 2, 1, 2));
3364 
3365  return pos;
3366 }
3367 
3368 Matrix
3370 {
3371  Matrix bb = get_boundingbox (true);
3372  Matrix pos (1, 2, 0);
3373 
3374  pos(0) = x;
3375  pos(1) = y;
3376 
3377  pos = convert_position (pos, get_units (), "pixels",
3378  bb.extract_n (0, 2, 1, 2));
3379  pos(0)--;
3380  pos(1) = bb(3) - pos(1);
3381 
3382  return pos;
3383 }
3384 
3385 void
3387  bool do_notify_toolkit)
3388 {
3389  if (! error_state)
3390  {
3391  Matrix old_bb, new_bb;
3392  bool modified = false;
3393 
3394  old_bb = get_boundingbox (true);
3395  modified = position.set (v, false, do_notify_toolkit);
3396  new_bb = get_boundingbox (true);
3397 
3398  if (old_bb != new_bb)
3399  {
3400  if (old_bb(2) != new_bb(2) || old_bb(3) != new_bb(3))
3401  {
3402  execute_resizefcn ();
3403  update_boundingbox ();
3404  }
3405  }
3406 
3407  if (modified)
3408  {
3409  position.run_listeners (POSTSET);
3410  mark_modified ();
3411  }
3412  }
3413 }
3414 
3415 void
3417  bool do_notify_toolkit)
3418 {
3419  if (! error_state)
3420  {
3421  if (outerposition.set (v, true, do_notify_toolkit))
3422  {
3423  mark_modified ();
3424  }
3425  }
3426 }
3427 
3428 void
3430 {
3431  if (! error_state)
3432  {
3433  caseless_str typ = get_papertype ();
3434  caseless_str punits = v.string_value ();
3435  if (! error_state)
3436  {
3437  if (punits.compare ("normalized") && typ.compare ("<custom>"))
3438  error ("set: can't set the paperunits to normalized when the papertype is custom");
3439  else
3440  {
3441  caseless_str old_paperunits = get_paperunits ();
3442  if (paperunits.set (v, true))
3443  {
3444  update_paperunits (old_paperunits);
3445  mark_modified ();
3446  }
3447  }
3448  }
3449  }
3450 }
3451 
3452 void
3454 {
3455  if (! error_state)
3456  {
3457  caseless_str typ = v.string_value ();
3458  caseless_str punits = get_paperunits ();
3459  if (! error_state)
3460  {
3461  if (punits.compare ("normalized") && typ.compare ("<custom>"))
3462  error ("set: can't set the paperunits to normalized when the papertype is custom");
3463  else
3464  {
3465  if (papertype.set (v, true))
3466  {
3467  update_papertype ();
3468  mark_modified ();
3469  }
3470  }
3471  }
3472  }
3473 }
3474 
3475 static Matrix
3477 {
3478  Matrix ret (1, 2, 1.0);
3479 
3480  if (! punits.compare ("normalized"))
3481  {
3482  double in2units;
3483  double mm2units;
3484 
3485  if (punits.compare ("inches"))
3486  {
3487  in2units = 1.0;
3488  mm2units = 1 / 25.4 ;
3489  }
3490  else if (punits.compare ("centimeters"))
3491  {
3492  in2units = 2.54;
3493  mm2units = 1 / 10.0;
3494  }
3495  else // points
3496  {
3497  in2units = 72.0;
3498  mm2units = 72.0 / 25.4;
3499  }
3500 
3501  if (typ.compare ("usletter"))
3502  {
3503  ret (0) = 8.5 * in2units;
3504  ret (1) = 11.0 * in2units;
3505  }
3506  else if (typ.compare ("uslegal"))
3507  {
3508  ret (0) = 8.5 * in2units;
3509  ret (1) = 14.0 * in2units;
3510  }
3511  else if (typ.compare ("tabloid"))
3512  {
3513  ret (0) = 11.0 * in2units;
3514  ret (1) = 17.0 * in2units;
3515  }
3516  else if (typ.compare ("a0"))
3517  {
3518  ret (0) = 841.0 * mm2units;
3519  ret (1) = 1189.0 * mm2units;
3520  }
3521  else if (typ.compare ("a1"))
3522  {
3523  ret (0) = 594.0 * mm2units;
3524  ret (1) = 841.0 * mm2units;
3525  }
3526  else if (typ.compare ("a2"))
3527  {
3528  ret (0) = 420.0 * mm2units;
3529  ret (1) = 594.0 * mm2units;
3530  }
3531  else if (typ.compare ("a3"))
3532  {
3533  ret (0) = 297.0 * mm2units;
3534  ret (1) = 420.0 * mm2units;
3535  }
3536  else if (typ.compare ("a4"))
3537  {
3538  ret (0) = 210.0 * mm2units;
3539  ret (1) = 297.0 * mm2units;
3540  }
3541  else if (typ.compare ("a5"))
3542  {
3543  ret (0) = 148.0 * mm2units;
3544  ret (1) = 210.0 * mm2units;
3545  }
3546  else if (typ.compare ("b0"))
3547  {
3548  ret (0) = 1029.0 * mm2units;
3549  ret (1) = 1456.0 * mm2units;
3550  }
3551  else if (typ.compare ("b1"))
3552  {
3553  ret (0) = 728.0 * mm2units;
3554  ret (1) = 1028.0 * mm2units;
3555  }
3556  else if (typ.compare ("b2"))
3557  {
3558  ret (0) = 514.0 * mm2units;
3559  ret (1) = 728.0 * mm2units;
3560  }
3561  else if (typ.compare ("b3"))
3562  {
3563  ret (0) = 364.0 * mm2units;
3564  ret (1) = 514.0 * mm2units;
3565  }
3566  else if (typ.compare ("b4"))
3567  {
3568  ret (0) = 257.0 * mm2units;
3569  ret (1) = 364.0 * mm2units;
3570  }
3571  else if (typ.compare ("b5"))
3572  {
3573  ret (0) = 182.0 * mm2units;
3574  ret (1) = 257.0 * mm2units;
3575  }
3576  else if (typ.compare ("arch-a"))
3577  {
3578  ret (0) = 9.0 * in2units;
3579  ret (1) = 12.0 * in2units;
3580  }
3581  else if (typ.compare ("arch-b"))
3582  {
3583  ret (0) = 12.0 * in2units;
3584  ret (1) = 18.0 * in2units;
3585  }
3586  else if (typ.compare ("arch-c"))
3587  {
3588  ret (0) = 18.0 * in2units;
3589  ret (1) = 24.0 * in2units;
3590  }
3591  else if (typ.compare ("arch-d"))
3592  {
3593  ret (0) = 24.0 * in2units;
3594  ret (1) = 36.0 * in2units;
3595  }
3596  else if (typ.compare ("arch-e"))
3597  {
3598  ret (0) = 36.0 * in2units;
3599  ret (1) = 48.0 * in2units;
3600  }
3601  else if (typ.compare ("a"))
3602  {
3603  ret (0) = 8.5 * in2units;
3604  ret (1) = 11.0 * in2units;
3605  }
3606  else if (typ.compare ("b"))
3607  {
3608  ret (0) = 11.0 * in2units;
3609  ret (1) = 17.0 * in2units;
3610  }
3611  else if (typ.compare ("c"))
3612  {
3613  ret (0) = 17.0 * in2units;
3614  ret (1) = 22.0 * in2units;
3615  }
3616  else if (typ.compare ("d"))
3617  {
3618  ret (0) = 22.0 * in2units;
3619  ret (1) = 34.0 * in2units;
3620  }
3621  else if (typ.compare ("e"))
3622  {
3623  ret (0) = 34.0 * in2units;
3624  ret (1) = 43.0 * in2units;
3625  }
3626  }
3627 
3628  return ret;
3629 }
3630 
3631 void
3633 {
3634  Matrix pos = get_paperposition ().matrix_value ();
3635  Matrix sz = get_papersize ().matrix_value ();
3636 
3637  pos(0) /= sz(0);
3638  pos(1) /= sz(1);
3639  pos(2) /= sz(0);
3640  pos(3) /= sz(1);
3641 
3642  std::string porient = get_paperorientation ();
3643  caseless_str punits = get_paperunits ();
3644  caseless_str typ = get_papertype ();
3645 
3646  if (typ.compare ("<custom>"))
3647  {
3648  if (old_paperunits.compare ("centimeters"))
3649  {
3650  sz(0) /= 2.54;
3651  sz(1) /= 2.54;
3652  }
3653  else if (old_paperunits.compare ("points"))
3654  {
3655  sz(0) /= 72.0;
3656  sz(1) /= 72.0;
3657  }
3658 
3659  if (punits.compare ("centimeters"))
3660  {
3661  sz(0) *= 2.54;
3662  sz(1) *= 2.54;
3663  }
3664  else if (punits.compare ("points"))
3665  {
3666  sz(0) *= 72.0;
3667  sz(1) *= 72.0;
3668  }
3669  }
3670  else
3671  {
3672  sz = papersize_from_type (punits, typ);
3673  if (porient == "landscape")
3674  std::swap (sz(0), sz(1));
3675  }
3676 
3677  pos(0) *= sz(0);
3678  pos(1) *= sz(1);
3679  pos(2) *= sz(0);
3680  pos(3) *= sz(1);
3681 
3682  papersize.set (octave_value (sz));
3683  paperposition.set (octave_value (pos));
3684 }
3685 
3686 void
3688 {
3689  caseless_str typ = get_papertype ();
3690  if (! typ.compare ("<custom>"))
3691  {
3692  Matrix sz = papersize_from_type (get_paperunits (), typ);
3693  if (get_paperorientation () == "landscape")
3694  std::swap (sz(0), sz(1));
3695  // Call papersize.set rather than set_papersize to avoid loops
3696  // between update_papersize and update_papertype
3697  papersize.set (octave_value (sz));
3698  }
3699 }
3700 
3701 void
3703 {
3704  Matrix sz = get_papersize ().matrix_value ();
3705  if (sz(0) > sz(1))
3706  {
3707  std::swap (sz(0), sz(1));
3708  papersize.set (octave_value (sz));
3709  paperorientation.set (octave_value ("landscape"));
3710  }
3711  else
3712  {
3713  paperorientation.set ("portrait");
3714  }
3715  std::string punits = get_paperunits ();
3716  if (punits == "centimeters")
3717  {
3718  sz(0) /= 2.54;
3719  sz(1) /= 2.54;
3720  }
3721  else if (punits == "points")
3722  {
3723  sz(0) /= 72.0;
3724  sz(1) /= 72.0;
3725  }
3726  if (punits == "normalized")
3727  {
3728  caseless_str typ = get_papertype ();
3729  if (get_papertype () == "<custom>")
3730  error ("set: can't set the papertype to <custom> when the paperunits is normalized");
3731  }
3732  else
3733  {
3734  // TODO - the papersizes info is also in papersize_from_type().
3735  // Both should be rewritten to avoid the duplication.
3736  std::string typ = "<custom>";
3737  const double mm2in = 1.0 / 25.4;
3738  const double tol = 0.01;
3739 
3740  if (std::abs (sz(0) - 8.5) + std::abs (sz(1) - 11.0) < tol)
3741  typ = "usletter";
3742  else if (std::abs (sz(0) - 8.5) + std::abs (sz(1) - 14.0) < tol)
3743  typ = "uslegal";
3744  else if (std::abs (sz(0) - 11.0) + std::abs (sz(1) - 17.0) < tol)
3745  typ = "tabloid";
3746  else if (std::abs (sz(0) - 841.0 * mm2in)
3747  + std::abs (sz(1) - 1198.0 * mm2in) < tol)
3748  typ = "a0";
3749  else if (std::abs (sz(0) - 594.0 * mm2in)
3750  + std::abs (sz(1) - 841.0 * mm2in) < tol)
3751  typ = "a1";
3752  else if (std::abs (sz(0) - 420.0 * mm2in)
3753  + std::abs (sz(1) - 594.0 * mm2in) < tol)
3754  typ = "a2";
3755  else if (std::abs (sz(0) - 297.0 * mm2in)
3756  + std::abs (sz(1) - 420.0 * mm2in) < tol)
3757  typ = "a3";
3758  else if (std::abs (sz(0) - 210.0 * mm2in)
3759  + std::abs (sz(1) - 297.0 * mm2in) < tol)
3760  typ = "a4";
3761  else if (std::abs (sz(0) - 148.0 * mm2in)
3762  + std::abs (sz(1) - 210.0 * mm2in) < tol)
3763  typ = "a5";
3764  else if (std::abs (sz(0) - 1029.0 * mm2in)
3765  + std::abs (sz(1) - 1456.0 * mm2in) < tol)
3766  typ = "b0";
3767  else if (std::abs (sz(0) - 728.0 * mm2in)
3768  + std::abs (sz(1) - 1028.0 * mm2in) < tol)
3769  typ = "b1";
3770  else if (std::abs (sz(0) - 514.0 * mm2in)
3771  + std::abs (sz(1) - 728.0 * mm2in) < tol)
3772  typ = "b2";
3773  else if (std::abs (sz(0) - 364.0 * mm2in)
3774  + std::abs (sz(1) - 514.0 * mm2in) < tol)
3775  typ = "b3";
3776  else if (std::abs (sz(0) - 257.0 * mm2in)
3777  + std::abs (sz(1) - 364.0 * mm2in) < tol)
3778  typ = "b4";
3779  else if (std::abs (sz(0) - 182.0 * mm2in)
3780  + std::abs (sz(1) - 257.0 * mm2in) < tol)
3781  typ = "b5";
3782  else if (std::abs (sz(0) - 9.0)
3783  + std::abs (sz(1) - 12.0) < tol)
3784  typ = "arch-a";
3785  else if (std::abs (sz(0) - 12.0)
3786  + std::abs (sz(1) - 18.0) < tol)
3787  typ = "arch-b";
3788  else if (std::abs (sz(0) - 18.0)
3789  + std::abs (sz(1) - 24.0) < tol)
3790  typ = "arch-c";
3791  else if (std::abs (sz(0) - 24.0)
3792  + std::abs (sz(1) - 36.0) < tol)
3793  typ = "arch-d";
3794  else if (std::abs (sz(0) - 36.0)
3795  + std::abs (sz(1) - 48.0) < tol)
3796  typ = "arch-e";
3797  else if (std::abs (sz(0) - 8.5)
3798  + std::abs (sz(1) - 11.0) < tol)
3799  typ = "a";
3800  else if (std::abs (sz(0) - 11.0)
3801  + std::abs (sz(1) - 17.0) < tol)
3802  typ = "b";
3803  else if (std::abs (sz(0) - 17.0)
3804  + std::abs (sz(1) - 22.0) < tol)
3805  typ = "c";
3806  else if (std::abs (sz(0) - 22.0)
3807  + std::abs (sz(1) - 34.0) < tol)
3808  typ = "d";
3809  else if (std::abs (sz(0) - 34.0)
3810  + std::abs (sz(1) - 43.0) < tol)
3811  typ = "e";
3812  // Call papertype.set rather than set_papertype to avoid loops between
3813  // update_papersize and update_papertype
3814  papertype.set (typ);
3815  }
3816  if (punits == "centimeters")
3817  {
3818  sz(0) *= 2.54;
3819  sz(1) *= 2.54;
3820  }
3821  else if (punits == "points")
3822  {
3823  sz(0) *= 72.0;
3824  sz(1) *= 72.0;
3825  }
3826  if (get_paperorientation () == "landscape")
3827  {
3828  std::swap (sz(0), sz(1));
3829  papersize.set (octave_value (sz));
3830  }
3831 }
3832 
3833 /*
3834 %!test
3835 %! hf = figure ("visible", "off");
3836 %! unwind_protect
3837 %! set (hf, "paperunits", "inches");
3838 %! set (hf, "papersize", [5, 4]);
3839 %! set (hf, "paperunits", "points");
3840 %! assert (get (hf, "papersize"), [5, 4] * 72, 1);
3841 %! papersize = get (hf, "papersize");
3842 %! set (hf, "papersize", papersize + 1);
3843 %! set (hf, "papersize", papersize);
3844 %! assert (get (hf, "papersize"), [5, 4] * 72, 1);
3845 %! unwind_protect_cleanup
3846 %! close (hf);
3847 %! end_unwind_protect
3848 
3849 %!test
3850 %! hf = figure ("visible", "off");
3851 %! unwind_protect
3852 %! set (hf, "paperunits", "inches");
3853 %! set (hf, "papersize", [5, 4]);
3854 %! set (hf, "paperunits", "centimeters");
3855 %! assert (get (hf, "papersize"), [5, 4] * 2.54, 2.54/72);
3856 %! papersize = get (hf, "papersize");
3857 %! set (hf, "papersize", papersize + 1);
3858 %! set (hf, "papersize", papersize);
3859 %! assert (get (hf, "papersize"), [5, 4] * 2.54, 2.54/72);
3860 %! unwind_protect_cleanup
3861 %! close (hf);
3862 %! end_unwind_protect
3863 */
3864 
3865 void
3867 {
3868  std::string porient = get_paperorientation ();
3869  Matrix sz = get_papersize ().matrix_value ();
3870  Matrix pos = get_paperposition ().matrix_value ();
3871  if ((sz(0) > sz(1) && porient == "portrait")
3872  || (sz(0) < sz(1) && porient == "landscape"))
3873  {
3874  std::swap (sz(0), sz(1));
3875  std::swap (pos(0), pos(1));
3876  std::swap (pos(2), pos(3));
3877  // Call papertype.set rather than set_papertype to avoid loops
3878  // between update_papersize and update_papertype
3879  papersize.set (octave_value (sz));
3880  paperposition.set (octave_value (pos));
3881  }
3882 }
3883 
3884 /*
3885 %!test
3886 %! hf = figure ("visible", "off");
3887 %! unwind_protect
3888 %! tol = 100 * eps ();
3889 %! ## UPPER case and MiXed case is part of test and should not be changed.
3890 %! set (hf, "paperorientation", "PORTRAIT");
3891 %! set (hf, "paperunits", "inches");
3892 %! set (hf, "papertype", "USletter");
3893 %! assert (get (hf, "papersize"), [8.5, 11.0], tol);
3894 %! set (hf, "paperorientation", "Landscape");
3895 %! assert (get (hf, "papersize"), [11.0, 8.5], tol);
3896 %! set (hf, "paperunits", "centimeters");
3897 %! assert (get (hf, "papersize"), [11.0, 8.5] * 2.54, tol);
3898 %! set (hf, "papertype", "a4");
3899 %! assert (get (hf, "papersize"), [29.7, 21.0], tol);
3900 %! set (hf, "paperunits", "inches", "papersize", [8.5, 11.0]);
3901 %! assert (get (hf, "papertype"), "usletter");
3902 %! assert (get (hf, "paperorientation"), "portrait");
3903 %! set (hf, "papersize", [11.0, 8.5]);
3904 %! assert (get (hf, "papertype"), "usletter");
3905 %! assert (get (hf, "paperorientation"), "landscape");
3906 %! unwind_protect_cleanup
3907 %! close (hf);
3908 %! end_unwind_protect
3909 */
3910 
3911 void
3913 {
3914  if (! error_state)
3915  {
3916  caseless_str old_units = get_units ();
3917  if (units.set (v, true))
3918  {
3919  update_units (old_units);
3920  mark_modified ();
3921  }
3922  }
3923 }
3924 
3925 void
3927 {
3928  position.set (convert_position (get_position ().matrix_value (), old_units,
3929  get_units (), screen_size_pixels ()), false);
3930 }
3931 
3932 /*
3933 %!test
3934 %! hf = figure ("visible", "off");
3935 %! old_units = get (0, "units");
3936 %! unwind_protect
3937 %! set (0, "units", "pixels");
3938 %! rsz = get (0, "screensize");
3939 %! set (gcf (), "units", "pixels");
3940 %! fsz = get (gcf (), "position");
3941 %! set (gcf (), "units", "normalized");
3942 %! pos = get (gcf (), "position");
3943 %! assert (pos, (fsz - [1, 1, 0, 0]) ./ rsz([3, 4, 3, 4]));
3944 %! unwind_protect_cleanup
3945 %! close (hf);
3946 %! set (0, "units", old_units);
3947 %! end_unwind_protect
3948 */
3949 
3950 std::string
3952 {
3953  if (is_numbertitle ())
3954  {
3955  std::ostringstream os;
3956  std::string nm = get_name ();
3957 
3958  os << "Figure " << __myhandle__.value ();
3959  if (! nm.empty ())
3960  os << ": " << get_name ();
3961 
3962  return os.str ();
3963  }
3964  else
3965  return get_name ();
3966 }
3967 
3970 {
3971  octave_value retval = default_properties.lookup (name);
3972 
3973  if (retval.is_undefined ())
3974  {
3975  graphics_handle parent = get_parent ();
3976  graphics_object parent_obj = gh_manager::get_object (parent);
3977 
3978  retval = parent_obj.get_default (name);
3979  }
3980 
3981  return retval;
3982 }
3983 
3984 void
3986 {
3988 }
3989 
3990 // ---------------------------------------------------------------------
3991 
3992 void
3994 {
3995  position.add_constraint (dim_vector (1, 4));
3996  outerposition.add_constraint (dim_vector (1, 4));
3997  tightinset.add_constraint (dim_vector (1, 4));
3998  looseinset.add_constraint (dim_vector (1, 4));
3999  colororder.add_constraint (dim_vector (-1, 3));
4000  dataaspectratio.add_constraint (dim_vector (1, 3));
4001  plotboxaspectratio.add_constraint (dim_vector (1, 3));
4002  alim.add_constraint (2);
4003  clim.add_constraint (2);
4004  xlim.add_constraint (2);
4005  ylim.add_constraint (2);
4006  zlim.add_constraint (2);
4007  xtick.add_constraint (dim_vector (1, -1));
4008  ytick.add_constraint (dim_vector (1, -1));
4009  ztick.add_constraint (dim_vector (1, -1));
4010  ticklength.add_constraint (dim_vector (1, 2));
4011  Matrix vw (1, 2, 0);
4012  vw(1) = 90;
4013  view = vw;
4014  view.add_constraint (dim_vector (1, 2));
4015  cameraposition.add_constraint (dim_vector (1, 3));
4016  cameratarget.add_constraint (dim_vector (1, 3));
4017  Matrix upv (1, 3, 0.0);
4018  upv(2) = 1.0;
4019  cameraupvector = upv;
4020  cameraupvector.add_constraint (dim_vector (1, 3));
4021  currentpoint.add_constraint (dim_vector (2, 3));
4022  // No constraints for hidden transform properties
4023  update_font ();
4024 
4025  x_zlim.resize (1, 2);
4026 
4027  sx = "linear";
4028  sy = "linear";
4029  sz = "linear";
4030 
4031  calc_ticklabels (xtick, xticklabel, xscale.is ("log"));
4032  calc_ticklabels (ytick, yticklabel, yscale.is ("log"));
4033  calc_ticklabels (ztick, zticklabel, zscale.is ("log"));
4034 
4035  xset (xlabel.handle_value (), "handlevisibility", "off");
4036  xset (ylabel.handle_value (), "handlevisibility", "off");
4037  xset (zlabel.handle_value (), "handlevisibility", "off");
4038  xset (title.handle_value (), "handlevisibility", "off");
4039 
4040  xset (xlabel.handle_value (), "horizontalalignment", "center");
4041  xset (xlabel.handle_value (), "horizontalalignmentmode", "auto");
4042  xset (ylabel.handle_value (), "horizontalalignment", "center");
4043  xset (ylabel.handle_value (), "horizontalalignmentmode", "auto");
4044  xset (zlabel.handle_value (), "horizontalalignment", "right");
4045  xset (zlabel.handle_value (), "horizontalalignmentmode", "auto");
4046  xset (title.handle_value (), "horizontalalignment", "center");
4047  xset (title.handle_value (), "horizontalalignmentmode", "auto");
4048 
4049  xset (xlabel.handle_value (), "verticalalignment", "top");
4050  xset (xlabel.handle_value (), "verticalalignmentmode", "auto");
4051  xset (ylabel.handle_value (), "verticalalignment", "bottom");
4052  xset (ylabel.handle_value (), "verticalalignmentmode", "auto");
4053  xset (title.handle_value (), "verticalalignment", "bottom");
4054  xset (title.handle_value (), "verticalalignmentmode", "auto");
4055 
4056  xset (ylabel.handle_value (), "rotation", 90.0);
4057  xset (ylabel.handle_value (), "rotationmode", "auto");
4058 
4059  xset (zlabel.handle_value (), "visible", "off");
4060 
4061  xset (xlabel.handle_value (), "clipping", "off");
4062  xset (ylabel.handle_value (), "clipping", "off");
4063  xset (zlabel.handle_value (), "clipping", "off");
4064  xset (title.handle_value (), "clipping", "off");
4065 
4066  xset (xlabel.handle_value (), "autopos_tag", "xlabel");
4067  xset (ylabel.handle_value (), "autopos_tag", "ylabel");
4068  xset (zlabel.handle_value (), "autopos_tag", "zlabel");
4069  xset (title.handle_value (), "autopos_tag", "title");
4070 
4071  adopt (xlabel.handle_value ());
4072  adopt (ylabel.handle_value ());
4073  adopt (zlabel.handle_value ());
4074  adopt (title.handle_value ());
4075 
4076  Matrix tlooseinset = default_axes_position ();
4077  tlooseinset(2) = 1-tlooseinset(0)-tlooseinset(2);
4078  tlooseinset(3) = 1-tlooseinset(1)-tlooseinset(3);
4079  looseinset = tlooseinset;
4080 }
4081 
4082 Matrix
4084 {
4085  Matrix pos = init_pos;
4087  Matrix parent_bb = obj.get_properties ().get_boundingbox (true);
4088  Matrix ext = get_extent (true, true);
4089  ext(1) = parent_bb(3) - ext(1) - ext(3);
4090  ext(0)++;
4091  ext(1)++;
4092  ext = convert_position (ext, "pixels", get_units (),
4093  parent_bb.extract_n (0, 2, 1, 2));
4094  if (ext(0) < pos(0))
4095  {
4096  pos(2) += pos(0)-ext(0);
4097  pos(0) = ext(0);
4098  }
4099  if (ext(0)+ext(2) > pos(0)+pos(2))
4100  pos(2) = ext(0)+ext(2)-pos(0);
4101 
4102  if (ext(1) < pos(1))
4103  {
4104  pos(3) += pos(1)-ext(1);
4105  pos(1) = ext(1);
4106  }
4107  if (ext(1)+ext(3) > pos(1)+pos(3))
4108  pos(3) = ext(1)+ext(3)-pos(1);
4109  return pos;
4110 }
4111 
4112 void
4114 {
4115  // First part is equivalent to `update_tightinset ()'
4116  if (activepositionproperty.is ("position"))
4117  update_position ();
4118  else
4119  update_outerposition ();
4120  caseless_str old_units = get_units ();
4121  set_units ("normalized");
4122  Matrix pos = position.get ().matrix_value ();
4123  Matrix outpos = outerposition.get ().matrix_value ();
4124  Matrix tightpos = calc_tightbox (pos);
4125  Matrix tinset (1, 4, 1.0);
4126  tinset(0) = pos(0)-tightpos(0);
4127  tinset(1) = pos(1)-tightpos(1);
4128  tinset(2) = tightpos(0)+tightpos(2)-pos(0)-pos(2);
4129  tinset(3) = tightpos(1)+tightpos(3)-pos(1)-pos(3);
4130  tightinset = tinset;
4131  set_units (old_units);
4132  update_transform ();
4133  if (activepositionproperty.is ("position"))
4134  update_position ();
4135  else
4136  update_outerposition ();
4137 }
4138 
4139 /*
4140 %!testif HAVE_FLTK
4141 %! hf = figure ("visible", "off");
4142 %! graphics_toolkit (hf, "fltk");
4143 %! unwind_protect
4144 %! subplot(2,1,1); plot(rand(10,1)); subplot(2,1,2); plot(rand(10,1));
4145 %! hax = findall (gcf (), "type", "axes");
4146 %! positions = cell2mat (get (hax, "position"));
4147 %! outerpositions = cell2mat (get (hax, "outerposition"));
4148 %! looseinsets = cell2mat (get (hax, "looseinset"));
4149 %! tightinsets = cell2mat (get (hax, "tightinset"));
4150 %! subplot(2,1,1); plot(rand(10,1)); subplot(2,1,2); plot(rand(10,1));
4151 %! hax = findall (gcf (), "type", "axes");
4152 %! assert (cell2mat (get (hax, "position")), positions, 1e-4);
4153 %! assert (cell2mat (get (hax, "outerposition")), outerpositions, 1e-4);
4154 %! assert (cell2mat (get (hax, "looseinset")), looseinsets, 1e-4);
4155 %! assert (cell2mat (get (hax, "tightinset")), tightinsets, 1e-4);
4156 %! unwind_protect_cleanup
4157 %! close (hf);
4158 %! end_unwind_protect
4159 %!testif HAVE_FLTK
4160 %! hf = figure ("visible", "off");
4161 %! graphics_toolkit (hf, "fltk");
4162 %! fpos = get (hf, "position");
4163 %! unwind_protect
4164 %! plot (rand (3))
4165 %! position = get (gca, "position");
4166 %! outerposition = get (gca, "outerposition");
4167 %! looseinset = get (gca, "looseinset");
4168 %! tightinset = get (gca, "tightinset");
4169 %! set (hf, "position", [fpos(1:2), 2*fpos(3:4)])
4170 %! set (hf, "position", fpos);
4171 %! assert (get (gca, "outerposition"), outerposition, 0.001)
4172 %! assert (get (gca, "position"), position, 0.001)
4173 %! assert (get (gca, "looseinset"), looseinset, 0.001)
4174 %! assert (get (gca, "tightinset"), tightinset, 0.001)
4175 %! unwind_protect_cleanup
4176 %! close (hf);
4177 %! end_unwind_protect
4178 %!testif HAVE_FLTK
4179 %! hf = figure ("visible", "off");
4180 %! graphics_toolkit (hf, "fltk");
4181 %! fpos = get (hf, "position");
4182 %! set (gca, "activepositionproperty", "position")
4183 %! unwind_protect
4184 %! plot (rand (3))
4185 %! position = get (gca, "position");
4186 %! outerposition = get (gca, "outerposition");
4187 %! looseinset = get (gca, "looseinset");
4188 %! tightinset = get (gca, "tightinset");
4189 %! set (hf, "position", [fpos(1:2), 2*fpos(3:4)])
4190 %! set (hf, "position", fpos);
4191 %! assert (get (gca, "position"), position, 0.001)
4192 %! assert (get (gca, "outerposition"), outerposition, 0.001)
4193 %! assert (get (gca, "looseinset"), looseinset, 0.001)
4194 %! assert (get (gca, "tightinset"), tightinset, 0.001)
4195 %! unwind_protect_cleanup
4196 %! close (hf);
4197 %! end_unwind_protect
4198 */
4199 
4200 void
4202  const std::string& who,
4203  const octave_value& v)
4204 {
4205  graphics_handle val;
4206 
4207  if (v.is_string ())
4208  {
4209  val = gh_manager::make_graphics_handle ("text", __myhandle__,
4210  false, false);
4211 
4212  xset (val, "string", v);
4213  }
4214  else
4215  {
4217 
4218  if (go.isa ("text"))
4219  val = ::reparent (v, "set", who, __myhandle__, false);
4220  else
4221  {
4222  std::string cname = v.class_name ();
4223 
4224  error ("set: expecting text graphics object or character string for %s property, found %s",
4225  who.c_str (), cname.c_str ());
4226  }
4227  }
4228 
4229  if (! error_state)
4230  {
4231  xset (val, "handlevisibility", "off");
4232 
4234 
4236 
4237  hp = val;
4238 
4239  adopt (hp.handle_value ());
4240  }
4241 }
4242 
4243 void
4245 {
4246  set_text_child (xlabel, "xlabel", v);
4247  xset (xlabel.handle_value (), "positionmode", "auto");
4248  xset (xlabel.handle_value (), "rotationmode", "auto");
4249  xset (xlabel.handle_value (), "horizontalalignmentmode", "auto");
4250  xset (xlabel.handle_value (), "verticalalignmentmode", "auto");
4251  xset (xlabel.handle_value (), "clipping", "off");
4252  xset (xlabel.handle_value (), "color", get_xcolor ());
4253  xset (xlabel.handle_value (), "autopos_tag", "xlabel");
4254  update_xlabel_position ();
4255 }
4256 
4257 void
4259 {
4260  set_text_child (ylabel, "ylabel", v);
4261  xset (ylabel.handle_value (), "positionmode", "auto");
4262  xset (ylabel.handle_value (), "rotationmode", "auto");
4263  xset (ylabel.handle_value (), "horizontalalignmentmode", "auto");
4264  xset (ylabel.handle_value (), "verticalalignmentmode", "auto");
4265  xset (ylabel.handle_value (), "clipping", "off");
4266  xset (ylabel.handle_value (), "color", get_ycolor ());
4267  xset (ylabel.handle_value (), "autopos_tag", "ylabel");
4268  update_ylabel_position ();
4269 }
4270 
4271 void
4273 {
4274  set_text_child (zlabel, "zlabel", v);
4275  xset (zlabel.handle_value (), "positionmode", "auto");
4276  xset (zlabel.handle_value (), "rotationmode", "auto");
4277  xset (zlabel.handle_value (), "horizontalalignmentmode", "auto");
4278  xset (zlabel.handle_value (), "verticalalignmentmode", "auto");
4279  xset (zlabel.handle_value (), "clipping", "off");
4280  xset (zlabel.handle_value (), "color", get_zcolor ());
4281  xset (zlabel.handle_value (), "autopos_tag", "zlabel");
4282  update_zlabel_position ();
4283 }
4284 
4285 void
4287 {
4288  set_text_child (title, "title", v);
4289  xset (title.handle_value (), "positionmode", "auto");
4290  xset (title.handle_value (), "horizontalalignment", "center");
4291  xset (title.handle_value (), "horizontalalignmentmode", "auto");
4292  xset (title.handle_value (), "verticalalignment", "bottom");
4293  xset (title.handle_value (), "verticalalignmentmode", "auto");
4294  xset (title.handle_value (), "clipping", "off");
4295  xset (title.handle_value (), "autopos_tag", "title");
4296  update_title_position ();
4297 }
4298 
4299 void
4301  const std::string& mode)
4302 {
4303  box = "on";
4304  colororder = default_colororder ();
4305  // Note: dataspectratio will be set through update_aspectratios
4306  dataaspectratiomode = "auto";
4307  layer = "bottom";
4308 
4309  Matrix tlim (1, 2, 0.0);
4310  tlim(1) = 1;
4311  xlim = tlim;
4312  ylim = tlim;
4313  zlim = tlim;
4314 
4315  Matrix cl (1, 2, 0);
4316  cl(1) = 1;
4317  clim = cl;
4318 
4319  alim = tlim;
4320 
4321  xlimmode = "auto";
4322  ylimmode = "auto";
4323  zlimmode = "auto";
4324  climmode = "auto";
4325  alimmode = "auto";
4326 
4327  xgrid = "off";
4328  ygrid = "off";
4329  zgrid = "off";
4330  xminorgrid = "off";
4331  yminorgrid = "off";
4332  zminorgrid = "off";
4333  xtick = Matrix ();
4334  ytick = Matrix ();
4335  ztick = Matrix ();
4336  xtickmode = "auto";
4337  ytickmode = "auto";
4338  ztickmode = "auto";
4339  xminortick = "off";
4340  yminortick = "off";
4341  zminortick = "off";
4342  xticklabel = "";
4343  yticklabel = "";
4344  zticklabel = "";
4345  xticklabelmode = "auto";
4346  yticklabelmode = "auto";
4347  zticklabelmode = "auto";
4348 
4349  interpreter = "none";
4350 
4351  color = color_values ("white");
4352  xcolor = color_values ("black");
4353  ycolor = color_values ("black");
4354  zcolor = color_values ("black");
4355  xscale = "linear";
4356  yscale = "linear";
4357  zscale = "linear";
4358  xdir = "normal";
4359  ydir = "normal";
4360  zdir = "normal";
4361  yaxislocation = "left";
4362  xaxislocation = "bottom";
4363 
4364  Matrix tview (1, 2, 0.0);
4365  tview(1) = 90;
4366  view = tview;
4367 
4368  __hold_all__ = "off";
4369  nextplot = "replace";
4370 
4371  ambientlightcolor = Matrix (1, 3, 1.0);
4372 
4373  // Note: camera properties (not mode) will be set in update_transform
4374  camerapositionmode = "auto";
4375  cameratargetmode = "auto";
4376  cameraupvectormode = "auto";
4377  cameraviewanglemode = "auto";
4378 
4379  drawmode = "normal";
4380 
4381  fontangle = "normal";
4382  fontname = OCTAVE_DEFAULT_FONTNAME;
4383  fontsize = 10;
4384  fontunits = "points";
4385  fontweight = "normal";
4386 
4387  gridlinestyle = ":";
4388  linestyleorder = "-";
4389  linewidth = 0.5;
4390  minorgridlinestyle = ":";
4391 
4392  // Note: plotboxaspectratio will be set through update_aspectratios
4393  plotboxaspectratiomode = "auto";
4394  projection = "orthographic";
4395 
4396  tickdir = "in";
4397  tickdirmode = "auto";
4398  ticklength = default_axes_ticklength ();
4399 
4400  tightinset = Matrix (1, 4, 0.0);
4401 
4402  sx = "linear";
4403  sy = "linear";
4404  sz = "linear";
4405 
4406  visible = "on";
4407 
4408  // Replace preserves Position and Units properties
4409  if (mode != "replace")
4410  {
4411  outerposition = default_axes_outerposition ();
4412  position = default_axes_position ();
4413  activepositionproperty = "outerposition";
4414  }
4415 
4416  delete_children (true);
4417 
4418  xlabel = gh_manager::make_graphics_handle ("text", __myhandle__,
4419  false, false);
4420 
4421  ylabel = gh_manager::make_graphics_handle ("text", __myhandle__,
4422  false, false);
4423 
4424  zlabel = gh_manager::make_graphics_handle ("text", __myhandle__,
4425  false, false);
4426 
4427  title = gh_manager::make_graphics_handle ("text", __myhandle__,
4428  false, false);
4429 
4430  xset (xlabel.handle_value (), "handlevisibility", "off");
4431  xset (ylabel.handle_value (), "handlevisibility", "off");
4432  xset (zlabel.handle_value (), "handlevisibility", "off");
4433  xset (title.handle_value (), "handlevisibility", "off");
4434 
4435  xset (xlabel.handle_value (), "horizontalalignment", "center");
4436  xset (xlabel.handle_value (), "horizontalalignmentmode", "auto");
4437  xset (ylabel.handle_value (), "horizontalalignment", "center");
4438  xset (ylabel.handle_value (), "horizontalalignmentmode", "auto");
4439  xset (zlabel.handle_value (), "horizontalalignment", "right");
4440  xset (zlabel.handle_value (), "horizontalalignmentmode", "auto");
4441  xset (title.handle_value (), "horizontalalignment", "center");
4442  xset (title.handle_value (), "horizontalalignmentmode", "auto");
4443 
4444  xset (xlabel.handle_value (), "verticalalignment", "top");
4445  xset (xlabel.handle_value (), "verticalalignmentmode", "auto");
4446  xset (ylabel.handle_value (), "verticalalignment", "bottom");
4447  xset (ylabel.handle_value (), "verticalalignmentmode", "auto");
4448  xset (title.handle_value (), "verticalalignment", "bottom");
4449  xset (title.handle_value (), "verticalalignmentmode", "auto");
4450 
4451  xset (ylabel.handle_value (), "rotation", 90.0);
4452  xset (ylabel.handle_value (), "rotationmode", "auto");
4453 
4454  xset (zlabel.handle_value (), "visible", "off");
4455 
4456  xset (xlabel.handle_value (), "clipping", "off");
4457  xset (ylabel.handle_value (), "clipping", "off");
4458  xset (zlabel.handle_value (), "clipping", "off");
4459  xset (title.handle_value (), "clipping", "off");
4460 
4461  xset (xlabel.handle_value (), "autopos_tag", "xlabel");
4462  xset (ylabel.handle_value (), "autopos_tag", "ylabel");
4463  xset (zlabel.handle_value (), "autopos_tag", "zlabel");
4464  xset (title.handle_value (), "autopos_tag", "title");
4465 
4466  adopt (xlabel.handle_value ());
4467  adopt (ylabel.handle_value ());
4468  adopt (zlabel.handle_value ());
4469  adopt (title.handle_value ());
4470 
4471  update_transform ();
4472  sync_positions ();
4473  override_defaults (obj);
4474 }
4475 
4476 void
4478 {
4479  graphics_handle h = hp.handle_value ();
4480 
4481  if (h.ok ())
4482  {
4484 
4485  if (go.valid_object ())
4486  gh_manager::free (h);
4487 
4489  }
4490 
4491  // FIXME: is it necessary to check whether the axes object is
4492  // being deleted now? I think this function is only called when an
4493  // individual child object is delete and not when the parent axes
4494  // object is deleted.
4495 
4496  if (! is_beingdeleted ())
4497  {
4498  hp = gh_manager::make_graphics_handle ("text", __myhandle__,
4499  false, false);
4500 
4501  xset (hp.handle_value (), "handlevisibility", "off");
4502 
4503  adopt (hp.handle_value ());
4504  }
4505 }
4506 
4507 void
4509 {
4510  if (xlabel.handle_value ().ok () && h == xlabel.handle_value ())
4511  delete_text_child (xlabel);
4512  else if (ylabel.handle_value ().ok () && h == ylabel.handle_value ())
4513  delete_text_child (ylabel);
4514  else if (zlabel.handle_value ().ok () && h == zlabel.handle_value ())
4515  delete_text_child (zlabel);
4516  else if (title.handle_value ().ok () && h == title.handle_value ())
4517  delete_text_child (title);
4518  else
4520 }
4521 
4522 inline Matrix
4524 {
4525  Matrix m (4, 4, 0.0);
4526  for (int i = 0; i < 4; i++)
4527  m(i,i) = 1;
4528  return m;
4529 }
4530 
4531 inline ColumnVector
4533 {
4534  ColumnVector v (4, 0.0);
4535  v(3) = 1;
4536  return v;
4537 }
4538 
4539 inline ColumnVector
4540 xform_vector (double x, double y, double z)
4541 {
4542  ColumnVector v (4, 1.0);
4543  v(0) = x; v(1) = y; v(2) = z;
4544  return v;
4545 }
4546 
4547 inline ColumnVector
4548 transform (const Matrix& m, double x, double y, double z)
4549 {
4550  return (m * xform_vector (x, y, z));
4551 }
4552 
4553 inline Matrix
4554 xform_scale (double x, double y, double z)
4555 {
4556  Matrix m (4, 4, 0.0);
4557  m(0,0) = x; m(1,1) = y; m(2,2) = z; m(3,3) = 1;
4558  return m;
4559 }
4560 
4561 inline Matrix
4562 xform_translate (double x, double y, double z)
4563 {
4564  Matrix m = xform_matrix ();
4565  m(0,3) = x; m(1,3) = y; m(2,3) = z; m(3,3) = 1;
4566  return m;
4567 }
4568 
4569 inline void
4570 scale (Matrix& m, double x, double y, double z)
4571 {
4572  m = m * xform_scale (x, y, z);
4573 }
4574 
4575 inline void
4576 translate (Matrix& m, double x, double y, double z)
4577 {
4578  m = m * xform_translate (x, y, z);
4579 }
4580 
4581 inline void
4582 xform (ColumnVector& v, const Matrix& m)
4583 {
4584  v = m*v;
4585 }
4586 
4587 inline void
4588 scale (ColumnVector& v, double x, double y, double z)
4589 {
4590  v(0) *= x;
4591  v(1) *= y;
4592  v(2) *= z;
4593 }
4594 
4595 inline void
4596 translate (ColumnVector& v, double x, double y, double z)
4597 {
4598  v(0) += x;
4599  v(1) += y;
4600  v(2) += z;
4601 }
4602 
4603 inline void
4605 {
4606  double fact = 1.0 / sqrt (v(0)*v(0)+v(1)*v(1)+v(2)*v(2));
4607  scale (v, fact, fact, fact);
4608 }
4609 
4610 inline double
4612 {
4613  return (v1(0)*v2(0)+v1(1)*v2(1)+v1(2)*v2(2));
4614 }
4615 
4616 inline double
4617 norm (const ColumnVector& v)
4618 {
4619  return sqrt (dot (v, v));
4620 }
4621 
4622 inline ColumnVector
4624 {
4625  ColumnVector r = xform_vector ();
4626  r(0) = v1(1)*v2(2)-v1(2)*v2(1);
4627  r(1) = v1(2)*v2(0)-v1(0)*v2(2);
4628  r(2) = v1(0)*v2(1)-v1(1)*v2(0);
4629  return r;
4630 }
4631 
4632 inline Matrix
4634 {
4635  static double data[32] =
4636  {
4637  0,0,0,1,
4638  1,0,0,1,
4639  0,1,0,1,
4640  0,0,1,1,
4641  1,1,0,1,
4642  1,0,1,1,
4643  0,1,1,1,
4644  1,1,1,1
4645  };
4646  Matrix m (4, 8);
4647  memcpy (m.fortran_vec (), data, sizeof (double)*32);
4648  return m;
4649 }
4650 
4651 inline ColumnVector
4653 {
4654  ColumnVector retval (4, 1.0);
4655  memcpy (retval.fortran_vec (), m.fortran_vec (), sizeof (double)*3);
4656  return retval;
4657 }
4658 
4659 inline RowVector
4661 {
4662  return v.extract_n (0, 3).transpose ();
4663 }
4664 
4665 void
4667 {
4668  double xd = (xdir_is ("normal") ? 1 : -1);
4669  double yd = (ydir_is ("normal") ? 1 : -1);
4670  double zd = (zdir_is ("normal") ? 1 : -1);
4671 
4672  Matrix xlimits = sx.scale (get_xlim ().matrix_value ());
4673  Matrix ylimits = sy.scale (get_ylim ().matrix_value ());
4674  Matrix zlimits = sz.scale (get_zlim ().matrix_value ());
4675 
4676  double xo = xlimits(xd > 0 ? 0 : 1);
4677  double yo = ylimits(yd > 0 ? 0 : 1);
4678  double zo = zlimits(zd > 0 ? 0 : 1);
4679 
4680  Matrix pb = get_plotboxaspectratio ().matrix_value ();
4681 
4682  bool autocam = (camerapositionmode_is ("auto")
4683  && cameratargetmode_is ("auto")
4684  && cameraupvectormode_is ("auto")
4685  && cameraviewanglemode_is ("auto"));
4686  bool dowarp = (autocam && dataaspectratiomode_is ("auto")
4687  && plotboxaspectratiomode_is ("auto"));
4688 
4689  ColumnVector c_eye (xform_vector ());
4690  ColumnVector c_center (xform_vector ());
4691  ColumnVector c_upv (xform_vector ());
4692 
4693  if (cameratargetmode_is ("auto"))
4694  {
4695  c_center(0) = (xlimits(0)+xlimits(1))/2;
4696  c_center(1) = (ylimits(0)+ylimits(1))/2;
4697  c_center(2) = (zlimits(0)+zlimits(1))/2;
4698 
4699  cameratarget = xform2cam (c_center);
4700  }
4701  else
4702  c_center = cam2xform (get_cameratarget ().matrix_value ());
4703 
4704  if (camerapositionmode_is ("auto"))
4705  {
4706  Matrix tview = get_view ().matrix_value ();
4707  double az = tview(0), el = tview(1);
4708  double d = 5 * sqrt (pb(0)*pb(0)+pb(1)*pb(1)+pb(2)*pb(2));
4709 
4710  if (el == 90 || el == -90)
4711  c_eye(2) = d*signum (el);
4712  else
4713  {
4714  az *= M_PI/180.0;
4715  el *= M_PI/180.0;
4716  c_eye(0) = d * cos (el) * sin (az);
4717  c_eye(1) = -d* cos (el) * cos (az);
4718  c_eye(2) = d * sin (el);
4719  }
4720  c_eye(0) = c_eye(0)*(xlimits(1)-xlimits(0))/(xd*pb(0))+c_center(0);
4721  c_eye(1) = c_eye(1)*(ylimits(1)-ylimits(0))/(yd*pb(1))+c_center(1);
4722  c_eye(2) = c_eye(2)*(zlimits(1)-zlimits(0))/(zd*pb(2))+c_center(2);
4723 
4724  cameraposition = xform2cam (c_eye);
4725  }
4726  else
4727  c_eye = cam2xform (get_cameraposition ().matrix_value ());
4728 
4729  if (cameraupvectormode_is ("auto"))
4730  {
4731  Matrix tview = get_view ().matrix_value ();
4732  double az = tview(0), el = tview(1);
4733 
4734  if (el == 90 || el == -90)
4735  {
4736  c_upv(0) =
4737  -signum (el) *sin (az*M_PI/180.0)*(xlimits(1)-xlimits(0))/pb(0);
4738  c_upv(1) =
4739  signum (el) * cos (az*M_PI/180.0)*(ylimits(1)-ylimits(0))/pb(1);
4740  }
4741  else
4742  c_upv(2) = 1;
4743 
4744  cameraupvector = xform2cam (c_upv);
4745  }
4746  else
4747  c_upv = cam2xform (get_cameraupvector ().matrix_value ());
4748 
4749  Matrix x_view = xform_matrix ();
4750  Matrix x_projection = xform_matrix ();
4751  Matrix x_viewport = xform_matrix ();
4752  Matrix x_normrender = xform_matrix ();
4753  Matrix x_pre = xform_matrix ();
4754 
4755  x_render = xform_matrix ();
4756  x_render_inv = xform_matrix ();
4757 
4758  scale (x_pre, pb(0), pb(1), pb(2));
4759  translate (x_pre, -0.5, -0.5, -0.5);
4760  scale (x_pre, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
4761  zd/(zlimits(1)-zlimits(0)));
4762  translate (x_pre, -xo, -yo, -zo);
4763 
4764  xform (c_eye, x_pre);
4765  xform (c_center, x_pre);
4766  scale (c_upv, pb(0)/(xlimits(1)-xlimits(0)), pb(1)/(ylimits(1)-ylimits(0)),
4767  pb(2)/(zlimits(1)-zlimits(0)));
4768  translate (c_center, -c_eye(0), -c_eye(1), -c_eye(2));
4769 
4770  ColumnVector F (c_center), f (F), UP (c_upv);
4771  normalize (f);
4772  normalize (UP);
4773 
4774  if (std::abs (dot (f, UP)) > 1e-15)
4775  {
4776  double fa = 1 / sqrt(1-f(2)*f(2));
4777  scale (UP, fa, fa, fa);
4778  }
4779 
4780  ColumnVector s = cross (f, UP);
4781  ColumnVector u = cross (s, f);
4782 
4783  scale (x_view, 1, 1, -1);
4784  Matrix l = xform_matrix ();
4785  l(0,0) = s(0); l(0,1) = s(1); l(0,2) = s(2);
4786  l(1,0) = u(0); l(1,1) = u(1); l(1,2) = u(2);
4787  l(2,0) = -f(0); l(2,1) = -f(1); l(2,2) = -f(2);
4788  x_view = x_view * l;
4789  translate (x_view, -c_eye(0), -c_eye(1), -c_eye(2));
4790  scale (x_view, pb(0), pb(1), pb(2));
4791  translate (x_view, -0.5, -0.5, -0.5);
4792 
4793  Matrix x_cube = x_view * unit_cube ();
4794  ColumnVector cmin = x_cube.row_min (), cmax = x_cube.row_max ();
4795  double xM = cmax(0)-cmin(0);
4796  double yM = cmax(1)-cmin(1);
4797 
4798  Matrix bb = get_boundingbox (true);
4799 
4800  double v_angle;
4801 
4802  if (cameraviewanglemode_is ("auto"))
4803  {
4804  double af;
4805 
4806  // FIXME: was this really needed? When compared to Matlab, it
4807  // does not seem to be required. Need investigation with concrete
4808  // graphics toolkit to see results visually.
4809  if (false && dowarp)
4810  af = 1.0 / (xM > yM ? xM : yM);
4811  else
4812  {
4813  if ((bb(2)/bb(3)) > (xM/yM))
4814  af = 1.0 / yM;
4815  else
4816  af = 1.0 / xM;
4817  }
4818  v_angle = 2 * (180.0 / M_PI) * atan (1 / (2 * af * norm (F)));
4819 
4820  cameraviewangle = v_angle;
4821  }
4822  else
4823  v_angle = get_cameraviewangle ();
4824 
4825  double pf = 1 / (2 * tan ((v_angle / 2) * M_PI / 180.0) * norm (F));
4826  scale (x_projection, pf, pf, 1);
4827 
4828  if (dowarp)
4829  {
4830  xM *= pf;
4831  yM *= pf;
4832  translate (x_viewport, bb(0)+bb(2)/2, bb(1)+bb(3)/2, 0);
4833  scale (x_viewport, bb(2)/xM, -bb(3)/yM, 1);
4834  }
4835  else
4836  {
4837  double pix = 1;
4838  if (autocam)
4839  {
4840  if ((bb(2)/bb(3)) > (xM/yM))
4841  pix = bb(3);
4842  else
4843  pix = bb(2);
4844  }
4845  else
4846  pix = (bb(2) < bb(3) ? bb(2) : bb(3));
4847  translate (x_viewport, bb(0)+bb(2)/2, bb(1)+bb(3)/2, 0);
4848  scale (x_viewport, pix, -pix, 1);
4849  }
4850 
4851  x_normrender = x_viewport * x_projection * x_view;
4852 
4853  x_cube = x_normrender * unit_cube ();
4854  cmin = x_cube.row_min ();
4855  cmax = x_cube.row_max ();
4856  x_zlim.resize (1, 2);
4857  x_zlim(0) = cmin(2);
4858  x_zlim(1) = cmax(2);
4859 
4860  x_render = x_normrender;
4861  scale (x_render, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
4862  zd/(zlimits(1)-zlimits(0)));
4863  translate (x_render, -xo, -yo, -zo);
4864 
4865  x_viewtransform = x_view;
4866  x_projectiontransform = x_projection;
4867  x_viewporttransform = x_viewport;
4868  x_normrendertransform = x_normrender;
4869  x_rendertransform = x_render;
4870 
4871  x_render_inv = x_render.inverse ();
4872 
4873  // Note: these matrices are a slight modified version of the regular
4874  // matrices, more suited for OpenGL rendering (x_gl_mat1 => light
4875  // => x_gl_mat2)
4876  x_gl_mat1 = x_view;
4877  scale (x_gl_mat1, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
4878  zd/(zlimits(1)-zlimits(0)));
4879  translate (x_gl_mat1, -xo, -yo, -zo);
4880  x_gl_mat2 = x_viewport * x_projection;
4881 }
4882 
4883 static bool updating_axes_layout = false;
4884 
4885 void
4887 {
4888  if (updating_axes_layout)
4889  return;
4890 
4891  graphics_xform xform = get_transform ();
4892 
4893  double xd = (xdir_is ("normal") ? 1 : -1);
4894  double yd = (ydir_is ("normal") ? 1 : -1);
4895  double zd = (zdir_is ("normal") ? 1 : -1);
4896 
4897  const Matrix xlims = xform.xscale (get_xlim ().matrix_value ());
4898  const Matrix ylims = xform.yscale (get_ylim ().matrix_value ());
4899  const Matrix zlims = xform.zscale (get_zlim ().matrix_value ());
4900  double x_min = xlims(0), x_max = xlims(1);
4901  double y_min = ylims(0), y_max = ylims(1);
4902  double z_min = zlims(0), z_max = zlims(1);
4903 
4904  ColumnVector p1, p2, dir (3);
4905 
4906  xstate = ystate = zstate = AXE_ANY_DIR;
4907 
4908  p1 = xform.transform (x_min, (y_min+y_max)/2, (z_min+z_max)/2, false);
4909  p2 = xform.transform (x_max, (y_min+y_max)/2, (z_min+z_max)/2, false);
4910  dir(0) = xround (p2(0)-p1(0));
4911  dir(1) = xround (p2(1)-p1(1));
4912  dir(2) = (p2(2)-p1(2));
4913  if (dir(0) == 0 && dir(1) == 0)
4914  xstate = AXE_DEPTH_DIR;
4915  else if (dir(2) == 0)
4916  {
4917  if (dir(0) == 0)
4918  xstate = AXE_VERT_DIR;
4919  else if (dir(1) == 0)
4920  xstate = AXE_HORZ_DIR;
4921  }
4922 
4923  if (dir(2) == 0)
4924  {
4925  if (dir(1) == 0)
4926  xPlane = (dir(0) > 0 ? x_max : x_min);
4927  else
4928  xPlane = (dir(1) < 0 ? x_max : x_min);
4929  }
4930  else
4931  xPlane = (dir(2) < 0 ? x_min : x_max);
4932 
4933  xPlaneN = (xPlane == x_min ? x_max : x_min);
4934  fx = (x_max-x_min) / sqrt (dir(0)*dir(0)+dir(1)*dir(1));
4935 
4936  p1 = xform.transform ((x_min+x_max)/2, y_min, (z_min+z_max)/2, false);
4937  p2 = xform.transform ((x_min+x_max)/2, y_max, (z_min+z_max)/2, false);
4938  dir(0) = xround (p2(0)-p1(0));
4939  dir(1) = xround (p2(1)-p1(1));
4940  dir(2) = (p2(2)-p1(2));
4941  if (dir(0) == 0 && dir(1) == 0)
4942  ystate = AXE_DEPTH_DIR;
4943  else if (dir(2) == 0)
4944  {
4945  if (dir(0) == 0)
4946  ystate = AXE_VERT_DIR;
4947  else if (dir(1) == 0)
4948  ystate = AXE_HORZ_DIR;
4949  }
4950 
4951  if (dir(2) == 0)
4952  {
4953  if (dir(1) == 0)
4954  yPlane = (dir(0) > 0 ? y_max : y_min);
4955  else
4956  yPlane = (dir(1) < 0 ? y_max : y_min);
4957  }
4958  else
4959  yPlane = (dir(2) < 0 ? y_min : y_max);
4960 
4961  yPlaneN = (yPlane == y_min ? y_max : y_min);
4962  fy = (y_max-y_min) / sqrt (dir(0)*dir(0)+dir(1)*dir(1));
4963 
4964  p1 = xform.transform ((x_min+x_max)/2, (y_min+y_max)/2, z_min, false);
4965  p2 = xform.transform ((x_min+x_max)/2, (y_min+y_max)/2, z_max, false);
4966  dir(0) = xround (p2(0)-p1(0));
4967  dir(1) = xround (p2(1)-p1(1));
4968  dir(2) = (p2(2)-p1(2));
4969  if (dir(0) == 0 && dir(1) == 0)
4970  zstate = AXE_DEPTH_DIR;
4971  else if (dir(2) == 0)
4972  {
4973  if (dir(0) == 0)
4974  zstate = AXE_VERT_DIR;
4975  else if (dir(1) == 0)
4976  zstate = AXE_HORZ_DIR;
4977  }
4978 
4979  if (dir(2) == 0)
4980  {
4981  if (dir(1) == 0)
4982  zPlane = (dir(0) > 0 ? z_min : z_max);
4983  else
4984  zPlane = (dir(1) < 0 ? z_min : z_max);
4985  }
4986  else
4987  zPlane = (dir(2) < 0 ? z_min : z_max);
4988 
4989  zPlaneN = (zPlane == z_min ? z_max : z_min);
4990  fz = (z_max-z_min) / sqrt (dir(0)*dir(0)+dir(1)*dir(1));
4991 
4992  unwind_protect frame;
4993  frame.protect_var (updating_axes_layout);
4994  updating_axes_layout = true;
4995 
4996  xySym = (xd*yd*(xPlane-xPlaneN)*(yPlane-yPlaneN) > 0);
4997  zSign = (zd*(zPlane-zPlaneN) <= 0);
4998  xyzSym = zSign ? xySym : !xySym;
4999  xpTick = (zSign ? xPlaneN : xPlane);
5000  ypTick = (zSign ? yPlaneN : yPlane);
5001  zpTick = (zSign ? zPlane : zPlaneN);
5002  xpTickN = (zSign ? xPlane : xPlaneN);
5003  ypTickN = (zSign ? yPlane : yPlaneN);
5004  zpTickN = (zSign ? zPlaneN : zPlane);
5005 
5006  /* 2D mode */
5007  x2Dtop = false;
5008  y2Dright = false;
5009  layer2Dtop = false;
5010  if (xstate == AXE_HORZ_DIR && ystate == AXE_VERT_DIR)
5011  {
5012  if (xaxislocation_is ("top"))
5013  {
5014  double tmp = yPlane;
5015  yPlane = yPlaneN;
5016  yPlaneN = tmp;
5017  x2Dtop = true;
5018  }
5019  ypTick = yPlaneN;
5020  ypTickN = yPlane;
5021  if (yaxislocation_is ("right"))
5022  {
5023  double tmp = xPlane;
5024  xPlane = xPlaneN;
5025  xPlaneN = tmp;
5026  y2Dright = true;
5027  }
5028  xpTick = xPlaneN;
5029  xpTickN = xPlane;
5030  if (layer_is ("top"))
5031  {
5032  zpTick = zPlaneN;
5033  layer2Dtop = true;
5034  }
5035  else
5036  zpTick = zPlane;
5037  }
5038 
5039  Matrix viewmat = get_view ().matrix_value ();
5040  nearhoriz = std::abs (viewmat(1)) <= 5;
5041 
5042  update_ticklength ();
5043 }
5044 
5045 void
5047 {
5048  bool mode2d = (((xstate > AXE_DEPTH_DIR ? 1 : 0) +
5049  (ystate > AXE_DEPTH_DIR ? 1 : 0) +
5050  (zstate > AXE_DEPTH_DIR ? 1 : 0)) == 2);
5051 
5052  if (tickdirmode_is ("auto"))
5053  tickdir.set (mode2d ? "in" : "out", true);
5054 
5055  double ticksign = (tickdir_is ("in") ? -1 : 1);
5056 
5057  Matrix bbox = get_boundingbox (true);
5058  Matrix ticklen = get_ticklength ().matrix_value ();
5059  ticklen(0) = ticklen(0) * std::max (bbox(2), bbox(3));
5060  ticklen(1) = ticklen(1) * std::max (bbox(2), bbox(3));
5061 
5062  xticklen = ticksign * (mode2d ? ticklen(0) : ticklen(1));
5063  yticklen = ticksign * (mode2d ? ticklen(0) : ticklen(1));
5064  zticklen = ticksign * (mode2d ? ticklen(0) : ticklen(1));
5065 
5066  xtickoffset = (mode2d ? std::max (0., xticklen) : std::abs (xticklen)) + 5;
5067  ytickoffset = (mode2d ? std::max (0., yticklen) : std::abs (yticklen)) + 5;
5068  ztickoffset = (mode2d ? std::max (0., zticklen) : std::abs (zticklen)) + 5;
5069 
5070  update_xlabel_position ();
5071  update_ylabel_position ();
5072  update_zlabel_position ();
5073  update_title_position ();
5074 }
5075 
5076 /*
5077 ## FIXME: A demo can't be called in a C++ file. This should be made a test
5078 ## or moved to a .m file where it can be called.
5079 %!demo
5080 %! clf;
5081 %! subplot (2,1,1);
5082 %! plot (rand (3));
5083 %! xlabel xlabel;
5084 %! ylabel ylabel;
5085 %! title title;
5086 %! subplot (2,1,2);
5087 %! plot (rand (3));
5088 %! set (gca, "ticklength", get (gca, "ticklength") * 2, "tickdir", "out");
5089 %! xlabel xlabel;
5090 %! ylabel ylabel;
5091 %! title title;
5092 */
5093 
5094 static bool updating_xlabel_position = false;
5095 
5096 void
5098 {
5099  if (updating_xlabel_position)
5100  return;
5101 
5102  text::properties& xlabel_props
5103  = reinterpret_cast<text::properties&>
5104  (gh_manager::get_object (get_xlabel ()).get_properties ());
5105 
5106  bool is_empty = xlabel_props.get_string ().is_empty ();
5107 
5108  unwind_protect frame;
5109  frame.protect_var (updating_xlabel_position);
5110  updating_xlabel_position = true;
5111 
5112  if (! is_empty)
5113  {
5114  if (xlabel_props.horizontalalignmentmode_is ("auto"))
5115  {
5116  xlabel_props.set_horizontalalignment
5117  (xstate > AXE_DEPTH_DIR
5118  ? "center" : (xyzSym ? "left" : "right"));
5119 
5120  xlabel_props.set_horizontalalignmentmode ("auto");
5121  }
5122 
5123  if (xlabel_props.verticalalignmentmode_is ("auto"))
5124  {
5125  xlabel_props.set_verticalalignment
5126  (xstate == AXE_VERT_DIR || x2Dtop ? "bottom" : "top");
5127 
5128  xlabel_props.set_verticalalignmentmode ("auto");
5129  }
5130  }
5131 
5132  if (xlabel_props.positionmode_is ("auto")
5133  || xlabel_props.rotationmode_is ("auto"))
5134  {
5135  graphics_xform xform = get_transform ();
5136 
5137  Matrix ext (1, 2, 0.0);
5138  ext = get_ticklabel_extents (get_xtick ().matrix_value (),
5139  get_xticklabel ().all_strings (),
5140  get_xlim ().matrix_value ());
5141 
5142  double wmax = ext(0), hmax = ext(1), angle = 0;
5143  ColumnVector p =
5144  graphics_xform::xform_vector ((xpTickN+xpTick)/2, ypTick, zpTick);
5145 
5146  bool tick_along_z = nearhoriz || xisinf (fy);
5147  if (tick_along_z)
5148  p(2) += (signum (zpTick-zpTickN)*fz*xtickoffset);
5149  else
5150  p(1) += (signum (ypTick-ypTickN)*fy*xtickoffset);
5151 
5152  p = xform.transform (p(0), p(1), p(2), false);
5153 
5154  switch (xstate)
5155  {
5156  case AXE_ANY_DIR:
5157  p(0) += (xyzSym ? wmax : -wmax);
5158  p(1) += hmax;
5159  break;
5160 
5161  case AXE_VERT_DIR:
5162  p(0) -= wmax;
5163  angle = 90;
5164  break;
5165 
5166  case AXE_HORZ_DIR:
5167  p(1) += (x2Dtop ? -hmax : hmax);
5168  break;
5169  }
5170 
5171  if (xlabel_props.positionmode_is ("auto"))
5172  {
5173  p = xform.untransform (p(0), p(1), p(2), true);
5174  xlabel_props.set_position (p.extract_n (0, 3).transpose ());
5175  xlabel_props.set_positionmode ("auto");
5176  }
5177 
5178  if (! is_empty && xlabel_props.rotationmode_is ("auto"))
5179  {
5180  xlabel_props.set_rotation (angle);
5181  xlabel_props.set_rotationmode ("auto");
5182  }
5183  }
5184 }
5185 
5186 static bool updating_ylabel_position = false;
5187 
5188 void
5190 {
5191  if (updating_ylabel_position)
5192  return;
5193 
5194  text::properties& ylabel_props
5195  = reinterpret_cast<text::properties&>
5196  (gh_manager::get_object (get_ylabel ()).get_properties ());
5197 
5198  bool is_empty = ylabel_props.get_string ().is_empty ();
5199 
5200  unwind_protect frame;
5201  frame.protect_var (updating_ylabel_position);
5202  updating_ylabel_position = true;
5203 
5204  if (! is_empty)
5205  {
5206  if (ylabel_props.horizontalalignmentmode_is ("auto"))
5207  {
5208  ylabel_props.set_horizontalalignment
5209  (ystate > AXE_DEPTH_DIR
5210  ? "center" : (!xyzSym ? "left" : "right"));
5211 
5212  ylabel_props.set_horizontalalignmentmode ("auto");
5213  }
5214 
5215  if (ylabel_props.verticalalignmentmode_is ("auto"))
5216  {
5217  ylabel_props.set_verticalalignment
5218  (ystate == AXE_VERT_DIR && !y2Dright ? "bottom" : "top");
5219 
5220  ylabel_props.set_verticalalignmentmode ("auto");
5221  }
5222  }
5223 
5224  if (ylabel_props.positionmode_is ("auto")
5225  || ylabel_props.rotationmode_is ("auto"))
5226  {
5227  graphics_xform xform = get_transform ();
5228 
5229  Matrix ext (1, 2, 0.0);
5230 
5231  // The underlying get_extents() from FreeType produces mismatched values.
5232  // x-extent accurately measures the width of the glyphs.
5233  // y-extent instead measures from baseline-to-baseline.
5234  // Pad x-extent (+4) so that it approximately matches y-extent.
5235  // This keeps ylabels about the same distance from y-axis as
5236  // xlabels are from x-axis.
5237  // ALWAYS use an even number for padding or horizontal alignment
5238  // will be off.
5239  ext = get_ticklabel_extents (get_ytick ().matrix_value (),
5240  get_yticklabel ().all_strings (),
5241  get_ylim ().matrix_value ());
5242 
5243  double wmax = ext(0)+4, hmax = ext(1), angle = 0;
5244  ColumnVector p =
5245  graphics_xform::xform_vector (xpTick, (ypTickN+ypTick)/2, zpTick);
5246 
5247  bool tick_along_z = nearhoriz || xisinf (fx);
5248  if (tick_along_z)
5249  p(2) += (signum (zpTick-zpTickN)*fz*ytickoffset);
5250  else
5251  p(0) += (signum (xpTick-xpTickN)*fx*ytickoffset);
5252 
5253  p = xform.transform (p(0), p(1), p(2), false);
5254 
5255  switch (ystate)
5256  {
5257  case AXE_ANY_DIR:
5258  p(0) += (!xyzSym ? wmax : -wmax);
5259  p(1) += hmax;
5260  break;
5261 
5262  case AXE_VERT_DIR:
5263  p(0) += (y2Dright ? wmax : -wmax);
5264  angle = 90;
5265  break;
5266 
5267  case AXE_HORZ_DIR:
5268  p(1) += hmax;
5269  break;
5270  }
5271 
5272  if (ylabel_props.positionmode_is ("auto"))
5273  {
5274  p = xform.untransform (p(0), p(1), p(2), true);
5275  ylabel_props.set_position (p.extract_n (0, 3).transpose ());
5276  ylabel_props.set_positionmode ("auto");
5277  }
5278 
5279  if (! is_empty && ylabel_props.rotationmode_is ("auto"))
5280  {
5281  ylabel_props.set_rotation (angle);
5282  ylabel_props.set_rotationmode ("auto");
5283  }
5284  }
5285 }
5286 
5287 static bool updating_zlabel_position = false;
5288 
5289 void
5291 {
5292  if (updating_zlabel_position)
5293  return;
5294 
5295  text::properties& zlabel_props
5296  = reinterpret_cast<text::properties&>
5297  (gh_manager::get_object (get_zlabel ()).get_properties ());
5298 
5299  bool camAuto = cameraupvectormode_is ("auto");
5300  bool is_empty = zlabel_props.get_string ().is_empty ();
5301 
5302  unwind_protect frame;
5303  frame.protect_var (updating_zlabel_position);
5304  updating_zlabel_position = true;
5305 
5306  if (! is_empty)
5307  {
5308  if (zlabel_props.horizontalalignmentmode_is ("auto"))
5309  {
5310  zlabel_props.set_horizontalalignment
5311  ((zstate > AXE_DEPTH_DIR || camAuto) ? "center" : "right");
5312 
5313  zlabel_props.set_horizontalalignmentmode ("auto");
5314  }
5315 
5316  if (zlabel_props.verticalalignmentmode_is ("auto"))
5317  {
5318  zlabel_props.set_verticalalignment
5319  (zstate == AXE_VERT_DIR
5320  ? "bottom" : ((zSign || camAuto) ? "bottom" : "top"));
5321 
5322  zlabel_props.set_verticalalignmentmode ("auto");
5323  }
5324  }
5325 
5326  if (zlabel_props.positionmode_is ("auto")
5327  || zlabel_props.rotationmode_is ("auto"))
5328  {
5329  graphics_xform xform = get_transform ();
5330 
5331  Matrix ext (1, 2, 0.0);
5332  ext = get_ticklabel_extents (get_ztick ().matrix_value (),
5333  get_zticklabel ().all_strings (),
5334  get_zlim ().matrix_value ());
5335 
5336  double wmax = ext(0), hmax = ext(1), angle = 0;
5337  ColumnVector p;
5338 
5339  if (xySym)
5340  {
5341  p = graphics_xform::xform_vector (xPlaneN, yPlane,
5342  (zpTickN+zpTick)/2);
5343  if (xisinf (fy))
5344  p(0) += (signum (xPlaneN-xPlane)*fx*ztickoffset);
5345  else
5346  p(1) += (signum (yPlane-yPlaneN)*fy*ztickoffset);
5347  }
5348  else
5349  {
5350  p = graphics_xform::xform_vector (xPlane, yPlaneN,
5351  (zpTickN+zpTick)/2);
5352  if (xisinf (fx))
5353  p(1) += (signum (yPlaneN-yPlane)*fy*ztickoffset);
5354  else
5355  p(0) += (signum (xPlane-xPlaneN)*fx*ztickoffset);
5356  }
5357 
5358  p = xform.transform (p(0), p(1), p(2), false);
5359 
5360  switch (zstate)
5361  {
5362  case AXE_ANY_DIR:
5363  if (camAuto)
5364  {
5365  p(0) -= wmax;
5366  angle = 90;
5367  }
5368 
5369  // FIXME: what's the correct offset?
5370  //
5371  // p[0] += (!xySym ? wmax : -wmax);
5372  // p[1] += (zSign ? hmax : -hmax);
5373 
5374  break;
5375 
5376  case AXE_VERT_DIR:
5377  p(0) -= wmax;
5378  angle = 90;
5379  break;
5380 
5381  case AXE_HORZ_DIR:
5382  p(1) += hmax;
5383  break;
5384  }
5385 
5386  if (zlabel_props.positionmode_is ("auto"))
5387  {
5388  p = xform.untransform (p(0), p(1), p(2), true);
5389  zlabel_props.set_position (p.extract_n (0, 3).transpose ());
5390  zlabel_props.set_positionmode ("auto");
5391  }
5392 
5393  if (! is_empty && zlabel_props.rotationmode_is ("auto"))
5394  {
5395  zlabel_props.set_rotation (angle);
5396  zlabel_props.set_rotationmode ("auto");
5397  }
5398  }
5399 }
5400 
5401 static bool updating_title_position = false;
5402 
5403 void
5405 {
5406  if (updating_title_position)
5407  return;
5408 
5409  text::properties& title_props
5410  = reinterpret_cast<text::properties&>
5411  (gh_manager::get_object (get_title ()).get_properties ());
5412 
5413  unwind_protect frame;
5414  frame.protect_var (updating_title_position);
5415  updating_title_position = true;
5416 
5417  if (title_props.positionmode_is ("auto"))
5418  {
5419  graphics_xform xform = get_transform ();
5420 
5421  // FIXME: bbox should be stored in axes::properties
5422  Matrix bbox = get_extent (false);
5423 
5424  ColumnVector p =
5425  graphics_xform::xform_vector (bbox(0)+bbox(2)/2,
5426  bbox(1)-10,
5427  (x_zlim(0)+x_zlim(1))/2);
5428 
5429  if (x2Dtop)
5430  {
5431  Matrix ext (1, 2, 0.0);
5432  ext = get_ticklabel_extents (get_xtick ().matrix_value (),
5433  get_xticklabel ().all_strings (),
5434  get_xlim ().matrix_value ());
5435  p(1) -= ext(1);
5436  }
5437 
5438  p = xform.untransform (p(0), p(1), p(2), true);
5439 
5440  title_props.set_position (p.extract_n (0, 3).transpose ());
5441  title_props.set_positionmode ("auto");
5442  }
5443 }
5444 
5445 void
5446 axes::properties::update_autopos (const std::string& elem_type)
5447 {
5448  if (elem_type == "xlabel")
5449  update_xlabel_position ();
5450  else if (elem_type == "ylabel")
5451  update_ylabel_position ();
5452  else if (elem_type == "zlabel")
5453  update_zlabel_position ();
5454  else if (elem_type == "title")
5455  update_title_position ();
5456  else if (elem_type == "sync")
5457  sync_positions ();
5458 }
5459 
5460 static void
5461 normalized_aspectratios (Matrix& aspectratios, const Matrix& scalefactors,
5462  double xlength, double ylength, double zlength)
5463 {
5464  double xval = xlength/scalefactors(0);
5465  double yval = ylength/scalefactors(1);
5466  double zval = zlength/scalefactors(2);
5467 
5468  double minval = xmin (xmin (xval, yval), zval);
5469 
5470  aspectratios(0) = xval/minval;
5471  aspectratios(1) = yval/minval;
5472  aspectratios(2) = zval/minval;
5473 }
5474 
5475 static void
5476 max_axes_scale (double& s, Matrix& limits, const Matrix& kids,
5477  double pbfactor, double dafactor, char limit_type, bool tight)
5478 {
5479  if (tight)
5480  {
5481  double minval = octave_Inf;
5482  double maxval = -octave_Inf;
5483  double min_pos = octave_Inf;
5484  double max_neg = -octave_Inf;
5485  get_children_limits (minval, maxval, min_pos, max_neg, kids, limit_type);
5486  if (xfinite (minval) && xfinite (maxval))
5487  {
5488  limits(0) = minval;
5489  limits(1) = maxval;
5490  s = xmax(s, (maxval - minval) / (pbfactor * dafactor));
5491  }
5492  }
5493  else
5494  s = xmax(s, (limits(1) - limits(0)) / (pbfactor * dafactor));
5495 }
5496 
5497 static bool updating_aspectratios = false;
5498 
5499 void
5501 {
5502  if (updating_aspectratios)
5503  return;
5504 
5505  Matrix xlimits = get_xlim ().matrix_value ();
5506  Matrix ylimits = get_ylim ().matrix_value ();
5507  Matrix zlimits = get_zlim ().matrix_value ();
5508 
5509  double dx = (xlimits(1)-xlimits(0));
5510  double dy = (ylimits(1)-ylimits(0));
5511  double dz = (zlimits(1)-zlimits(0));
5512 
5513  Matrix da = get_dataaspectratio ().matrix_value ();
5514  Matrix pba = get_plotboxaspectratio ().matrix_value ();
5515 
5516  if (dataaspectratiomode_is ("auto"))
5517  {
5518  if (plotboxaspectratiomode_is ("auto"))
5519  {
5520  pba = Matrix (1, 3, 1.0);
5521  plotboxaspectratio.set (pba, false);
5522  }
5523 
5524  normalized_aspectratios (da, pba, dx, dy, dz);
5525  dataaspectratio.set (da, false);
5526  }
5527  else if (plotboxaspectratiomode_is ("auto"))
5528  {
5529  normalized_aspectratios (pba, da, dx, dy, dz);
5530  plotboxaspectratio.set (pba, false);
5531  }
5532  else
5533  {
5534  double s = -octave_Inf;
5535  bool modified_limits = false;
5536  Matrix kids;
5537 
5538  if (xlimmode_is ("auto") && ylimmode_is ("auto") && zlimmode_is ("auto"))
5539  {
5540  modified_limits = true;
5541  kids = get_children ();
5542  max_axes_scale (s, xlimits, kids, pba(0), da(0), 'x', true);
5543  max_axes_scale (s, ylimits, kids, pba(1), da(1), 'y', true);
5544  max_axes_scale (s, zlimits, kids, pba(2), da(2), 'z', true);
5545  }
5546  else if (xlimmode_is ("auto") && ylimmode_is ("auto"))
5547  {
5548  modified_limits = true;
5549  max_axes_scale (s, zlimits, kids, pba(2), da(2), 'z', false);
5550  }
5551  else if (ylimmode_is ("auto") && zlimmode_is ("auto"))
5552  {
5553  modified_limits = true;
5554  max_axes_scale (s, xlimits, kids, pba(0), da(0), 'x', false);
5555  }
5556  else if (zlimmode_is ("auto") && xlimmode_is ("auto"))
5557  {
5558  modified_limits = true;
5559  max_axes_scale (s, ylimits, kids, pba(1), da(1), 'y', false);
5560  }
5561 
5562  if (modified_limits)
5563  {
5564 
5565  unwind_protect frame;
5566  frame.protect_var (updating_aspectratios);
5567 
5568  updating_aspectratios = true;
5569 
5570  dx = pba(0) *da(0);
5571  dy = pba(1) *da(1);
5572  dz = pba(2) *da(2);
5573  if (xisinf (s))
5574  s = 1 / xmin (xmin (dx, dy), dz);
5575 
5576  if (xlimmode_is ("auto"))
5577  {
5578  dx = s * dx;
5579  xlimits(0) = 0.5 * (xlimits(0) + xlimits(1) - dx);
5580  xlimits(1) = xlimits(0) + dx;
5581  set_xlim (xlimits);
5582  set_xlimmode ("auto");
5583  }
5584 
5585  if (ylimmode_is ("auto"))
5586  {
5587  dy = s * dy;
5588  ylimits(0) = 0.5 * (ylimits(0) + ylimits(1) - dy);
5589  ylimits(1) = ylimits(0) + dy;
5590  set_ylim (ylimits);
5591  set_ylimmode ("auto");
5592  }
5593 
5594  if (zlimmode_is ("auto"))
5595  {
5596  dz = s * dz;
5597  zlimits(0) = 0.5 * (zlimits(0) + zlimits(1) - dz);
5598  zlimits(1) = zlimits(0) + dz;
5599  set_zlim (zlimits);
5600  set_zlimmode ("auto");
5601  }
5602  }
5603  else
5604  {
5605  normalized_aspectratios (pba, da, dx, dy, dz);
5606  plotboxaspectratio.set (pba, false);
5607  }
5608  }
5609 }
5610 
5611 void
5613 {
5614 #ifdef HAVE_FREETYPE
5615 #ifdef HAVE_FONTCONFIG
5616  text_renderer.set_font (get ("fontname").string_value (),
5617  get ("fontweight").string_value (),
5618  get ("fontangle").string_value (),
5619  get ("fontsize").double_value ());
5620 #endif
5621 #endif
5622 }
5623 
5624 // The INTERNAL flag defines whether position or outerposition is used.
5625 
5626 Matrix
5628  const Matrix& parent_pix_size) const
5629 {
5630  Matrix pos = internal ? get_position ().matrix_value ()
5631  : get_outerposition ().matrix_value ();
5632  Matrix parent_size (parent_pix_size);
5633 
5634  if (parent_size.numel () == 0)
5635  {
5637 
5638  if (obj.valid_object ())
5639  parent_size =
5640  obj.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
5641  else
5642  parent_size = default_figure_position ();
5643  }
5644 
5645  pos = convert_position (pos, get_units (), "pixels", parent_size);
5646 
5647  pos(0)--;
5648  pos(1)--;
5649  pos(1) = parent_size(1) - pos(1) - pos(3);
5650 
5651  return pos;
5652 }
5653 
5654 Matrix
5655 axes::properties::get_extent (bool with_text, bool only_text_height) const
5656 {
5657  graphics_xform xform = get_transform ();
5658 
5659  Matrix ext (1, 4, 0.0);
5660  ext(0) = octave_Inf;
5661  ext(1) = octave_Inf;
5662  ext(2) = -octave_Inf;
5663  ext(3) = -octave_Inf;
5664  for (int i = 0; i <= 1; i++)
5665  for (int j = 0; j <= 1; j++)
5666  for (int k = 0; k <= 1; k++)
5667  {
5668  ColumnVector p = xform.transform (i ? xPlaneN : xPlane,
5669  j ? yPlaneN : yPlane,
5670  k ? zPlaneN : zPlane, false);
5671  ext(0) = std::min (ext(0), p(0));
5672  ext(1) = std::min (ext(1), p(1));
5673  ext(2) = std::max (ext(2), p(0));
5674  ext(3) = std::max (ext(3), p(1));
5675  }
5676 
5677  if (with_text)
5678  {
5679  for (int i = 0; i < 4; i++)
5680  {
5681  graphics_handle text_handle;
5682  if (i == 0)
5683  text_handle = get_title ();
5684  else if (i == 1)
5685  text_handle = get_xlabel ();
5686  else if (i == 2)
5687  text_handle = get_ylabel ();
5688  else if (i == 3)
5689  text_handle = get_zlabel ();
5690 
5691  text::properties& text_props
5692  = reinterpret_cast<text::properties&>
5693  (gh_manager::get_object (text_handle).get_properties ());
5694 
5695  Matrix text_pos = text_props.get_data_position ();
5696  text_pos = xform.transform (text_pos(0), text_pos(1), text_pos(2));
5697  if (text_props.get_string ().is_empty ())
5698  {
5699  ext(0) = std::min (ext(0), text_pos(0));
5700  ext(1) = std::min (ext(1), text_pos(1));
5701  ext(2) = std::max (ext(2), text_pos(0));
5702  ext(3) = std::max (ext(3), text_pos(1));
5703  }
5704  else
5705  {
5706  Matrix text_ext = text_props.get_extent_matrix ();
5707 
5708  bool ignore_horizontal = false;
5709  bool ignore_vertical = false;
5710  if (only_text_height)
5711  {
5712  double text_rotation = text_props.get_rotation ();
5713  if (text_rotation == 0. || text_rotation == 180.)
5714  ignore_horizontal = true;
5715  else if (text_rotation == 90. || text_rotation == 270.)
5716  ignore_vertical = true;
5717  }
5718 
5719  if (! ignore_horizontal)
5720  {
5721  ext(0) = std::min (ext(0), text_pos(0)+text_ext(0));
5722  ext(2) = std::max (ext(2),
5723  text_pos(0)+text_ext(0)+text_ext(2));
5724  }
5725 
5726  if (! ignore_vertical)
5727  {
5728  ext(1) = std::min (ext(1),
5729  text_pos(1)-text_ext(1)-text_ext(3));
5730  ext(3) = std::max (ext(3), text_pos(1)-text_ext(1));
5731  }
5732  }
5733  }
5734  }
5735 
5736  ext(2) = ext(2)-ext(0);
5737  ext(3) = ext(3)-ext(1);
5738 
5739  return ext;
5740 }
5741 
5742 static octave_value
5744 {
5745  octave_value retval = val;
5746 
5747  if (val.is_cellstr ())
5748  {
5749  // Always return a column vector for Matlab Compatibility
5750  if (val.columns () > 1)
5751  retval = val.reshape (dim_vector (val.numel (), 1));
5752  }
5753  else
5754  {
5755  string_vector sv;
5756  if (val.is_numeric_type ())
5757  {
5758  NDArray data = val.array_value ();
5759  std::ostringstream oss;
5760  oss.precision (5);
5761  for (octave_idx_type i = 0; i < val.numel (); i++)
5762  {
5763  oss.str ("");
5764  oss << data(i);
5765  sv.append (oss.str ());
5766  }
5767  }
5768  else if (val.is_string () && val.rows () == 1)
5769  {
5770  std::string valstr = val.string_value ();
5771  std::istringstream iss (valstr);
5772  std::string tmpstr;
5773 
5774  // Split string with delimiter '|'
5775  while (std::getline (iss, tmpstr, '|'))
5776  sv.append (tmpstr);
5777 
5778  // If string ends with '|' Matlab appends a null string
5779  if (*valstr.rbegin () == '|')
5780  sv.append (std::string (""));
5781  }
5782  else
5783  return retval;
5784 
5785  charMatrix chmat (sv, ' ');
5786 
5787  retval = octave_value (chmat);
5788  }
5789 
5790  return retval;
5791 }
5792 
5793 void
5795 {
5796  if (!error_state)
5797  {
5798  if (xticklabel.set (convert_ticklabel_string (v), false))
5799  {
5800  set_xticklabelmode ("manual");
5801  xticklabel.run_listeners (POSTSET);
5802  mark_modified ();
5803  }
5804  else
5805  set_xticklabelmode ("manual");
5806  }
5807 }
5808 
5809 void
5811 {
5812  if (!error_state)
5813  {
5814  if (yticklabel.set (convert_ticklabel_string (v), false))
5815  {
5816  set_yticklabelmode ("manual");
5817  yticklabel.run_listeners (POSTSET);
5818  mark_modified ();
5819  }
5820  else
5821  set_yticklabelmode ("manual");
5822  }
5823 }
5824 
5825 void
5827 {
5828  if (!error_state)
5829  {
5830  if (zticklabel.set (convert_ticklabel_string (v), false))
5831  {
5832  set_zticklabelmode ("manual");
5833  zticklabel.run_listeners (POSTSET);
5834  mark_modified ();
5835  }
5836  else
5837  set_zticklabelmode ("manual");
5838  }
5839 }
5840 
5841 // Almost identical to convert_ticklabel_string but it only accepts
5842 // cellstr or string, not numeric input.
5843 static octave_value
5845 {
5846  octave_value retval = val;
5847 
5848  if (val.is_cellstr ())
5849  {
5850  // Always return a column vector for Matlab Compatibility
5851  if (val.columns () > 1)
5852  retval = val.reshape (dim_vector (val.numel (), 1));
5853  }
5854  else
5855  {
5856  string_vector sv;
5857  if (val.is_string () && val.rows () == 1)
5858  {
5859  std::string valstr = val.string_value ();
5860  std::istringstream iss (valstr);
5861  std::string tmpstr;
5862 
5863  // Split string with delimiter '|'
5864  while (std::getline (iss, tmpstr, '|'))
5865  sv.append (tmpstr);
5866 
5867  // If string ends with '|' Matlab appends a null string
5868  if (*valstr.rbegin () == '|')
5869  sv.append (std::string (""));
5870  }
5871  else
5872  return retval;
5873 
5874  charMatrix chmat (sv, ' ');
5875 
5876  retval = octave_value (chmat);
5877  }
5878 
5879  return retval;
5880 }
5881 
5882 void
5884 {
5885  if (!error_state)
5886  {
5887  linestyleorder.set (convert_linestyleorder_string (v), false);
5888  }
5889 }
5890 
5891 void
5893 {
5894  if (! error_state)
5895  {
5896  caseless_str old_units = get_units ();
5897  if (units.set (v, true))
5898  {
5899  update_units (old_units);
5900  mark_modified ();
5901  }
5902  }
5903 }
5904 
5905 void
5907 {
5909  Matrix parent_bb
5910  = obj.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
5911  caseless_str new_units = get_units ();
5912  position.set (octave_value (convert_position (get_position ().matrix_value (),
5913  old_units, new_units,
5914  parent_bb)),
5915  false);
5916  outerposition.set (octave_value (convert_position (get_outerposition ().matrix_value (),
5917  old_units, new_units,
5918  parent_bb)),
5919  false);
5920  tightinset.set (octave_value (convert_position (get_tightinset ().matrix_value (),
5921  old_units, new_units,
5922  parent_bb)),
5923  false);
5924  looseinset.set (octave_value (convert_position (get_looseinset ().matrix_value (),
5925  old_units, new_units,
5926  parent_bb)),
5927  false);
5928 }
5929 
5930 void
5932 {
5933  if (! error_state)
5934  {
5935  caseless_str old_fontunits = get_fontunits ();
5936  if (fontunits.set (v, true))
5937  {
5938  update_fontunits (old_fontunits);
5939  mark_modified ();
5940  }
5941  }
5942 }
5943 
5944 void
5946 {
5947  caseless_str new_units = get_fontunits ();
5948  double parent_height = get_boundingbox (true).elem (3);
5949  double fsz = get_fontsize ();
5950 
5951  fsz = convert_font_size (fsz, old_units, new_units, parent_height);
5952 
5953  set_fontsize (octave_value (fsz));
5954 }
5955 
5956 double
5957 axes::properties::get_fontsize_points (double box_pix_height) const
5958 {
5959  double fs = get_fontsize ();
5960  double parent_height = box_pix_height;
5961 
5962  if (fontunits_is ("normalized") && parent_height <= 0)
5963  parent_height = get_boundingbox (true).elem (3);
5964 
5965  return convert_font_size (fs, get_fontunits (), "points", parent_height);
5966 }
5967 
5969 graphics_xform::xform_vector (double x, double y, double z)
5970 {
5971  return ::xform_vector (x, y, z);
5972 }
5973 
5974 Matrix
5976 {
5978 }
5979 
5981 graphics_xform::transform (double x, double y, double z,
5982  bool use_scale) const
5983 {
5984  if (use_scale)
5985  {
5986  x = sx.scale (x);
5987  y = sy.scale (y);
5988  z = sz.scale (z);
5989  }
5990 
5991  return ::transform (xform, x, y, z);
5992 }
5993 
5995 graphics_xform::untransform (double x, double y, double z,
5996  bool use_scale) const
5997 {
5998  ColumnVector v = ::transform (xform_inv, x, y, z);
5999 
6000  if (use_scale)
6001  {
6002  v(0) = sx.unscale (v(0));
6003  v(1) = sy.unscale (v(1));
6004  v(2) = sz.unscale (v(2));
6005  }
6006 
6007  return v;
6008 }
6009 
6011 axes::get_default (const caseless_str& name) const
6012 {
6013  octave_value retval = default_properties.lookup (name);
6014 
6015  if (retval.is_undefined ())
6016  {
6017  graphics_handle parent = get_parent ();
6018  graphics_object parent_obj = gh_manager::get_object (parent);
6019 
6020  retval = parent_obj.get_default (name);
6021  }
6022 
6023  return retval;
6024 }
6025 
6026 // FIXME: remove.
6027 // FIXME: maybe this should go into array_property class?
6028 /*
6029 static void
6030 check_limit_vals (double& min_val, double& max_val,
6031  double& min_pos, double& max_neg,
6032  const array_property& data)
6033 {
6034  double val = data.min_val ();
6035  if (xfinite (val) && val < min_val)
6036  min_val = val;
6037  val = data.max_val ();
6038  if (xfinite (val) && val > max_val)
6039  max_val = val;
6040  val = data.min_pos ();
6041  if (xfinite (val) && val > 0 && val < min_pos)
6042  min_pos = val;
6043  val = data.max_neg ();
6044  if (xfinite (val) && val < 0 && val > max_neg)
6045  max_neg = val;
6046 }
6047 */
6048 
6049 static void
6050 check_limit_vals (double& min_val, double& max_val,
6051  double& min_pos, double& max_neg,
6052  const octave_value& data)
6053 {
6054  if (data.is_matrix_type ())
6055  {
6056  Matrix m = data.matrix_value ();
6057 
6058  if (! error_state && m.numel () == 4)
6059  {
6060  double val;
6061 
6062  val = m(0);
6063  if (xfinite (val) && val < min_val)
6064  min_val = val;
6065 
6066  val = m(1);
6067  if (xfinite (val) && val > max_val)
6068  max_val = val;
6069 
6070  val = m(2);
6071  if (xfinite (val) && val > 0 && val < min_pos)
6072  min_pos = val;
6073 
6074  val = m(3);
6075  if (xfinite (val) && val < 0 && val > max_neg)
6076  max_neg = val;
6077  }
6078  }
6079 }
6080 
6081 // magform(x) Returns (a, b), where x = a * 10^b, abs (a) >= 1., and b is
6082 // integer.
6083 
6084 static void
6085 magform (double x, double& a, int& b)
6086 {
6087  if (x == 0)
6088  {
6089  a = 0;
6090  b = 0;
6091  }
6092  else
6093  {
6094  b = static_cast<int> (gnulib::floor (std::log10 (std::abs (x))));
6095  a = x / std::pow (10.0, b);
6096  }
6097 }
6098 
6099 // A translation from Tom Holoryd's python code at
6100 // http://kurage.nimh.nih.gov/tomh/tics.py
6101 // FIXME: add log ticks
6102 
6103 double
6104 axes::properties::calc_tick_sep (double lo, double hi)
6105 {
6106  int ticint = 5;
6107 
6108  // Reference: Lewart, C. R., "Algorithms SCALE1, SCALE2, and
6109  // SCALE3 for Determination of Scales on Computer Generated
6110  // Plots", Communications of the ACM, 10 (1973), 639-640.
6111  // Also cited as ACM Algorithm 463.
6112 
6113  double a;
6114  int b, x;
6115 
6116  magform ((hi-lo)/ticint, a, b);
6117 
6118  static const double sqrt_2 = sqrt (2.0);
6119  static const double sqrt_10 = sqrt (10.0);
6120  static const double sqrt_50 = sqrt (50.0);
6121 
6122  if (a < sqrt_2)
6123  x = 1;
6124  else if (a < sqrt_10)
6125  x = 2;
6126  else if (a < sqrt_50)
6127  x = 5;
6128  else
6129  x = 10;
6130 
6131  return x * std::pow (10., b);
6132 
6133 }
6134 
6135 // Attempt to make "nice" limits from the actual max and min of the
6136 // data. For log plots, we will also use the smallest strictly positive
6137 // value.
6138 
6139 Matrix
6141  double min_pos, double max_neg,
6142  bool logscale)
6143 {
6144  Matrix retval;
6145 
6146  double min_val = xmin;
6147  double max_val = xmax;
6148 
6149  if (xisinf (min_val) && min_val > 0 && xisinf (max_val) && max_val < 0)
6150  {
6151  retval = default_lim (logscale);
6152  return retval;
6153  }
6154  else if (! (xisinf (min_val) || xisinf (max_val)))
6155  {
6156  if (logscale)
6157  {
6158  if (xisinf (min_pos) && xisinf (max_neg))
6159  {
6160  // TODO -- max_neg is needed for "loglog ([0 -Inf])"
6161  // This is the only place where max_neg is needed.
6162  // Is there another way?
6163  retval = default_lim ();
6164  retval(0) = pow (10., retval(0));
6165  retval(1) = pow (10., retval(1));
6166  return retval;
6167  }
6168  if ((min_val <= 0 && max_val > 0))
6169  {
6170  warning ("axis: omitting non-positive data in log plot");
6171  min_val = min_pos;
6172  }
6173  // FIXME: maybe this test should also be relative?
6174  if (std::abs (min_val - max_val)
6175  < sqrt (std::numeric_limits<double>::epsilon ()))
6176  {
6177  // Widen range when too small
6178  if (min_val >= 0)
6179  {
6180  min_val *= 0.9;
6181  max_val *= 1.1;
6182  }
6183  else
6184  {
6185  min_val *= 1.1;
6186  max_val *= 0.9;
6187  }
6188  }
6189  if (min_val > 0)
6190  {
6191  // Log plots with all positive data
6192  min_val = pow (10, gnulib::floor (log10 (min_val)));
6193  max_val = pow (10, std::ceil (log10 (max_val)));
6194  }
6195  else
6196  {
6197  // Log plots with all negative data
6198  min_val = -pow (10, std::ceil (log10 (-min_val)));
6199  max_val = -pow (10, gnulib::floor (log10 (-max_val)));
6200  }
6201  }
6202  else
6203  {
6204  if (min_val == 0 && max_val == 0)
6205  {
6206  min_val = -1;
6207  max_val = 1;
6208  }
6209  // FIXME: maybe this test should also be relative?
6210  else if (std::abs (min_val - max_val)
6211  < sqrt (std::numeric_limits<double>::epsilon ()))
6212  {
6213  min_val -= 0.1 * std::abs (min_val);
6214  max_val += 0.1 * std::abs (max_val);
6215  }
6216 
6217  double tick_sep = calc_tick_sep (min_val , max_val);
6218  double min_tick = gnulib::floor (min_val / tick_sep);
6219  double max_tick = std::ceil (max_val / tick_sep);
6220  // Prevent round-off from cropping ticks
6221  min_val = std::min (min_val, tick_sep * min_tick);
6222  max_val = std::max (max_val, tick_sep * max_tick);
6223  }
6224  }
6225 
6226  retval.resize (1, 2);
6227 
6228  retval(1) = max_val;
6229  retval(0) = min_val;
6230 
6231  return retval;
6232 }
6233 
6234 void
6236  array_property& ticks,
6237  array_property& mticks,
6238  bool limmode_is_auto, bool is_logscale)
6239 {
6240  // FIXME: add log ticks and lims
6241 
6242  if (lims.get ().is_empty ())
6243  return;
6244 
6245  double lo = (lims.get ().matrix_value ()) (0);
6246  double hi = (lims.get ().matrix_value ()) (1);
6247  bool is_negative = lo < 0 && hi < 0;
6248  double tmp;
6249  // FIXME: should this be checked for somewhere else? (i.e. set{x,y,z}lim)
6250  if (hi < lo)
6251  {
6252  tmp = hi;
6253  hi = lo;
6254  lo = tmp;
6255  }
6256 
6257  if (is_logscale)
6258  {
6259  if (is_negative)
6260  {
6261  tmp = hi;
6262  hi = std::log10 (-lo);
6263  lo = std::log10 (-tmp);
6264  }
6265  else
6266  {
6267  hi = std::log10 (hi);
6268  lo = std::log10 (lo);
6269  }
6270  }
6271 
6272  double tick_sep = calc_tick_sep (lo , hi);
6273 
6274  if (is_logscale && ! (xisinf (hi) || xisinf (lo)))
6275  {
6276  // FIXME: what if (hi-lo) < tick_sep?
6277  // ex: loglog ([1 1.1])
6278  tick_sep = std::max (tick_sep, 1.);
6279  tick_sep = std::ceil (tick_sep);
6280  }
6281 
6282  int i1 = static_cast<int> (gnulib::floor (lo / tick_sep));
6283  int i2 = static_cast<int> (std::ceil (hi / tick_sep));
6284 
6285  if (limmode_is_auto)
6286  {
6287  // adjust limits to include min and max tics
6288  Matrix tmp_lims (1,2);
6289  tmp_lims(0) = std::min (tick_sep * i1, lo);
6290  tmp_lims(1) = std::max (tick_sep * i2, hi);
6291 
6292  if (is_logscale)
6293  {
6294  tmp_lims(0) = std::pow (10.,tmp_lims(0));
6295  tmp_lims(1) = std::pow (10.,tmp_lims(1));
6296  if (tmp_lims(0) <= 0)
6297  tmp_lims(0) = std::pow (10., lo);
6298  if (is_negative)
6299  {
6300  tmp = tmp_lims(0);
6301  tmp_lims(0) = -tmp_lims(1);
6302  tmp_lims(1) = -tmp;
6303  }
6304  }
6305  lims = tmp_lims;
6306  }
6307 
6308  Matrix tmp_ticks (1, i2-i1+1);
6309  for (int i = 0; i <= i2-i1; i++)
6310  {
6311  tmp_ticks (i) = tick_sep * (i+i1);
6312  if (is_logscale)
6313  tmp_ticks (i) = std::pow (10., tmp_ticks (i));
6314  }
6315  if (is_logscale && is_negative)
6316  {
6317  Matrix rev_ticks (1, i2-i1+1);
6318  rev_ticks = -tmp_ticks;
6319  for (int i = 0; i <= i2-i1; i++)
6320  tmp_ticks (i) = rev_ticks (i2-i1-i);
6321  }
6322 
6323  ticks = tmp_ticks;
6324 
6325  int n = is_logscale ? 8 : 4;
6326  Matrix tmp_mticks (1, n * (tmp_ticks.numel () - 1));
6327 
6328  for (int i = 0; i < tmp_ticks.numel ()-1; i++)
6329  {
6330  double d = (tmp_ticks (i+1) - tmp_ticks (i)) / (n+1);
6331  for (int j = 0; j < n; j++)
6332  {
6333  tmp_mticks (n*i+j) = tmp_ticks (i) + d * (j+1);
6334  }
6335  }
6336  mticks = tmp_mticks;
6337 }
6338 
6339 void
6341  any_property& labels, bool logscale)
6342 {
6343  Matrix values = ticks.get ().matrix_value ();
6344  Cell c (values.dims ());
6345  std::ostringstream os;
6346 
6347  if (logscale)
6348  {
6349  double significand;
6350  double exponent;
6351  double exp_max = 0.;
6352  double exp_min = 0.;
6353 
6354  for (int i = 0; i < values.numel (); i++)
6355  {
6356  exp_max = std::max (exp_max, std::log10 (values(i)));
6357  exp_min = std::max (exp_min, std::log10 (values(i)));
6358  }
6359 
6360  for (int i = 0; i < values.numel (); i++)
6361  {
6362  if (values(i) < 0.)
6363  exponent = gnulib::floor (std::log10 (-values(i)));
6364  else
6365  exponent = gnulib::floor (std::log10 (values(i)));
6366  significand = values(i) * std::pow (10., -exponent);
6367  os.str (std::string ());
6368  os << significand;
6369  if (exponent < 0.)
6370  {
6371  os << "e-";
6372  exponent = -exponent;
6373  }
6374  else
6375  os << "e+";
6376  if (exponent < 10. && (exp_max > 9 || exp_min < -9))
6377  os << "0";
6378  os << exponent;
6379  c(i) = os.str ();
6380  }
6381  }
6382  else
6383  {
6384  for (int i = 0; i < values.numel (); i++)
6385  {
6386  os.str (std::string ());
6387  os << values(i);
6388  c(i) = os.str ();
6389  }
6390  }
6391 
6392  labels = c;
6393 }
6394 
6395 Matrix
6397  const string_vector& ticklabels,
6398  const Matrix& limits)
6399 {
6400 #ifndef HAVE_FREETYPE
6401  double fontsize = get ("fontsize").double_value ();
6402 #endif
6403 
6404  Matrix ext (1, 2, 0.0);
6405  double wmax = 0., hmax = 0.;
6406  int n = std::min (ticklabels.numel (), ticks.numel ());
6407  for (int i = 0; i < n; i++)
6408  {
6409  double val = ticks(i);
6410  if (limits(0) <= val && val <= limits(1))
6411  {
6412  std::string label (ticklabels(i));
6413  label.erase (0, label.find_first_not_of (" "));
6414  label = label.substr (0, label.find_last_not_of (" ")+1);
6415 #ifdef HAVE_FREETYPE
6416  ext = text_renderer.get_extent (label, 0.0, "none");
6417  wmax = std::max (wmax, ext(0));
6418  hmax = std::max (hmax, ext(1));
6419 #else
6420  // FIXME: find a better approximation
6421  int len = label.length ();
6422  wmax = std::max (wmax, 0.5*fontsize*len);
6423  hmax = fontsize;
6424 #endif
6425  }
6426  }
6427 
6428  ext(0) = wmax;
6429  ext(1) = hmax;
6430  return ext;
6431 }
6432 
6433 void
6434 get_children_limits (double& min_val, double& max_val,
6435  double& min_pos, double& max_neg,
6436  const Matrix& kids, char limit_type)
6437 {
6438  octave_idx_type n = kids.numel ();
6439 
6440  switch (limit_type)
6441  {
6442  case 'x':
6443  for (octave_idx_type i = 0; i < n; i++)
6444  {
6445  graphics_object obj = gh_manager::get_object (kids(i));
6446 
6447  if (obj.is_xliminclude ())
6448  {
6449  octave_value lim = obj.get_xlim ();
6450 
6451  check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
6452  }
6453  }
6454  break;
6455 
6456  case 'y':
6457  for (octave_idx_type i = 0; i < n; i++)
6458  {
6459  graphics_object obj = gh_manager::get_object (kids(i));
6460 
6461  if (obj.is_yliminclude ())
6462  {
6463  octave_value lim = obj.get_ylim ();
6464 
6465  check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
6466  }
6467  }
6468  break;
6469 
6470  case 'z':
6471  for (octave_idx_type i = 0; i < n; i++)
6472  {
6473  graphics_object obj = gh_manager::get_object (kids(i));
6474 
6475  if (obj.is_zliminclude ())
6476  {
6477  octave_value lim = obj.get_zlim ();
6478 
6479  check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
6480  }
6481  }
6482  break;
6483 
6484  case 'c':
6485  for (octave_idx_type i = 0; i < n; i++)
6486  {
6487  graphics_object obj = gh_manager::get_object (kids(i));
6488 
6489  if (obj.is_climinclude ())
6490  {
6491  octave_value lim = obj.get_clim ();
6492 
6493  check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
6494  }
6495  }
6496  break;
6497 
6498  case 'a':
6499  for (octave_idx_type i = 0; i < n; i++)
6500  {
6501  graphics_object obj = gh_manager::get_object (kids(i));
6502 
6503  if (obj.is_aliminclude ())
6504  {
6505  octave_value lim = obj.get_alim ();
6506 
6507  check_limit_vals (min_val, max_val, min_pos, max_neg, lim);
6508  }
6509  }
6510  break;
6511 
6512  default:
6513  break;
6514  }
6515 }
6516 
6517 static bool updating_axis_limits = false;
6518 
6519 void
6520 axes::update_axis_limits (const std::string& axis_type,
6521  const graphics_handle& h)
6522 {
6523  if (updating_axis_limits)
6524  return;
6525 
6526  Matrix kids = Matrix (1, 1, h.value ());
6527 
6528  double min_val = octave_Inf;
6529  double max_val = -octave_Inf;
6530  double min_pos = octave_Inf;
6531  double max_neg = -octave_Inf;
6532 
6533  char update_type = 0;
6534 
6535  Matrix limits;
6536  double val;
6537 
6538 #define FIX_LIMITS \
6539  if (limits.numel () == 4) \
6540  { \
6541  val = limits(0); \
6542  if (xfinite (val)) \
6543  min_val = val; \
6544  val = limits(1); \
6545  if (xfinite (val)) \
6546  max_val = val; \
6547  val = limits(2); \
6548  if (xfinite (val)) \
6549  min_pos = val; \
6550  val = limits(3); \
6551  if (xfinite (val)) \
6552  max_neg = val; \
6553  } \
6554  else \
6555  { \
6556  limits.resize (4, 1); \
6557  limits(0) = min_val; \
6558  limits(1) = max_val; \
6559  limits(2) = min_pos; \
6560  limits(3) = max_neg; \
6561  }
6562 
6563  if (axis_type == "xdata" || axis_type == "xscale"
6564  || axis_type == "xlimmode" || axis_type == "xliminclude"
6565  || axis_type == "xlim")
6566  {
6567  if (xproperties.xlimmode_is ("auto"))
6568  {
6569  limits = xproperties.get_xlim ().matrix_value ();
6570  FIX_LIMITS ;
6571 
6572  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'x');
6573 
6574  limits = xproperties.get_axis_limits (min_val, max_val,
6575  min_pos, max_neg,
6576  xproperties.xscale_is ("log"));
6577 
6578  update_type = 'x';
6579  }
6580  }
6581  else if (axis_type == "ydata" || axis_type == "yscale"
6582  || axis_type == "ylimmode" || axis_type == "yliminclude"
6583  || axis_type == "ylim")
6584  {
6585  if (xproperties.ylimmode_is ("auto"))
6586  {
6587  limits = xproperties.get_ylim ().matrix_value ();
6588  FIX_LIMITS ;
6589 
6590  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'y');
6591 
6592  limits = xproperties.get_axis_limits (min_val, max_val,
6593  min_pos, max_neg,
6594  xproperties.yscale_is ("log"));
6595 
6596  update_type = 'y';
6597  }
6598  }
6599  else if (axis_type == "zdata" || axis_type == "zscale"
6600  || axis_type == "zlimmode" || axis_type == "zliminclude"
6601  || axis_type == "zlim")
6602  {
6603  if (xproperties.zlimmode_is ("auto"))
6604  {
6605  limits = xproperties.get_zlim ().matrix_value ();
6606  FIX_LIMITS ;
6607 
6608  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'z');
6609 
6610  limits = xproperties.get_axis_limits (min_val, max_val,
6611  min_pos, max_neg,
6612  xproperties.zscale_is ("log"));
6613 
6614  update_type = 'z';
6615  }
6616  }
6617  else if (axis_type == "cdata" || axis_type == "climmode"
6618  || axis_type == "cdatamapping" || axis_type == "climinclude"
6619  || axis_type == "clim")
6620  {
6621  if (xproperties.climmode_is ("auto"))
6622  {
6623  limits = xproperties.get_clim ().matrix_value ();
6624  FIX_LIMITS ;
6625 
6626  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'c');
6627 
6628  if (min_val > max_val)
6629  {
6630  min_val = min_pos = 0;
6631  max_val = 1;
6632  }
6633  else if (min_val == max_val)
6634  {
6635  max_val = min_val + 1;
6636  min_val -= 1;
6637  }
6638 
6639  limits.resize (1, 2);
6640 
6641  limits(0) = min_val;
6642  limits(1) = max_val;
6643 
6644  update_type = 'c';
6645  }
6646 
6647  }
6648  else if (axis_type == "alphadata" || axis_type == "alimmode"
6649  || axis_type == "alphadatamapping" || axis_type == "aliminclude"
6650  || axis_type == "alim")
6651  {
6652  if (xproperties.alimmode_is ("auto"))
6653  {
6654  limits = xproperties.get_alim ().matrix_value ();
6655  FIX_LIMITS ;
6656 
6657  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'a');
6658 
6659  if (min_val > max_val)
6660  {
6661  min_val = min_pos = 0;
6662  max_val = 1;
6663  }
6664  else if (min_val == max_val)
6665  max_val = min_val + 1;
6666 
6667  limits.resize (1, 2);
6668 
6669  limits(0) = min_val;
6670  limits(1) = max_val;
6671 
6672  update_type = 'a';
6673  }
6674 
6675  }
6676 
6677 #undef FIX_LIMITS
6678 
6679  unwind_protect frame;
6680  frame.protect_var (updating_axis_limits);
6681 
6682  updating_axis_limits = true;
6683 
6684  switch (update_type)
6685  {
6686  case 'x':
6687  xproperties.set_xlim (limits);
6688  xproperties.set_xlimmode ("auto");
6689  xproperties.update_xlim ();
6690  break;
6691 
6692  case 'y':
6693  xproperties.set_ylim (limits);
6694  xproperties.set_ylimmode ("auto");
6695  xproperties.update_ylim ();
6696  break;
6697 
6698  case 'z':
6699  xproperties.set_zlim (limits);
6700  xproperties.set_zlimmode ("auto");
6701  xproperties.update_zlim ();
6702  break;
6703 
6704  case 'c':
6705  xproperties.set_clim (limits);
6706  xproperties.set_climmode ("auto");
6707  break;
6708 
6709  case 'a':
6710  xproperties.set_alim (limits);
6711  xproperties.set_alimmode ("auto");
6712  break;
6713 
6714  default:
6715  break;
6716  }
6717 
6718  xproperties.update_transform ();
6719 
6720 }
6721 
6722 void
6723 axes::update_axis_limits (const std::string& axis_type)
6724 {
6725  if (updating_axis_limits || updating_aspectratios)
6726  return;
6727 
6728  Matrix kids = xproperties.get_children ();
6729 
6730  double min_val = octave_Inf;
6731  double max_val = -octave_Inf;
6732  double min_pos = octave_Inf;
6733  double max_neg = -octave_Inf;
6734 
6735  char update_type = 0;
6736 
6737  Matrix limits;
6738 
6739  if (axis_type == "xdata" || axis_type == "xscale"
6740  || axis_type == "xlimmode" || axis_type == "xliminclude"
6741  || axis_type == "xlim")
6742  {
6743  if (xproperties.xlimmode_is ("auto"))
6744  {
6745  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'x');
6746 
6747  limits = xproperties.get_axis_limits (min_val, max_val,
6748  min_pos, max_neg,
6749  xproperties.xscale_is ("log"));
6750 
6751  update_type = 'x';
6752  }
6753  }
6754  else if (axis_type == "ydata" || axis_type == "yscale"
6755  || axis_type == "ylimmode" || axis_type == "yliminclude"
6756  || axis_type == "ylim")
6757  {
6758  if (xproperties.ylimmode_is ("auto"))
6759  {
6760  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'y');
6761 
6762  limits = xproperties.get_axis_limits (min_val, max_val,
6763  min_pos, max_neg,
6764  xproperties.yscale_is ("log"));
6765 
6766  update_type = 'y';
6767  }
6768  }
6769  else if (axis_type == "zdata" || axis_type == "zscale"
6770  || axis_type == "zlimmode" || axis_type == "zliminclude"
6771  || axis_type == "zlim")
6772  {
6773  if (xproperties.zlimmode_is ("auto"))
6774  {
6775  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'z');
6776 
6777  limits = xproperties.get_axis_limits (min_val, max_val,
6778  min_pos, max_neg,
6779  xproperties.zscale_is ("log"));
6780 
6781  update_type = 'z';
6782  }
6783  }
6784  else if (axis_type == "cdata" || axis_type == "climmode"
6785  || axis_type == "cdatamapping" || axis_type == "climinclude"
6786  || axis_type == "clim")
6787  {
6788  if (xproperties.climmode_is ("auto"))
6789  {
6790  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'c');
6791 
6792  if (min_val > max_val)
6793  {
6794  min_val = min_pos = 0;
6795  max_val = 1;
6796  }
6797  else if (min_val == max_val)
6798  {
6799  max_val = min_val + 1;
6800  min_val -= 1;
6801  }
6802 
6803  limits.resize (1, 2);
6804 
6805  limits(0) = min_val;
6806  limits(1) = max_val;
6807 
6808  update_type = 'c';
6809  }
6810 
6811  }
6812  else if (axis_type == "alphadata" || axis_type == "alimmode"
6813  || axis_type == "alphadatamapping" || axis_type == "aliminclude"
6814  || axis_type == "alim")
6815  {
6816  if (xproperties.alimmode_is ("auto"))
6817  {
6818  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'a');
6819 
6820  if (min_val > max_val)
6821  {
6822  min_val = min_pos = 0;
6823  max_val = 1;
6824  }
6825  else if (min_val == max_val)
6826  max_val = min_val + 1;
6827 
6828  limits.resize (1, 2);
6829 
6830  limits(0) = min_val;
6831  limits(1) = max_val;
6832 
6833  update_type = 'a';
6834  }
6835 
6836  }
6837 
6838  unwind_protect frame;
6839  frame.protect_var (updating_axis_limits);
6840 
6841  updating_axis_limits = true;
6842 
6843  switch (update_type)
6844  {
6845  case 'x':
6846  xproperties.set_xlim (limits);
6847  xproperties.set_xlimmode ("auto");
6848  xproperties.update_xlim ();
6849  break;
6850 
6851  case 'y':
6852  xproperties.set_ylim (limits);
6853  xproperties.set_ylimmode ("auto");
6854  xproperties.update_ylim ();
6855  break;
6856 
6857  case 'z':
6858  xproperties.set_zlim (limits);
6859  xproperties.set_zlimmode ("auto");
6860  xproperties.update_zlim ();
6861  break;
6862 
6863  case 'c':
6864  xproperties.set_clim (limits);
6865  xproperties.set_climmode ("auto");
6866  break;
6867 
6868  case 'a':
6869  xproperties.set_alim (limits);
6870  xproperties.set_alimmode ("auto");
6871  break;
6872 
6873  default:
6874  break;
6875  }
6876 
6877  xproperties.update_transform ();
6878 }
6879 
6880 inline
6881 double force_in_range (const double x, const double lower, const double upper)
6882 {
6883  if (x < lower)
6884  { return lower; }
6885  else if (x > upper)
6886  { return upper; }
6887  else
6888  { return x; }
6889 }
6890 
6891 static Matrix
6892 do_zoom (double val, double factor, const Matrix& lims, bool is_logscale)
6893 {
6894  Matrix new_lims = lims;
6895 
6896  double lo = lims(0);
6897  double hi = lims(1);
6898 
6899  bool is_negative = lo < 0 && hi < 0;
6900 
6901  if (is_logscale)
6902  {
6903  if (is_negative)
6904  {
6905  double tmp = hi;
6906  hi = std::log10 (-lo);
6907  lo = std::log10 (-tmp);
6908  val = std::log10 (-val);
6909  }
6910  else
6911  {
6912  hi = std::log10 (hi);
6913  lo = std::log10 (lo);
6914  val = std::log10 (val);
6915  }
6916  }
6917 
6918  // Perform the zooming
6919  lo = val + factor * (lo - val);
6920  hi = val + factor * (hi - val);
6921 
6922  if (is_logscale)
6923  {
6924  if (is_negative)
6925  {
6926  double tmp = -std::pow (10.0, hi);
6927  hi = -std::pow (10.0, lo);
6928  lo = tmp;
6929  }
6930  else
6931  {
6932  lo = std::pow (10.0, lo);
6933  hi = std::pow (10.0, hi);
6934  }
6935  }
6936 
6937  new_lims(0) = lo;
6938  new_lims(1) = hi;
6939 
6940  return new_lims;
6941 }
6942 
6943 void
6944 axes::properties::zoom_about_point (double x, double y, double factor,
6945  bool push_to_zoom_stack)
6946 {
6947  // FIXME: Do we need error checking here?
6948  Matrix xlims = get_xlim ().matrix_value ();
6949  Matrix ylims = get_ylim ().matrix_value ();
6950 
6951  // Get children axes limits
6952  Matrix kids = get_children ();
6953  double minx = octave_Inf;
6954  double maxx = -octave_Inf;
6955  double min_pos_x = octave_Inf;
6956  double max_neg_x = -octave_Inf;
6957  get_children_limits (minx, maxx, min_pos_x, max_neg_x, kids, 'x');
6958 
6959  double miny = octave_Inf;
6960  double maxy = -octave_Inf;
6961  double min_pos_y = octave_Inf;
6962  double max_neg_y = -octave_Inf;
6963  get_children_limits (miny, maxy, min_pos_y, max_neg_y, kids, 'y');
6964 
6965  xlims = do_zoom (x, factor, xlims, xscale_is ("log"));
6966  ylims = do_zoom (y, factor, ylims, yscale_is ("log"));
6967 
6968  zoom (xlims, ylims, push_to_zoom_stack);
6969 }
6970 
6971 void
6972 axes::properties::zoom (const Matrix& xl, const Matrix& yl,
6973  bool push_to_zoom_stack)
6974 {
6975  if (push_to_zoom_stack)
6976  {
6977  zoom_stack.push_front (xlimmode.get ());
6978  zoom_stack.push_front (xlim.get ());
6979  zoom_stack.push_front (ylimmode.get ());
6980  zoom_stack.push_front (ylim.get ());
6981  }
6982 
6983  xlim = xl;
6984  xlimmode = "manual";
6985  ylim = yl;
6986  ylimmode = "manual";
6987 
6988  update_transform ();
6989  update_xlim (false);
6990  update_ylim (false);
6991 }
6992 
6993 static Matrix
6994 do_translate (double x0, double x1, const Matrix& lims, bool is_logscale)
6995 {
6996  Matrix new_lims = lims;
6997 
6998  double lo = lims(0);
6999  double hi = lims(1);
7000 
7001  bool is_negative = lo < 0 && hi < 0;
7002 
7003  double delta;
7004 
7005  if (is_logscale)
7006  {
7007  if (is_negative)
7008  {
7009  double tmp = hi;
7010  hi = std::log10 (-lo);
7011  lo = std::log10 (-tmp);
7012  x0 = -x0;
7013  x1 = -x1;
7014  }
7015  else
7016  {
7017  hi = std::log10 (hi);
7018  lo = std::log10 (lo);
7019  }
7020 
7021  delta = std::log10 (x0) - std::log10 (x1);
7022  }
7023  else
7024  {
7025  delta = x0 - x1;
7026  }
7027 
7028  // Perform the translation
7029  lo += delta;
7030  hi += delta;
7031 
7032  if (is_logscale)
7033  {
7034  if (is_negative)
7035  {
7036  double tmp = -std::pow (10.0, hi);
7037  hi = -std::pow (10.0, lo);
7038  lo = tmp;
7039  }
7040  else
7041  {
7042  lo = std::pow (10.0, lo);
7043  hi = std::pow (10.0, hi);
7044  }
7045  }
7046 
7047  new_lims(0) = lo;
7048  new_lims(1) = hi;
7049 
7050  return new_lims;
7051 }
7052 
7053 void
7054 axes::properties::translate_view (double x0, double x1, double y0, double y1)
7055 {
7056  // FIXME: Do we need error checking here?
7057  Matrix xlims = get_xlim ().matrix_value ();
7058  Matrix ylims = get_ylim ().matrix_value ();
7059 
7060  // Get children axes limits
7061  Matrix kids = get_children ();
7062  double minx = octave_Inf;
7063  double maxx = -octave_Inf;
7064  double min_pos_x = octave_Inf;
7065  double max_neg_x = -octave_Inf;
7066  get_children_limits (minx, maxx, min_pos_x, max_neg_x, kids, 'x');
7067 
7068  double miny = octave_Inf;
7069  double maxy = -octave_Inf;
7070  double min_pos_y = octave_Inf;
7071  double max_neg_y = -octave_Inf;
7072  get_children_limits (miny, maxy, min_pos_y, max_neg_y, kids, 'y');
7073 
7074  xlims = do_translate (x0, x1, xlims, xscale_is ("log"));
7075  ylims = do_translate (y0, y1, ylims, yscale_is ("log"));
7076 
7077  zoom (xlims, ylims, false);
7078 }
7079 
7080 void
7081 axes::properties::rotate_view (double delta_el, double delta_az)
7082 {
7083  Matrix v = get_view ().matrix_value ();
7084 
7085  v(1) += delta_el;
7086 
7087  if (v(1) > 90)
7088  v(1) = 90;
7089  if (v(1) < -90)
7090  v(1) = -90;
7091 
7092  v(0) = fmod (v(0) - delta_az + 720,360);
7093 
7094  set_view (v);
7095  update_transform ();
7096 }
7097 
7098 void
7100 {
7101  if (zoom_stack.size () >= 4)
7102  {
7103  ylim = zoom_stack.front ();
7104  zoom_stack.pop_front ();
7105  ylimmode = zoom_stack.front ();
7106  zoom_stack.pop_front ();
7107  xlim = zoom_stack.front ();
7108  zoom_stack.pop_front ();
7109  xlimmode = zoom_stack.front ();
7110  zoom_stack.pop_front ();
7111 
7112  update_transform ();
7113  update_xlim (false);
7114  update_ylim (false);
7115  }
7116 }
7117 
7118 void
7120 {
7121  while (zoom_stack.size () > 4)
7122  zoom_stack.pop_front ();
7123 
7124  unzoom ();
7125 }
7126 
7127 void
7129 {
7131 }
7132 
7133 void
7135 {
7137 
7139  xinitialize (xproperties.get_xlabel ());
7140  xinitialize (xproperties.get_ylabel ());
7141  xinitialize (xproperties.get_zlabel ());
7142 
7143  xproperties.sync_positions ();
7144 }
7145 
7146 // ---------------------------------------------------------------------
7147 
7148 Matrix
7150 {
7151  Matrix m (1, 4);
7152 
7153  m(0) = xdata.min_val ();
7154  m(1) = xdata.max_val ();
7155  m(2) = xdata.min_pos ();
7156  m(3) = xdata.max_neg ();
7157 
7158  return m;
7159 }
7160 
7161 Matrix
7163 {
7164  Matrix m (1, 4);
7165 
7166  m(0) = ydata.min_val ();
7167  m(1) = ydata.max_val ();
7168  m(2) = ydata.min_pos ();
7169  m(3) = ydata.max_neg ();
7170 
7171  return m;
7172 }
7173 
7174 // ---------------------------------------------------------------------
7175 
7176 Matrix
7178 {
7179  Matrix pos = get_position ().matrix_value ();
7180 
7181  if (! units_is ("data"))
7182  pos = convert_text_position (pos, *this, get_units (), "data");
7183 
7184  return pos;
7185 }
7186 
7187 Matrix
7189 {
7190  // FIXME: Should this function also add the (x,y) base position?
7191  return extent.get ().matrix_value ();
7192 }
7193 
7196 {
7197  // FIXME: This doesn't work right for 3D plots.
7198  // (It doesn't in Matlab either, at least not in version 6.5.)
7199  Matrix m = extent.get ().matrix_value ();
7200  Matrix pos = get_position ().matrix_value ();
7201  Matrix p = convert_text_position (pos, *this, get_units (), "pixels");
7202 
7203  m(0) += p(0);
7204  m(1) += p(1);
7205 
7206  return convert_text_position (m, *this, "pixels", get_units ());
7207 }
7208 
7209 void
7211 {
7212 #ifdef HAVE_FREETYPE
7213 #ifdef HAVE_FONTCONFIG
7214  renderer.set_font (get ("fontname").string_value (),
7215  get ("fontweight").string_value (),
7216  get ("fontangle").string_value (),
7217  get ("fontsize").double_value ());
7218 #endif
7219  renderer.set_color (get_color_rgb ());
7220 #endif
7221 }
7222 
7223 void
7225 {
7226 #ifdef HAVE_FREETYPE
7227 
7228  int halign = 0, valign = 0;
7229 
7230  if (horizontalalignment_is ("center"))
7231  halign = 1;
7232  else if (horizontalalignment_is ("right"))
7233  halign = 2;
7234 
7235  if (verticalalignment_is ("middle"))
7236  valign = 1;
7237  else if (verticalalignment_is ("top"))
7238  valign = 2;
7239  else if (verticalalignment_is ("baseline"))
7240  valign = 3;
7241  else if (verticalalignment_is ("cap"))
7242  valign = 4;
7243 
7244  Matrix bbox;
7245 
7246  // FIXME: string should be parsed only when modified, for efficiency
7247 
7248  octave_value string_prop = get_string ();
7249 
7250  string_vector sv = string_prop.all_strings ();
7251 
7252  renderer.text_to_pixels (sv.join ("\n"), pixels, bbox,
7253  halign, valign, get_rotation (),
7254  get_interpreter ());
7255  /* The bbox is relative to the text's position.
7256  We'll leave it that way, because get_position () does not return
7257  valid results when the text is first constructed.
7258  Conversion to proper coordinates is performed in get_extent. */
7259  set_extent (bbox);
7260 
7261 #endif
7262 
7263  if (autopos_tag_is ("xlabel") || autopos_tag_is ("ylabel") ||
7264  autopos_tag_is ("zlabel") || autopos_tag_is ("title"))
7265  update_autopos ("sync");
7266 }
7267 
7268 void
7270 {
7271  if (autopos_tag_is ("xlabel") || autopos_tag_is ("ylabel") ||
7272  autopos_tag_is ("zlabel") || autopos_tag_is ("title"))
7273  update_autopos (get_autopos_tag ());
7274 }
7275 
7276 void
7278 {
7279  if (! units_is ("data"))
7280  {
7281  set_xliminclude ("off");
7282  set_yliminclude ("off");
7283  set_zliminclude ("off");
7284  }
7285 
7286  Matrix pos = get_position ().matrix_value ();
7287 
7288  pos = convert_text_position (pos, *this, cached_units, get_units ());
7289  // FIXME: if the current axes view is 2D, then one should
7290  // probably drop the z-component of "pos" and leave "zliminclude"
7291  // to "off".
7292  set_position (pos);
7293 
7294  if (units_is ("data"))
7295  {
7296  set_xliminclude ("on");
7297  set_yliminclude ("on");
7298  // FIXME: see above
7299  set_zliminclude ("off");
7300  }
7301 
7302  cached_units = get_units ();
7303 }
7304 
7305 double
7306 text::properties::get_fontsize_points (double box_pix_height) const
7307 {
7308  double fs = get_fontsize ();
7309  double parent_height = box_pix_height;
7310 
7311  if (fontunits_is ("normalized") && parent_height <= 0)
7312  {
7313  graphics_object go (gh_manager::get_object (get___myhandle__ ()));
7314  graphics_object ax (go.get_ancestor ("axes"));
7315 
7316  parent_height = ax.get_properties ().get_boundingbox (true).elem (3);
7317  }
7318 
7319  return convert_font_size (fs, get_fontunits (), "points", parent_height);
7320 }
7321 
7322 // ---------------------------------------------------------------------
7323 
7326 {
7327  return convert_cdata (*this, get_cdata (),
7328  cdatamapping_is ("scaled"), 3);
7329 }
7330 
7331 // ---------------------------------------------------------------------
7332 
7335 {
7336  octave_value fvc = get_facevertexcdata ();
7337  if (fvc.is_undefined () || fvc.is_empty ())
7338  return Matrix ();
7339  else
7340  return convert_cdata (*this, fvc,cdatamapping_is ("scaled"), 2);
7341 }
7342 
7343 // ---------------------------------------------------------------------
7344 
7347 {
7348  return convert_cdata (*this, get_cdata (), cdatamapping_is ("scaled"), 3);
7349 }
7350 
7351 inline void
7352 cross_product (double x1, double y1, double z1,
7353  double x2, double y2, double z2,
7354  double& x, double& y, double& z)
7355 {
7356  x += (y1 * z2 - z1 * y2);
7357  y += (z1 * x2 - x1 * z2);
7358  z += (x1 * y2 - y1 * x2);
7359 }
7360 
7361 void
7363 {
7364  if (normalmode_is ("auto"))
7365  {
7366  Matrix x = get_xdata ().matrix_value ();
7367  Matrix y = get_ydata ().matrix_value ();
7368  Matrix z = get_zdata ().matrix_value ();
7369 
7370 
7371  int p = z.columns (), q = z.rows ();
7372  int i1 = 0, i2 = 0, i3 = 0;
7373  int j1 = 0, j2 = 0, j3 = 0;
7374 
7375  bool x_mat = (x.rows () == q);
7376  bool y_mat = (y.columns () == p);
7377 
7378  NDArray n (dim_vector (q, p, 3), 0.0);
7379 
7380  for (int i = 0; i < p; i++)
7381  {
7382  if (y_mat)
7383  {
7384  i1 = i - 1;
7385  i2 = i;
7386  i3 = i + 1;
7387  }
7388 
7389  for (int j = 0; j < q; j++)
7390  {
7391  if (x_mat)
7392  {
7393  j1 = j - 1;
7394  j2 = j;
7395  j3 = j + 1;
7396  }
7397 
7398  double& nx = n(j, i, 0);
7399  double& ny = n(j, i, 1);
7400  double& nz = n(j, i, 2);
7401 
7402  if ((j > 0) && (i > 0))
7403  // upper left quadrangle
7405  (x(j1,i-1)-x(j2,i), y(j-1,i1)-y(j,i2), z(j-1,i-1)-z(j,i),
7406  x(j2,i-1)-x(j1,i), y(j,i1)-y(j-1,i2), z(j,i-1)-z(j-1,i),
7407  nx, ny, nz);
7408 
7409  if ((j > 0) && (i < (p -1)))
7410  // upper right quadrangle
7412  (x(j1,i+1)-x(j2,i), y(j-1,i3)-y(j,i2), z(j-1,i+1)-z(j,i),
7413  x(j1,i)-x(j2,i+1), y(j-1,i2)-y(j,i3), z(j-1,i)-z(j,i+1),
7414  nx, ny, nz);
7415 
7416  if ((j < (q - 1)) && (i > 0))
7417  // lower left quadrangle
7419  (x(j2,i-1)-x(j3,i), y(j,i1)-y(j+1,i2), z(j,i-1)-z(j+1,i),
7420  x(j3,i-1)-x(j2,i), y(j+1,i1)-y(j,i2), z(j+1,i-1)-z(j,i),
7421  nx, ny, nz);
7422 
7423  if ((j < (q - 1)) && (i < (p -1)))
7424  // lower right quadrangle
7426  (x(j3,i)-x(j2,i+1), y(j+1,i2)-y(j,i3), z(j+1,i)-z(j,i+1),
7427  x(j3,i+1)-x(j2,i), y(j+1,i3)-y(j,i2), z(j+1,i+1)-z(j,i),
7428  nx, ny, nz);
7429 
7430  double d = -std::max (std::max (fabs (nx), fabs (ny)), fabs (nz));
7431 
7432  nx /= d;
7433  ny /= d;
7434  nz /= d;
7435  }
7436  }
7437  vertexnormals = n;
7438  }
7439 }
7440 
7441 // ---------------------------------------------------------------------
7442 
7443 void
7445 {
7446  graphics_object obj = gh_manager::get_object (__myhandle__);
7447 
7448  if (obj)
7449  {
7450  obj.update_axis_limits ("xlim");
7451  obj.update_axis_limits ("ylim");
7452  obj.update_axis_limits ("zlim");
7453  obj.update_axis_limits ("clim");
7454  obj.update_axis_limits ("alim");
7455  }
7456 }
7457 
7458 void
7460 {
7461  graphics_object obj = gh_manager::get_object (__myhandle__);
7462 
7463  if (obj)
7464  {
7465  obj.update_axis_limits ("xlim", h);
7466  obj.update_axis_limits ("ylim", h);
7467  obj.update_axis_limits ("zlim", h);
7468  obj.update_axis_limits ("clim", h);
7469  obj.update_axis_limits ("alim", h);
7470  }
7471 }
7472 
7473 static bool updating_hggroup_limits = false;
7474 
7475 void
7476 hggroup::update_axis_limits (const std::string& axis_type,
7477  const graphics_handle& h)
7478 {
7479  if (updating_hggroup_limits)
7480  return;
7481 
7482  Matrix kids = Matrix (1, 1, h.value ());
7483 
7484  double min_val = octave_Inf;
7485  double max_val = -octave_Inf;
7486  double min_pos = octave_Inf;
7487  double max_neg = -octave_Inf;
7488 
7489  Matrix limits;
7490  double val;
7491 
7492  char update_type = 0;
7493 
7494  if (axis_type == "xlim" || axis_type == "xliminclude")
7495  {
7496  limits = xproperties.get_xlim ().matrix_value ();
7497  update_type = 'x';
7498  }
7499  else if (axis_type == "ylim" || axis_type == "yliminclude")
7500  {
7501  limits = xproperties.get_ylim ().matrix_value ();
7502  update_type = 'y';
7503  }
7504  else if (axis_type == "zlim" || axis_type == "zliminclude")
7505  {
7506  limits = xproperties.get_zlim ().matrix_value ();
7507  update_type = 'z';
7508  }
7509  else if (axis_type == "clim" || axis_type == "climinclude")
7510  {
7511  limits = xproperties.get_clim ().matrix_value ();
7512  update_type = 'c';
7513  }
7514  else if (axis_type == "alim" || axis_type == "aliminclude")
7515  {
7516  limits = xproperties.get_alim ().matrix_value ();
7517  update_type = 'a';
7518  }
7519 
7520  if (limits.numel () == 4)
7521  {
7522  val = limits(0);
7523  if (xfinite (val))
7524  min_val = val;
7525  val = limits(1);
7526  if (xfinite (val))
7527  max_val = val;
7528  val = limits(2);
7529  if (xfinite (val))
7530  min_pos = val;
7531  val = limits(3);
7532  if (xfinite (val))
7533  max_neg = val;
7534  }
7535  else
7536  {
7537  limits.resize (4,1);
7538  limits(0) = min_val;
7539  limits(1) = max_val;
7540  limits(2) = min_pos;
7541  limits(3) = max_neg;
7542  }
7543 
7544  get_children_limits (min_val, max_val, min_pos, max_neg, kids, update_type);
7545 
7546  unwind_protect frame;
7547  frame.protect_var (updating_hggroup_limits);
7548 
7549  updating_hggroup_limits = true;
7550 
7551  if (limits(0) != min_val || limits(1) != max_val
7552  || limits(2) != min_pos || limits(3) != max_neg)
7553  {
7554  limits(0) = min_val;
7555  limits(1) = max_val;
7556  limits(2) = min_pos;
7557  limits(3) = max_neg;
7558 
7559  switch (update_type)
7560  {
7561  case 'x':
7562  xproperties.set_xlim (limits);
7563  break;
7564 
7565  case 'y':
7566  xproperties.set_ylim (limits);
7567  break;
7568 
7569  case 'z':
7570  xproperties.set_zlim (limits);
7571  break;
7572 
7573  case 'c':
7574  xproperties.set_clim (limits);
7575  break;
7576 
7577  case 'a':
7578  xproperties.set_alim (limits);
7579  break;
7580 
7581  default:
7582  break;
7583  }
7584 
7586  }
7587 }
7588 
7589 void
7590 hggroup::update_axis_limits (const std::string& axis_type)
7591 {
7592  if (updating_hggroup_limits)
7593  return;
7594 
7595  Matrix kids = xproperties.get_children ();
7596 
7597  double min_val = octave_Inf;
7598  double max_val = -octave_Inf;
7599  double min_pos = octave_Inf;
7600  double max_neg = -octave_Inf;
7601 
7602  char update_type = 0;
7603 
7604  if (axis_type == "xlim" || axis_type == "xliminclude")
7605  {
7606  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'x');
7607 
7608  update_type = 'x';
7609  }
7610  else if (axis_type == "ylim" || axis_type == "yliminclude")
7611  {
7612  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'y');
7613 
7614  update_type = 'y';
7615  }
7616  else if (axis_type == "zlim" || axis_type == "zliminclude")
7617  {
7618  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'z');
7619 
7620  update_type = 'z';
7621  }
7622  else if (axis_type == "clim" || axis_type == "climinclude")
7623  {
7624  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'c');
7625 
7626  update_type = 'c';
7627  }
7628  else if (axis_type == "alim" || axis_type == "aliminclude")
7629  {
7630  get_children_limits (min_val, max_val, min_pos, max_neg, kids, 'a');
7631 
7632  update_type = 'a';
7633  }
7634 
7635  unwind_protect frame;
7636  frame.protect_var (updating_hggroup_limits);
7637 
7638  updating_hggroup_limits = true;
7639 
7640  Matrix limits (1, 4, 0.0);
7641 
7642  limits(0) = min_val;
7643  limits(1) = max_val;
7644  limits(2) = min_pos;
7645  limits(3) = max_neg;
7646 
7647  switch (update_type)
7648  {
7649  case 'x':
7650  xproperties.set_xlim (limits);
7651  break;
7652 
7653  case 'y':
7654  xproperties.set_ylim (limits);
7655  break;
7656 
7657  case 'z':
7658  xproperties.set_zlim (limits);
7659  break;
7660 
7661  case 'c':
7662  xproperties.set_clim (limits);
7663  break;
7664 
7665  case 'a':
7666  xproperties.set_alim (limits);
7667  break;
7668 
7669  default:
7670  break;
7671  }
7672 
7674 }
7675 
7676 // ---------------------------------------------------------------------
7677 
7680 {
7681  Matrix m = extent.get ().matrix_value ();
7682 
7683  graphics_object parent_obj =
7685  Matrix parent_bbox = parent_obj.get_properties ().get_boundingbox (true),
7686  parent_size = parent_bbox.extract_n (0, 2, 1, 2);
7687 
7688  return convert_position (m, "pixels", get_units (), parent_size);
7689 }
7690 
7691 void
7693 {
7694 #ifdef HAVE_FREETYPE
7695 
7696  text_element *elt;
7697  ft_render text_renderer;
7698  Matrix box;
7699 
7700  // FIXME: parsed content should be cached for efficiency
7701  // FIXME: support multiline text
7702 
7703  elt = text_parser::parse (get_string_string (), "none");
7704 #ifdef HAVE_FONTCONFIG
7705  text_renderer.set_font (get_fontname (),
7706  get_fontweight (),
7707  get_fontangle (),
7708  get_fontsize ());
7709 #endif
7710  box = text_renderer.get_extent (elt, 0);
7711  delete elt;
7712 
7713  Matrix ext (1, 4, 0.0);
7714 
7715  // FIXME: also handle left and bottom components
7716 
7717  ext(0) = ext(1) = 1;
7718  ext(2) = box(0);
7719  ext(3) = box(1);
7720 
7721  set_extent (ext);
7722 
7723 #endif
7724 }
7725 
7726 void
7728 {
7729  Matrix pos = get_position ().matrix_value ();
7730 
7732  Matrix parent_bbox = parent_obj.get_properties ().get_boundingbox (true),
7733  parent_size = parent_bbox.extract_n (0, 2, 1, 2);
7734 
7735  pos = convert_position (pos, cached_units, get_units (), parent_size);
7736  set_position (pos);
7737 
7738  cached_units = get_units ();
7739 }
7740 
7741 void
7743 {
7744  if (get___object__ ().is_empty ())
7745  style = st;
7746  else
7747  error ("set: cannot change the style of a uicontrol object after creation.");
7748 }
7749 
7750 Matrix
7752  const Matrix& parent_pix_size) const
7753 {
7754  Matrix pos = get_position ().matrix_value ();
7755  Matrix parent_size (parent_pix_size);
7756 
7757  if (parent_size.numel () == 0)
7758  {
7760 
7761  if (obj.valid_object ())
7762  parent_size =
7763  obj.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
7764  else
7765  parent_size = default_figure_position ();
7766  }
7767 
7768  pos = convert_position (pos, get_units (), "pixels", parent_size);
7769 
7770  pos(0)--;
7771  pos(1)--;
7772  pos(1) = parent_size(1) - pos(1) - pos(3);
7773 
7774  return pos;
7775 }
7776 
7777 void
7779 {
7780  if (! error_state)
7781  {
7782  caseless_str old_fontunits = get_fontunits ();
7783  if (fontunits.set (v, true))
7784  {
7785  update_fontunits (old_fontunits);
7786  mark_modified ();
7787  }
7788  }
7789 }
7790 
7791 void
7793 {
7794  caseless_str new_units = get_fontunits ();
7795  double parent_height = get_boundingbox (false).elem (3);
7796  double fsz = get_fontsize ();
7797 
7798  fsz = convert_font_size (fsz, old_units, new_units, parent_height);
7799 
7800  fontsize.set (octave_value (fsz), true);
7801 }
7802 
7803 double
7804 uicontrol::properties::get_fontsize_points (double box_pix_height) const
7805 {
7806  double fs = get_fontsize ();
7807  double parent_height = box_pix_height;
7808 
7809  if (fontunits_is ("normalized") && parent_height <= 0)
7810  parent_height = get_boundingbox (false).elem (3);
7811 
7812  return convert_font_size (fs, get_fontunits (), "points", parent_height);
7813 }
7814 
7815 // ---------------------------------------------------------------------
7816 
7817 Matrix
7819  const Matrix& parent_pix_size) const
7820 {
7821  Matrix pos = get_position ().matrix_value ();
7822  Matrix parent_size (parent_pix_size);
7823 
7824  if (parent_size.numel () == 0)
7825  {
7827 
7828  parent_size =
7829  obj.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
7830  }
7831 
7832  pos = convert_position (pos, get_units (), "pixels", parent_size);
7833 
7834  pos(0)--;
7835  pos(1)--;
7836  pos(1) = parent_size(1) - pos(1) - pos(3);
7837 
7838  if (internal)
7839  {
7840  double outer_height = pos(3);
7841 
7842  pos(0) = pos(1) = 0;
7843 
7844  if (! bordertype_is ("none"))
7845  {
7846  double bw = get_borderwidth ();
7847  double mul = 1.0;
7848 
7849  if (bordertype_is ("etchedin") || bordertype_is ("etchedout"))
7850  mul = 2.0;
7851 
7852  pos(0) += mul * bw;
7853  pos(1) += mul * bw;
7854  pos(2) -= 2 * mul * bw;
7855  pos(3) -= 2 * mul * bw;
7856  }
7857 
7858  if (! get_title ().empty ())
7859  {
7860  double fs = get_fontsize ();
7861 
7862  if (! fontunits_is ("pixels"))
7863  {
7864  double res = xget (0, "screenpixelsperinch").double_value ();
7865 
7866  if (fontunits_is ("points"))
7867  fs *= (res / 72.0);
7868  else if (fontunits_is ("inches"))
7869  fs *= res;
7870  else if (fontunits_is ("centimeters"))
7871  fs *= (res / 2.54);
7872  else if (fontunits_is ("normalized"))
7873  fs *= outer_height;
7874  }
7875 
7876  if (titleposition_is ("lefttop") || titleposition_is ("centertop")
7877  || titleposition_is ("righttop"))
7878  pos(1) += (fs / 2);
7879  pos(3) -= (fs / 2);
7880  }
7881  }
7882 
7883  return pos;
7884 }
7885 
7886 void
7888 {
7889  if (! error_state)
7890  {
7891  caseless_str old_units = get_units ();
7892  if (units.set (v, true))
7893  {
7894  update_units (old_units);
7895  mark_modified ();
7896  }
7897  }
7898 }
7899 
7900 void
7902 {
7903  Matrix pos = get_position ().matrix_value ();
7904 
7906  Matrix parent_bbox = parent_obj.get_properties ().get_boundingbox (true),
7907  parent_size = parent_bbox.extract_n (0, 2, 1, 2);
7908 
7909  pos = convert_position (pos, old_units, get_units (), parent_size);
7910  set_position (pos);
7911 }
7912 
7913 void
7915 {
7916  if (! error_state)
7917  {
7918  caseless_str old_fontunits = get_fontunits ();
7919  if (fontunits.set (v, true))
7920  {
7921  update_fontunits (old_fontunits);
7922  mark_modified ();
7923  }
7924  }
7925 }
7926 
7927 void
7929 {
7930  caseless_str new_units = get_fontunits ();
7931  double parent_height = get_boundingbox (false).elem (3);
7932  double fsz = get_fontsize ();
7933 
7934  fsz = convert_font_size (fsz, old_units, new_units, parent_height);
7935 
7936  set_fontsize (octave_value (fsz));
7937 }
7938 
7939 double
7940 uipanel::properties::get_fontsize_points (double box_pix_height) const
7941 {
7942  double fs = get_fontsize ();
7943  double parent_height = box_pix_height;
7944 
7945  if (fontunits_is ("normalized") && parent_height <= 0)
7946  parent_height = get_boundingbox (false).elem (3);
7947 
7948  return convert_font_size (fs, get_fontunits (), "points", parent_height);
7949 }
7950 
7951 // ---------------------------------------------------------------------
7952 
7955 {
7956  octave_value retval = default_properties.lookup (name);
7957 
7958  if (retval.is_undefined ())
7959  {
7960  graphics_handle parent = get_parent ();
7961  graphics_object parent_obj = gh_manager::get_object (parent);
7962 
7963  retval = parent_obj.get_default (name);
7964  }
7965 
7966  return retval;
7967 }
7968 
7969 void
7971 {
7973 }
7974 
7975 // ---------------------------------------------------------------------
7976 
7979 {
7980  graphics_handle parent = get_parent ();
7981  graphics_object parent_obj = gh_manager::get_object (parent);
7982 
7983  return parent_obj.get_default (type () + name);
7984 }
7985 
7988 {
7989  graphics_object parent_obj = gh_manager::get_object (0);
7990 
7991  return parent_obj.get_factory_default (type () + name);
7992 }
7993 
7994 // We use a random value for the handle to avoid issues with plots and
7995 // scalar values for the first argument.
7997  : handle_map (), handle_free_list (),
7998  next_handle (-1.0 - (rand () + 1.0) / (RAND_MAX + 2.0)),
7999  figure_list (), graphics_lock (), event_queue (),
8000  callback_objects (), event_processing (0)
8001 {
8002  handle_map[0] = graphics_object (new root_figure ());
8003 
8004  // Make sure the default graphics toolkit is registered.
8006 }
8007 
8008 void
8010 {
8011  instance = new gh_manager ();
8012 
8013  if (instance)
8015 }
8016 
8018 gh_manager::do_make_graphics_handle (const std::string& go_name,
8019  const graphics_handle& p,
8020  bool integer_figure_handle,
8021  bool do_createfcn,
8022  bool do_notify_toolkit)
8023 {
8024  graphics_handle h = get_handle (integer_figure_handle);
8025 
8026  base_graphics_object *go = 0;
8027 
8028  go = make_graphics_object_from_type (go_name, h, p);
8029 
8030  if (go)
8031  {
8032  graphics_object obj (go);
8033 
8034  handle_map[h] = obj;
8035  if (do_createfcn)
8037 
8038  // Notify graphics toolkit.
8039  if (do_notify_toolkit)
8040  obj.initialize ();
8041  }
8042  else
8043  error ("gh_manager::do_make_graphics_handle: invalid object type '%s'",
8044  go_name.c_str ());
8045 
8046  return h;
8047 }
8048 
8050 gh_manager::do_make_figure_handle (double val, bool do_notify_toolkit)
8051 {
8052  graphics_handle h = val;
8053 
8054  base_graphics_object* go = new figure (h, 0);
8055  graphics_object obj (go);
8056 
8057  handle_map[h] = obj;
8058 
8059  // Notify graphics toolkit.
8060  if (do_notify_toolkit)
8061  obj.initialize ();
8062 
8063  return h;
8064 }
8065 
8066 void
8068 {
8069  do_pop_figure (h);
8070 
8071  figure_list.push_front (h);
8072 }
8073 
8074 void
8076 {
8077  for (figure_list_iterator p = figure_list.begin ();
8078  p != figure_list.end ();
8079  p++)
8080  {
8081  if (*p == h)
8082  {
8083  figure_list.erase (p);
8084  break;
8085  }
8086  }
8087 }
8088 
8089 class
8091 {
8092 public:
8093  callback_event (const graphics_handle& h, const std::string& name,
8094  const octave_value& data = Matrix ())
8095  : base_graphics_event (), handle (h), callback_name (name),
8096  callback (), callback_data (data) { }
8097 
8099  const octave_value& data = Matrix ())
8100  : base_graphics_event (), handle (h), callback_name (),
8101  callback (cb), callback_data (data) { }
8102 
8103  void execute (void)
8104  {
8105  if (callback.is_defined ())
8106  gh_manager::execute_callback (handle, callback, callback_data);
8107  else
8108  gh_manager::execute_callback (handle, callback_name, callback_data);
8109  }
8110 
8111 private:
8113  : base_graphics_event (), handle (), callback_name (), callback_data ()
8114  { }
8115 
8116 private:
8118  std::string callback_name;
8121 };
8122 
8123 class
8125 {
8126 public:
8128  : base_graphics_event (), function (fcn), function_data (data)
8129  { }
8130 
8131  void execute (void)
8132  {
8133  function (function_data);
8134  }
8135 
8136 private:
8137 
8139 
8141 
8142  // function_event objects must be created with at least a function.
8143  function_event (void);
8144 
8145  // No copying!
8146 
8147  function_event (const function_event &);
8148 
8149  function_event & operator = (const function_event &);
8150 };
8151 
8152 class
8154 {
8155 public:
8156  set_event (const graphics_handle& h, const std::string& name,
8157  const octave_value& value, bool do_notify_toolkit = true)
8158  : base_graphics_event (), handle (h), property_name (name),
8159  property_value (value), notify_toolkit (do_notify_toolkit) { }
8160 
8161  void execute (void)
8162  {
8163  gh_manager::auto_lock guard;
8164 
8166 
8167  if (go)
8168  {
8169  property p = go.get_properties ().get_property (property_name);
8170 
8171  if (p.ok ())
8172  p.set (property_value, true, notify_toolkit);
8173  }
8174  }
8175 
8176 private:
8177  set_event (void)
8178  : base_graphics_event (), handle (), property_name (), property_value ()
8179  { }
8180 
8181 private:
8183  std::string property_name;
8186 };
8187 
8190  const std::string& name,
8191  const octave_value& data)
8192 {
8193  graphics_event e;
8194 
8195  e.rep = new callback_event (h, name, data);
8196 
8197  return e;
8198 }
8199 
8202  const octave_value& cb,
8203  const octave_value& data)
8204 {
8205  graphics_event e;
8206 
8207  e.rep = new callback_event (h, cb, data);
8208 
8209  return e;
8210 }
8211 
8214  void *data)
8215 {
8216  graphics_event e;
8217 
8218  e.rep = new function_event (fcn, data);
8219 
8220  return e;
8221 }
8222 
8225  const std::string& name,
8226  const octave_value& data,
8227  bool notify_toolkit)
8228 {
8229  graphics_event e;
8230 
8231  e.rep = new set_event (h, name, data, notify_toolkit);
8232 
8233  return e;
8234 }
8235 
8236 static void
8238 {
8240  root_figure::properties& props =
8241  dynamic_cast<root_figure::properties&> (go.get_properties ());
8242 
8243  props.set_callbackobject (h.as_octave_value ());
8244 }
8245 
8246 void
8248 {
8249  gh_manager::auto_lock guard;
8250 
8251  callback_objects.pop_front ();
8252 
8253  xset_gcbo (callback_objects.empty ()
8254  ? graphics_handle ()
8255  : callback_objects.front ().get_handle ());
8256 }
8257 
8258 void
8260  const octave_value& l)
8261 {
8264  else
8265  {
8266  gh_manager::auto_lock guard;
8267 
8269  }
8270 }
8271 
8272 void
8274  const octave_value& cb_arg,
8275  const octave_value& data)
8276 {
8277  if (cb_arg.is_defined () && ! cb_arg.is_empty ())
8278  {
8279  octave_value_list args;
8280  octave_function *fcn = 0;
8281 
8282  args(0) = h.as_octave_value ();
8283  if (data.is_defined ())
8284  args(1) = data;
8285  else
8286  args(1) = Matrix ();
8287 
8288  unwind_protect_safe frame;
8290 
8291  if (true)
8292  {
8293  gh_manager::auto_lock guard;
8294 
8295  callback_objects.push_front (get_object (h));
8296  xset_gcbo (h);
8297  }
8298 
8299  BEGIN_INTERRUPT_WITH_EXCEPTIONS;
8300 
8301  // Copy CB because "function_value" method is non-const.
8302 
8303  octave_value cb = cb_arg;
8304 
8305  if (cb.is_function () || cb.is_function_handle ())
8306  fcn = cb.function_value ();
8307  else if (cb.is_string ())
8308  {
8309  int status;
8310  std::string s = cb.string_value ();
8311 
8312  eval_string (s, false, status, 0);
8313  }
8314  else if (cb.is_cell () && cb.length () > 0
8315  && (cb.rows () == 1 || cb.columns () == 1)
8316  && (cb.cell_value ()(0).is_function ()
8317  || cb.cell_value ()(0).is_function_handle ()))
8318  {
8319  Cell c = cb.cell_value ();
8320 
8321  fcn = c(0).function_value ();
8322  if (! error_state)
8323  {
8324  for (int i = 1; i < c.length () ; i++)
8325  args(1+i) = c(i);
8326  }
8327  }
8328  else
8329  {
8330  std::string nm = cb.class_name ();
8331  error ("trying to execute non-executable object (class = %s)",
8332  nm.c_str ());
8333  }
8334 
8335  if (fcn && ! error_state)
8336  feval (fcn, args);
8337 
8338  END_INTERRUPT_WITH_EXCEPTIONS;
8339  }
8340 }
8341 
8342 void
8344 {
8345  event_queue.push_back (e);
8346 
8348 }
8349 
8350 void
8351 gh_manager::do_post_callback (const graphics_handle& h, const std::string name,
8352  const octave_value& data)
8353 {
8354  gh_manager::auto_lock guard;
8355 
8356  graphics_object go = get_object (h);
8357 
8358  if (go.valid_object ())
8359  {
8360  if (callback_objects.empty ())
8362  else
8363  {
8364  const graphics_object& current = callback_objects.front ();
8365 
8366  if (current.get_properties ().is_interruptible ())
8368  data));
8369  else
8370  {
8371  caseless_str busy_action (go.get_properties ().get_busyaction ());
8372 
8373  if (busy_action.compare ("queue"))
8375  data));
8376  else
8377  {
8378  caseless_str cname (name);
8379 
8380  if (cname.compare ("deletefcn") || cname.compare ("createfcn")
8381  || (go.isa ("figure")
8382  && (cname.compare ("closerequestfcn")
8383  || cname.compare ("resizefcn"))))
8384  do_post_event (
8385  graphics_event::create_callback_event (h, name, data));
8386  }
8387  }
8388  }
8389  }
8390 }
8391 
8392 void
8394 {
8395  gh_manager::auto_lock guard;
8396 
8398 }
8399 
8400 void
8401 gh_manager::do_post_set (const graphics_handle& h, const std::string name,
8402  const octave_value& value, bool notify_toolkit)
8403 {
8404  gh_manager::auto_lock guard;
8405 
8407  notify_toolkit));
8408 }
8409 
8410 int
8412 {
8413  graphics_event e;
8414  bool old_Vdrawnow_requested = Vdrawnow_requested;
8415  bool events_executed = false;
8416 
8417  do
8418  {
8419  e = graphics_event ();
8420 
8421  gh_manager::lock ();
8422 
8423  if (! event_queue.empty ())
8424  {
8425  if (callback_objects.empty () || force)
8426  {
8427  e = event_queue.front ();
8428 
8429  event_queue.pop_front ();
8430  }
8431  else
8432  {
8433  const graphics_object& go = callback_objects.front ();
8434 
8435  if (go.get_properties ().is_interruptible ())
8436  {
8437  e = event_queue.front ();
8438 
8439  event_queue.pop_front ();
8440  }
8441  }
8442  }
8443 
8444  gh_manager::unlock ();
8445 
8446  if (e.ok ())
8447  {
8448  e.execute ();
8449  events_executed = true;
8450  }
8451  }
8452  while (e.ok ());
8453 
8454  gh_manager::lock ();
8455 
8456  if (event_queue.empty () && event_processing == 0)
8458 
8459  gh_manager::unlock ();
8460 
8461  if (events_executed)
8463 
8464  if (Vdrawnow_requested && ! old_Vdrawnow_requested)
8465  {
8466  Fdrawnow ();
8467 
8468  Vdrawnow_requested = false;
8469  }
8470 
8471  return 0;
8472 }
8473 
8474 void
8476 {
8477  gh_manager::auto_lock guard;
8478 
8479  if (enable)
8480  {
8481  event_processing++;
8482 
8484  }
8485  else
8486  {
8487  event_processing--;
8488 
8489  if (event_queue.empty () && event_processing == 0)
8491  }
8492 }
8493 
8496 {
8498 
8499  plist_map["figure"] = figure::properties::factory_defaults ();
8500  plist_map["axes"] = axes::properties::factory_defaults ();
8501  plist_map["line"] = line::properties::factory_defaults ();
8502  plist_map["text"] = text::properties::factory_defaults ();
8503  plist_map["image"] = image::properties::factory_defaults ();
8504  plist_map["patch"] = patch::properties::factory_defaults ();
8505  plist_map["surface"] = surface::properties::factory_defaults ();
8506  plist_map["hggroup"] = hggroup::properties::factory_defaults ();
8507  plist_map["uimenu"] = uimenu::properties::factory_defaults ();
8508  plist_map["uicontrol"] = uicontrol::properties::factory_defaults ();
8509  plist_map["uipanel"] = uipanel::properties::factory_defaults ();
8510  plist_map["uicontextmenu"] = uicontextmenu::properties::factory_defaults ();
8511  plist_map["uitoolbar"] = uitoolbar::properties::factory_defaults ();
8512  plist_map["uipushtool"] = uipushtool::properties::factory_defaults ();
8513  plist_map["uitoggletool"] = uitoggletool::properties::factory_defaults ();
8514 
8515  return plist_map;
8516 }
8517 
8518 // ---------------------------------------------------------------------
8519 
8520 DEFUN (ishandle, args, ,
8521  "-*- texinfo -*-\n\
8522 @deftypefn {Built-in Function} {} ishandle (@var{h})\n\
8523 Return true if @var{h} is a graphics handle and false otherwise.\n\
8524 \n\
8525 @var{h} may also be a matrix of handles in which case a logical\n\
8526 array is returned that is true where the elements of @var{h} are\n\
8527 graphics handles and false where they are not.\n\
8528 @seealso{isaxes, isfigure}\n\
8529 @end deftypefn")
8530 {
8531  gh_manager::auto_lock guard;
8532 
8533  octave_value retval;
8534 
8535  if (args.length () == 1)
8536  retval = is_handle (args(0));
8537  else
8538  print_usage ();
8539 
8540  return retval;
8541 }
8542 
8543 static bool
8545 {
8546  return h.ok () && gh_manager::is_handle_visible (h);
8547 }
8548 
8549 static bool
8550 is_handle_visible (double val)
8551 {
8552  return is_handle_visible (gh_manager::lookup (val));
8553 }
8554 
8555 static octave_value
8557 {
8558  octave_value retval = false;
8559 
8560  if (val.is_real_scalar () && is_handle_visible (val.double_value ()))
8561  retval = true;
8562  else if (val.is_numeric_type () && val.is_real_type ())
8563  {
8564  const NDArray handles = val.array_value ();
8565 
8566  if (! error_state)
8567  {
8568  boolNDArray result (handles.dims ());
8569 
8570  for (octave_idx_type i = 0; i < handles.numel (); i++)
8571  result.xelem (i) = is_handle_visible (handles (i));
8572 
8573  retval = result;
8574  }
8575  }
8576 
8577  return retval;
8578 }
8579 
8580 DEFUN (__is_handle_visible__, args, ,
8581  "-*- texinfo -*-\n\
8582 @deftypefn {Built-in Function} __is_handle_visible__ (@var{h})\n\
8583 Undocumented internal function.\n\
8584 @end deftypefn")
8585 {
8586  octave_value retval;
8587 
8588  if (args.length () == 1)
8589  retval = is_handle_visible (args(0));
8590  else
8591  print_usage ();
8592 
8593  return retval;
8594 }
8595 
8596 DEFUN (reset, args, ,
8597  "-*- texinfo -*-\n\
8598 @deftypefn {Built-in Function} {} reset (@var{h}, @var{property})\n\
8599 Remove any defaults set for the handle @var{h}. The default figure\n\
8600 properties of @qcode{\"position\"}, @qcode{\"units\"},\n\
8601 @qcode{\"windowstyle\"} and @qcode{\"paperunits\"} and the default axes\n\
8602 properties of @qcode{\"position\"} and @qcode{\"units\"} are not reset.\n\
8603 @seealso{cla, clf}\n\
8604 @end deftypefn")
8605 {
8606  int nargin = args.length ();
8607 
8608  if (nargin != 1)
8609  print_usage ();
8610  else
8611  {
8612  // get vector of graphics handles
8613  ColumnVector hcv (args(0).vector_value ());
8614 
8615  if (! error_state)
8616  {
8617  // loop over graphics objects
8618  for (octave_idx_type n = 0; n < hcv.length (); n++)
8620  }
8621  }
8622 
8623  return octave_value ();
8624 }
8625 
8626 DEFUN (set, args, nargout,
8627  "-*- texinfo -*-\n\
8628 @deftypefn {Built-in Function} {} set (@var{h}, @var{property}, @var{value}, @dots{})\n\
8629 @deftypefnx {Built-in Function} {} set (@var{h}, @var{properties}, @var{values})\n\
8630 @deftypefnx {Built-in Function} {} set (@var{h}, @var{pv})\n\
8631 Set named property values for the graphics handle (or vector of graphics\n\
8632 handles) @var{h}.\n\
8633 There are three ways how to give the property names and values:\n\
8634 \n\
8635 @itemize\n\
8636 @item as a comma separated list of @var{property}, @var{value} pairs\n\
8637 \n\
8638 Here, each @var{property} is a string containing the property name, each\n\
8639 @var{value} is a value of the appropriate type for the property.\n\
8640 \n\
8641 @item as a cell array of strings @var{properties} containing property names\n\
8642 and a cell array @var{values} containing property values.\n\
8643 \n\
8644 In this case, the number of columns of @var{values} must match the number of\n\
8645 elements in @var{properties}. The first column of @var{values} contains\n\
8646 values for the first entry in @var{properties}, etc. The number of rows of\n\
8647 @var{values} must be 1 or match the number of elements of @var{h}. In the\n\
8648 first case, each handle in @var{h} will be assigned the same values. In the\n\
8649 latter case, the first handle in @var{h} will be assigned the values from\n\
8650 the first row of @var{values} and so on.\n\
8651 \n\
8652 @item as a structure array @var{pv}\n\
8653 \n\
8654 Here, the field names of @var{pv} represent the property names, and the field\n\
8655 values give the property values. In contrast to the previous case, all\n\
8656 elements of @var{pv} will be set in all handles in @var{h} independent of\n\
8657 the dimensions of @var{pv}.\n\
8658 @end itemize\n\
8659 @seealso{get}\n\
8660 @end deftypefn")
8661 {
8662  gh_manager::auto_lock guard;
8663 
8664  octave_value retval;
8665 
8666  int nargin = args.length ();
8667 
8668  if (nargin > 0)
8669  {
8670  // get vector of graphics handles
8671  ColumnVector hcv (args(0).vector_value ());
8672 
8673  if (! error_state)
8674  {
8675  bool request_drawnow = false;
8676 
8677  // loop over graphics objects
8678  for (octave_idx_type n = 0; n < hcv.length (); n++)
8679  {
8680  graphics_object obj = gh_manager::get_object (hcv(n));
8681 
8682  if (obj)
8683  {
8684  if (nargin == 3 && args(1).is_cellstr ()
8685  && args(2).is_cell ())
8686  {
8687  if (args(2).cell_value ().rows () == 1)
8688  {
8689  obj.set (args(1).cellstr_value (),
8690  args(2).cell_value (), 0);
8691  }
8692  else if (hcv.length () == args(2).cell_value ().rows ())
8693  {
8694  obj.set (args(1).cellstr_value (),
8695  args(2).cell_value (), n);
8696  }
8697  else
8698  {
8699  error ("set: number of graphics handles must match number of value rows (%d != %d)",
8700  hcv.length (), args(2).cell_value ().rows ());
8701  break;
8702 
8703  }
8704  }
8705  else if (nargin == 2 && args(1).is_map ())
8706  {
8707  obj.set (args(1).map_value ());
8708  }
8709  else if (nargin == 1)
8710  {
8711  if (nargout != 0)
8712  retval = obj.values_as_struct ();
8713  else
8714  {
8715  std::string s = obj.values_as_string ();
8716  if (! error_state)
8717  octave_stdout << s;
8718  }
8719  }
8720  else
8721  {
8722  obj.set (args.splice (0, 1));
8723  request_drawnow = true;
8724  }
8725  }
8726  else
8727  {
8728  error ("set: invalid handle (= %g)", hcv(n));
8729  break;
8730  }
8731 
8732  if (error_state)
8733  break;
8734 
8735  request_drawnow = true;
8736  }
8737 
8738  if (! error_state && request_drawnow)
8739  Vdrawnow_requested = true;
8740  }
8741  else
8742  error ("set: expecting graphics handle as first argument");
8743  }
8744  else
8745  print_usage ();
8746 
8747  return retval;
8748 }
8749 
8750 static std::string
8751 get_graphics_object_type (const double val)
8752 {
8753  std::string retval;
8754 
8756 
8757  if (obj)
8758  retval = obj.type ();
8759  else
8760  error ("get: invalid handle (= %g)", val);
8761 
8762  return retval;
8763 }
8764 
8765 DEFUN (get, args, ,
8766  "-*- texinfo -*-\n\
8767 @deftypefn {Built-in Function} {@var{val} =} get (@var{h})\n\
8768 @deftypefnx {Built-in Function} {@var{val} =} get (@var{h}, @var{p})\n\
8769 Return the value of the named property @var{p} from the graphics handle\n\
8770 @var{h}. If @var{p} is omitted, return the complete property list for\n\
8771 @var{h}. If @var{h} is a vector, return a cell array including the property\n\
8772 values or lists respectively.\n\
8773 @seealso{set}\n\
8774 @end deftypefn")
8775 {
8776  gh_manager::auto_lock guard;
8777 
8778  octave_value retval;
8779 
8780  Cell vals;
8781 
8782  int nargin = args.length ();
8783 
8784  bool use_cell_format = false;
8785 
8786  if (nargin == 1 || nargin == 2)
8787  {
8788  if (args(0).is_empty ())
8789  {
8790  retval = Matrix ();
8791  return retval;
8792  }
8793 
8794  ColumnVector hcv (args(0).vector_value ());
8795 
8796  if (! error_state)
8797  {
8798  octave_idx_type len = hcv.length ();
8799 
8800  if (nargin == 1 && len > 1)
8801  {
8802  std::string t0 = get_graphics_object_type (hcv(0));
8803 
8804  if (! error_state)
8805  {
8806  for (octave_idx_type n = 1; n < len; n++)
8807  {
8808  std::string t = get_graphics_object_type (hcv(n));
8809 
8810  if (error_state)
8811  break;
8812 
8813  if (t != t0)
8814  {
8815  error ("get: vector of handles must all have same type");
8816  break;
8817  }
8818  }
8819 
8820  }
8821  }
8822 
8823  if (! error_state)
8824  {
8825  if (nargin > 1 && args(1).is_cellstr ())
8826  {
8827  Array<std::string> plist = args(1).cellstr_value ();
8828 
8829  if (! error_state)
8830  {
8831  octave_idx_type plen = plist.numel ();
8832 
8833  use_cell_format = true;
8834 
8835  vals.resize (dim_vector (len, plen));
8836 
8837  for (octave_idx_type n = 0; ! error_state && n < len; n++)
8838  {
8839  graphics_object obj = gh_manager::get_object (hcv(n));
8840 
8841  if (obj)
8842  {
8843  for (octave_idx_type m = 0;
8844  ! error_state && m < plen;
8845  m++)
8846  {
8847  caseless_str property = plist(m);
8848 
8849  vals(n, m) = obj.get (property);
8850  }
8851  }
8852  else
8853  {
8854  error ("get: invalid handle (= %g)", hcv(n));
8855  break;
8856  }
8857  }
8858  }
8859  else
8860  error ("get: expecting property name or cell array of property names as second argument");
8861  }
8862  else
8863  {
8865 
8866  if (nargin > 1)
8867  {
8868  property = args(1).string_value ();
8869 
8870  if (error_state)
8871  error ("get: expecting property name or cell array of property names as second argument");
8872  }
8873 
8874  vals.resize (dim_vector (len, 1));
8875 
8876  if (! error_state)
8877  {
8878  for (octave_idx_type n = 0; ! error_state && n < len; n++)
8879  {
8880  graphics_object obj = gh_manager::get_object (hcv(n));
8881 
8882  if (obj)
8883  {
8884  if (nargin == 1)
8885  vals(n) = obj.get ();
8886  else
8887  vals(n) = obj.get (property);
8888  }
8889  else
8890  {
8891  error ("get: invalid handle (= %g)", hcv(n));
8892  break;
8893  }
8894  }
8895  }
8896  }
8897  }
8898  }
8899  else
8900  error ("get: expecting graphics handle as first argument");
8901  }
8902  else
8903  print_usage ();
8904 
8905  if (! error_state)
8906  {
8907  if (use_cell_format)
8908  retval = vals;
8909  else
8910  {
8911  octave_idx_type len = vals.numel ();
8912 
8913  if (len == 0)
8914  retval = Matrix ();
8915  else if (len == 1)
8916  retval = vals(0);
8917  else if (len > 1 && nargin == 1)
8918  {
8920 
8921  for (octave_idx_type n = 0; n < len; n++)
8922  tmp[n] = vals(n).scalar_map_value ();
8923 
8924  retval = octave_map::cat (0, len, tmp);
8925  }
8926  else
8927  retval = vals;
8928  }
8929  }
8930 
8931  return retval;
8932 }
8933 
8934 /*
8935 %!assert (get (findobj (0, "Tag", "nonexistenttag"), "nonexistentproperty"), [])
8936 */
8937 
8938 // Return all properties from the graphics handle @var{h}.
8939 // If @var{h} is a vector, return a cell array including the
8940 // property values or lists respectively.
8941 
8942 DEFUN (__get__, args, ,
8943  "-*- texinfo -*-\n\
8944 @deftypefn {Built-in Function} {} __get__ (@var{h})\n\
8945 Undocumented internal function.\n\
8946 @end deftypefn")
8947 {
8948  gh_manager::auto_lock guard;
8949 
8950  octave_value retval;
8951 
8952  Cell vals;
8953 
8954  int nargin = args.length ();
8955 
8956  if (nargin == 1)
8957  {
8958  ColumnVector hcv (args(0).vector_value ());
8959 
8960  if (! error_state)
8961  {
8962  octave_idx_type len = hcv.length ();
8963 
8964  vals.resize (dim_vector (len, 1));
8965 
8966  for (octave_idx_type n = 0; n < len; n++)
8967  {
8968  graphics_object obj = gh_manager::get_object (hcv(n));
8969 
8970  if (obj)
8971  vals(n) = obj.get (true);
8972  else
8973  {
8974  error ("get: invalid handle (= %g)", hcv(n));
8975  break;
8976  }
8977  }
8978  }
8979  else
8980  error ("get: expecting graphics handle as first argument");
8981  }
8982  else
8983  print_usage ();
8984 
8985  if (! error_state)
8986  {
8987  octave_idx_type len = vals.numel ();
8988 
8989  if (len > 1)
8990  retval = vals;
8991  else if (len == 1)
8992  retval = vals(0);
8993  }
8994 
8995  return retval;
8996 }
8997 
8998 static octave_value
8999 make_graphics_object (const std::string& go_name,
9000  bool integer_figure_handle,
9001  const octave_value_list& args)
9002 {
9003  octave_value retval;
9004 
9005  double val = octave_NaN;
9006 
9007  octave_value_list xargs = args.splice (0, 1);
9008 
9009  caseless_str p ("parent");
9010 
9011  for (int i = 0; i < xargs.length (); i++)
9012  if (xargs(i).is_string ()
9013  && p.compare (xargs(i).string_value ()))
9014  {
9015  if (i < (xargs.length () - 1))
9016  {
9017  val = xargs(i+1).double_value ();
9018 
9019  if (! error_state)
9020  {
9021  xargs = xargs.splice (i, 2);
9022  break;
9023  }
9024  }
9025  else
9026  error ("__go_%s__: missing value for parent property",
9027  go_name.c_str ());
9028  }
9029 
9030  if (! error_state && xisnan (val))
9031  val = args(0).double_value ();
9032 
9033  if (! error_state)
9034  {
9035  graphics_handle parent = gh_manager::lookup (val);
9036 
9037  if (parent.ok ())
9038  {
9039  graphics_handle h
9040  = gh_manager::make_graphics_handle (go_name, parent,
9041  integer_figure_handle,
9042  false, false);
9043 
9044  if (! error_state)
9045  {
9046  adopt (parent, h);
9047 
9048  xset (h, xargs);
9049  xcreatefcn (h);
9050  xinitialize (h);
9051 
9052  retval = h.value ();
9053 
9054  if (! error_state)
9055  Vdrawnow_requested = true;
9056  }
9057  else
9058  error ("__go%s__: unable to create graphics handle",
9059  go_name.c_str ());
9060  }
9061  else
9062  error ("__go_%s__: invalid parent", go_name.c_str ());
9063  }
9064  else
9065  error ("__go_%s__: invalid parent", go_name.c_str ());
9066 
9067  return retval;
9068 }
9069 
9070 DEFUN (__go_figure__, args, ,
9071  "-*- texinfo -*-\n\
9072 @deftypefn {Built-in Function} {} __go_figure__ (@var{fignum})\n\
9073 Undocumented internal function.\n\
9074 @end deftypefn")
9075 {
9076  gh_manager::auto_lock guard;
9077 
9078  octave_value retval;
9079 
9080  if (args.length () > 0)
9081  {
9082  double val = args(0).double_value ();
9083 
9084  if (! error_state)
9085  {
9086  if (is_figure (val))
9087  {
9089 
9090  xset (h, args.splice (0, 1));
9091 
9092  retval = h.value ();
9093  }
9094  else
9095  {
9096  bool int_fig_handle = true;
9097 
9098  octave_value_list xargs = args.splice (0, 1);
9099 
9101 
9102  if (xisnan (val))
9103  {
9104  caseless_str p ("integerhandle");
9105 
9106  for (int i = 0; i < xargs.length (); i++)
9107  {
9108  if (xargs(i).is_string ()
9109  && p.compare (xargs(i).string_value ()))
9110  {
9111  if (i < (xargs.length () - 1))
9112  {
9113  std::string pval = xargs(i+1).string_value ();
9114 
9115  if (! error_state)
9116  {
9117  caseless_str on ("on");
9118  int_fig_handle = on.compare (pval);
9119  xargs = xargs.splice (i, 2);
9120  break;
9121  }
9122  }
9123  }
9124  }
9125 
9126  h = gh_manager::make_graphics_handle ("figure", 0,
9127  int_fig_handle,
9128  false, false);
9129 
9130  if (! int_fig_handle)
9131  {
9132  // We need to intiailize the integerhandle
9133  // property without calling the set_integerhandle
9134  // method, because doing that will generate a new
9135  // handle value...
9136 
9138  go.get_properties ().init_integerhandle ("off");
9139  }
9140  }
9141  else if (val > 0 && D_NINT (val) == val)
9142  h = gh_manager::make_figure_handle (val, false);
9143 
9144  if (! error_state && h.ok ())
9145  {
9146  adopt (0, h);
9147 
9149 
9150  xset (h, xargs);
9151  xcreatefcn (h);
9152  xinitialize (h);
9153 
9154  retval = h.value ();
9155  }
9156  else
9157  error ("__go_figure__: failed to create figure handle");
9158  }
9159  }
9160  else
9161  error ("__go_figure__: expecting figure number to be double value");
9162  }
9163  else
9164  print_usage ();
9165 
9166  return retval;
9167 }
9168 
9169 #define GO_BODY(TYPE) \
9170  gh_manager::auto_lock guard; \
9171  \
9172  octave_value retval; \
9173  \
9174  if (args.length () > 0) \
9175  retval = make_graphics_object (#TYPE, false, args); \
9176  else \
9177  print_usage (); \
9178  \
9179  return retval
9180 
9181 int
9183 {
9184 
9185  int nd = 2;
9186 
9187  if (go.isa ("surface"))
9188  nd = 3;
9189  else if ((go.isa ("line") || go.isa ("patch"))
9190  && ! go.get ("zdata").is_empty ())
9191  nd = 3;
9192  else
9193  {
9194  Matrix kids = go.get_properties ().get_children ();
9195 
9196  for (octave_idx_type i = 0; i < kids.length (); i++)
9197  {
9198  graphics_handle hnd = gh_manager::lookup (kids(i));
9199 
9200  if (hnd.ok ())
9201  {
9202  const graphics_object& kid = gh_manager::get_object (hnd);
9203 
9204  if (kid.valid_object ())
9205  nd = calc_dimensions (kid);
9206 
9207  if (nd == 3)
9208  break;
9209  }
9210  }
9211  }
9212 
9213  return nd;
9214 }
9215 
9216 DEFUN (__calc_dimensions__, args, ,
9217  "-*- texinfo -*-\n\
9218 @deftypefn {Built-in Function} {} __calc_dimensions__ (@var{axes})\n\
9219 Internal function. Determine the number of dimensions in a graphics\n\
9220 object, whether 2 or 3.\n\
9221 @end deftypefn")
9222 {
9223  gh_manager::auto_lock guard;
9224 
9225  octave_value retval;
9226 
9227  int nargin = args.length ();
9228 
9229  if (nargin != 1)
9230  print_usage ();
9231 
9232  double h = args(0).double_value ();
9233 
9234  if (! error_state)
9235  retval = calc_dimensions (gh_manager::get_object (h));
9236  else
9237  error ("__calc_dimensions__: expecting graphics handle as only argument");
9238 
9239  return retval;
9240 }
9241 
9242 DEFUN (__go_axes__, args, ,
9243  "-*- texinfo -*-\n\
9244 @deftypefn {Built-in Function} {} __go_axes__ (@var{parent})\n\
9245 Undocumented internal function.\n\
9246 @end deftypefn")
9247 {
9248  GO_BODY (axes);
9249 }
9250 
9251 DEFUN (__go_line__, args, ,
9252  "-*- texinfo -*-\n\
9253 @deftypefn {Built-in Function} {} __go_line__ (@var{parent})\n\
9254 Undocumented internal function.\n\
9255 @end deftypefn")
9256 {
9257  GO_BODY (line);
9258 }
9259 
9260 DEFUN (__go_text__, args, ,
9261  "-*- texinfo -*-\n\
9262 @deftypefn {Built-in Function} {} __go_text__ (@var{parent})\n\
9263 Undocumented internal function.\n\
9264 @end deftypefn")
9265 {
9266  GO_BODY (text);
9267 }
9268 
9269 DEFUN (__go_image__, args, ,
9270  "-*- texinfo -*-\n\
9271 @deftypefn {Built-in Function} {} __go_image__ (@var{parent})\n\
9272 Undocumented internal function.\n\
9273 @end deftypefn")
9274 {
9275  GO_BODY (image);
9276 }
9277 
9278 DEFUN (__go_surface__, args, ,
9279  "-*- texinfo -*-\n\
9280 @deftypefn {Built-in Function} {} __go_surface__ (@var{parent})\n\
9281 Undocumented internal function.\n\
9282 @end deftypefn")
9283 {
9284  GO_BODY (surface);
9285 }
9286 
9287 DEFUN (__go_patch__, args, ,
9288  "-*- texinfo -*-\n\
9289 @deftypefn {Built-in Function} {} __go_patch__ (@var{parent})\n\
9290 Undocumented internal function.\n\
9291 @end deftypefn")
9292 {
9293  GO_BODY (patch);
9294 }
9295 
9296 DEFUN (__go_hggroup__, args, ,
9297  "-*- texinfo -*-\n\
9298 @deftypefn {Built-in Function} {} __go_hggroup__ (@var{parent})\n\
9299 Undocumented internal function.\n\
9300 @end deftypefn")
9301 {
9302  GO_BODY (hggroup);
9303 }
9304 
9305 DEFUN (__go_uimenu__, args, ,
9306  "-*- texinfo -*-\n\
9307 @deftypefn {Built-in Function} {} __go_uimenu__ (@var{parent})\n\
9308 Undocumented internal function.\n\
9309 @end deftypefn")
9310 {
9311  GO_BODY (uimenu);
9312 }
9313 
9314 DEFUN (__go_uicontrol__, args, ,
9315  "-*- texinfo -*-\n\
9316 @deftypefn {Built-in Function} {} __go_uicontrol__ (@var{parent})\n\
9317 Undocumented internal function.\n\
9318 @end deftypefn")
9319 {
9320  GO_BODY (uicontrol);
9321 }
9322 
9323 DEFUN (__go_uipanel__, args, ,
9324  "-*- texinfo -*-\n\
9325 @deftypefn {Built-in Function} {} __go_uipanel__ (@var{parent})\n\
9326 Undocumented internal function.\n\
9327 @end deftypefn")
9328 {
9329  GO_BODY (uipanel);
9330 }
9331 
9332 DEFUN (__go_uicontextmenu__, args, ,
9333  "-*- texinfo -*-\n\
9334 @deftypefn {Built-in Function} {} __go_uicontextmenu__ (@var{parent})\n\
9335 Undocumented internal function.\n\
9336 @end deftypefn")
9337 {
9339 }
9340 
9341 DEFUN (__go_uitoolbar__, args, ,
9342  "-*- texinfo -*-\n\
9343 @deftypefn {Built-in Function} {} __go_uitoolbar__ (@var{parent})\n\
9344 Undocumented internal function.\n\
9345 @end deftypefn")
9346 {
9347  GO_BODY (uitoolbar);
9348 }
9349 
9350 DEFUN (__go_uipushtool__, args, ,
9351  "-*- texinfo -*-\n\
9352 @deftypefn {Built-in Function} {} __go_uipushtool__ (@var{parent})\n\
9353 Undocumented internal function.\n\
9354 @end deftypefn")
9355 {
9356  GO_BODY (uipushtool);
9357 }
9358 
9359 DEFUN (__go_uitoggletool__, args, ,
9360  "-*- texinfo -*-\n\
9361 @deftypefn {Built-in Function} {} __go_uitoggletool__ (@var{parent})\n\
9362 Undocumented internal function.\n\
9363 @end deftypefn")
9364 {
9366 }
9367 
9368 DEFUN (__go_delete__, args, ,
9369  "-*- texinfo -*-\n\
9370 @deftypefn {Built-in Function} {} __go_delete__ (@var{h})\n\
9371 Undocumented internal function.\n\
9372 @end deftypefn")
9373 {
9374  gh_manager::auto_lock guard;
9375 
9376  octave_value_list retval;
9377 
9378  if (args.length () == 1)
9379  {
9381 
9382  const NDArray vals = args (0).array_value ();
9383 
9384  if (! error_state)
9385  {
9386  // Check is all the handles to delete are valid first
9387  // as callbacks might delete one of the handles we
9388  // later want to delete
9389  for (octave_idx_type i = 0; i < vals.numel (); i++)
9390  {
9391  h = gh_manager::lookup (vals.elem (i));
9392 
9393  if (! h.ok ())
9394  {
9395  error ("delete: invalid graphics object (= %g)",
9396  vals.elem (i));
9397  break;
9398  }
9399  }
9400 
9401  if (! error_state)
9402  delete_graphics_objects (vals);
9403  }
9404  else
9405  error ("delete: invalid graphics object");
9406  }
9407  else
9408  print_usage ();
9409 
9410  return retval;
9411 }
9412 
9413 DEFUN (__go_axes_init__, args, ,
9414  "-*- texinfo -*-\n\
9415 @deftypefn {Built-in Function} {} __go_axes_init__ (@var{h}, @var{mode})\n\
9416 Undocumented internal function.\n\
9417 @end deftypefn")
9418 {
9419  gh_manager::auto_lock guard;
9420 
9421  octave_value retval;
9422 
9423  int nargin = args.length ();
9424 
9425  std::string mode = "";
9426 
9427  if (nargin == 2)
9428  {
9429  mode = args(1).string_value ();
9430 
9431  if (error_state)
9432  return retval;
9433  }
9434 
9435  if (nargin == 1 || nargin == 2)
9436  {
9438 
9439  double val = args(0).double_value ();
9440 
9441  if (! error_state)
9442  {
9443  h = gh_manager::lookup (val);
9444 
9445  if (h.ok ())
9446  {
9448 
9449  obj.set_defaults (mode);
9450 
9451  h = gh_manager::lookup (val);
9452  if (! h.ok ())
9453  error ("__go_axes_init__: axis deleted during initialization (= %g)",
9454  val);
9455  }
9456  else
9457  error ("__go_axes_init__: invalid graphics object (= %g)", val);
9458  }
9459  else
9460  error ("__go_axes_init__: invalid graphics object");
9461  }
9462  else
9463  print_usage ();
9464 
9465  return retval;
9466 }
9467 
9468 DEFUN (__go_handles__, args, ,
9469  "-*- texinfo -*-\n\
9470 @deftypefn {Built-in Function} {} __go_handles__ (@var{show_hidden})\n\
9471 Undocumented internal function.\n\
9472 @end deftypefn")
9473 {
9474  gh_manager::auto_lock guard;
9475 
9476  bool show_hidden = false;
9477 
9478  if (args.length () > 0)
9479  show_hidden = args(0).bool_value ();
9480 
9481  return octave_value (gh_manager::handle_list (show_hidden));
9482 }
9483 
9484 DEFUN (__go_figure_handles__, args, ,
9485  "-*- texinfo -*-\n\
9486 @deftypefn {Built-in Function} {} __go_figure_handles__ (@var{show_hidden})\n\
9487 Undocumented internal function.\n\
9488 @end deftypefn")
9489 {
9490  gh_manager::auto_lock guard;
9491 
9492  bool show_hidden = false;
9493 
9494  if (args.length () > 0)
9495  show_hidden = args(0).bool_value ();
9496 
9497  return octave_value (gh_manager::figure_handle_list (show_hidden));
9498 }
9499 
9500 DEFUN (__go_execute_callback__, args, ,
9501  "-*- texinfo -*-\n\
9502 @deftypefn {Built-in Function} {} __go_execute_callback__ (@var{h}, @var{name})\n\
9503 @deftypefnx {Built-in Function} {} __go_execute_callback__ (@var{h}, @var{name}, @var{param})\n\
9504 Undocumented internal function.\n\
9505 @end deftypefn")
9506 {
9507  octave_value retval;
9508 
9509  int nargin = args.length ();
9510 
9511  if (nargin == 2 || nargin == 3)
9512  {
9513  double val = args(0).double_value ();
9514 
9515  if (! error_state)
9516  {
9518 
9519  if (h.ok ())
9520  {
9521  std::string name = args(1).string_value ();
9522 
9523  if (! error_state)
9524  {
9525  if (nargin == 2)
9526  gh_manager::execute_callback (h, name);
9527  else
9528  gh_manager::execute_callback (h, name, args(2));
9529  }
9530  else
9531  error ("__go_execute_callback__: invalid callback name");
9532  }
9533  else
9534  error ("__go_execute_callback__: invalid graphics object (= %g)",
9535  val);
9536  }
9537  else
9538  error ("__go_execute_callback__: invalid graphics object");
9539  }
9540  else
9541  print_usage ();
9542 
9543  return retval;
9544 }
9545 
9546 DEFUN (__image_pixel_size__, args, ,
9547  "-*- texinfo -*-\n\
9548 @deftypefn {Built-in Function} {@var{px}, @var{py}} __image_pixel_size__ (@var{h})\n\
9549 Internal function: returns the pixel size of the image in normalized units.\n\
9550 @end deftypefn")
9551 {
9552  octave_value retval;
9553 
9554  int nargin = args.length ();
9555 
9556  if (nargin == 1)
9557  {
9558  double h = args(0).double_value ();
9559 
9560  if (! error_state)
9561  {
9563  if (fobj && fobj.isa ("image"))
9564  {
9565  image::properties& ip =
9566  dynamic_cast<image::properties&> (fobj.get_properties ());
9567 
9568  Matrix dp = Matrix (1, 2, 0);
9569  dp(0, 0) = ip.pixel_xsize ();
9570  dp(0, 1) = ip.pixel_ysize ();
9571  retval = dp;
9572  }
9573  else
9574  error ("__image_pixel_size__: object is not an image");
9575  }
9576  else
9577  error ("__image_pixel_size__: argument is not a handle");
9578  }
9579  else
9580  print_usage ();
9581 
9582  return retval;
9583 }
9584 
9586 
9588  : dtk (), available_toolkits (), loaded_toolkits ()
9589 {
9590 #if defined (HAVE_FLTK)
9591  dtk = display_info::display_available () ? "fltk" : "gnuplot";
9592 #else
9593  dtk = "gnuplot";
9594 #endif
9595 }
9596 
9597 void
9599 {
9600  instance = new gtk_manager ();
9601 
9602  if (instance)
9604 }
9605 
9608 {
9609  graphics_toolkit retval;
9610 
9612 
9613  if (pl == loaded_toolkits.end ())
9614  {
9616 
9617  if (pa != available_toolkits.end ())
9618  {
9619  octave_value_list args;
9620  args(0) = dtk;
9621  feval ("graphics_toolkit", args);
9622 
9623  if (! error_state)
9624  pl = loaded_toolkits.find (dtk);
9625 
9626  if (error_state || pl == loaded_toolkits.end ())
9627  error ("failed to load %s graphics toolkit", dtk.c_str ());
9628  else
9629  retval = pl->second;
9630  }
9631  else
9632  error ("default graphics toolkit '%s' is not available!",
9633  dtk.c_str ());
9634  }
9635  else
9636  retval = pl->second;
9637 
9638  return retval;
9639 }
9640 
9641 DEFUN (available_graphics_toolkits, , ,
9642  "-*- texinfo -*-\n\
9643 @deftypefn {Built-in Function} {} available_graphics_toolkits ()\n\
9644 Return a cell array of registered graphics toolkits.\n\
9645 @seealso{graphics_toolkit, register_graphics_toolkit}\n\
9646 @end deftypefn")
9647 {
9648  gh_manager::auto_lock guard;
9649 
9651 }
9652 
9653 DEFUN (register_graphics_toolkit, args, ,
9654  "-*- texinfo -*-\n\
9655 @deftypefn {Built-in Function} {} register_graphics_toolkit (@var{toolkit})\n\
9656 List @var{toolkit} as an available graphics toolkit.\n\
9657 @seealso{available_graphics_toolkits}\n\
9658 @end deftypefn")
9659 {
9660  octave_value retval;
9661 
9662  gh_manager::auto_lock guard;
9663 
9664  if (args.length () == 1)
9665  {
9666  std::string name = args(0).string_value ();
9667 
9668  if (! error_state)
9670  else
9671  error ("register_graphics_toolkit: expecting character string");
9672  }
9673  else
9674  print_usage ();
9675 
9676  return retval;
9677 }
9678 
9679 DEFUN (loaded_graphics_toolkits, , ,
9680  "-*- texinfo -*-\n\
9681 @deftypefn {Built-in Function} {} loaded_graphics_toolkits ()\n\
9682 Return a cell array of the currently loaded graphics toolkits.\n\
9683 @seealso{available_graphics_toolkits}\n\
9684 @end deftypefn")
9685 {
9686  gh_manager::auto_lock guard;
9687 
9689 }
9690 
9691 DEFUN (drawnow, args, ,
9692  "-*- texinfo -*-\n\
9693 @deftypefn {Built-in Function} {} drawnow ()\n\
9694 @deftypefnx {Built-in Function} {} drawnow (\"expose\")\n\
9695 @deftypefnx {Built-in Function} {} drawnow (@var{term}, @var{file}, @var{mono}, @var{debug_file})\n\
9696 Update figure windows and their children. The event queue is flushed and\n\
9697 any callbacks generated are executed. With the optional argument\n\
9698 @qcode{\"expose\"}, only graphic objects are updated and no other events or\n\
9699 callbacks are processed.\n\
9700 The third calling form of @code{drawnow} is for debugging and is\n\
9701 undocumented.\n\
9702 @end deftypefn")
9703 {
9704  static int drawnow_executing = 0;
9705 
9706  octave_value retval;
9707 
9708  gh_manager::lock ();
9709 
9710  unwind_protect frame;
9711  frame.protect_var (Vdrawnow_requested, false);
9712 
9713  frame.protect_var (drawnow_executing);
9714 
9715  if (++drawnow_executing <= 1)
9716  {
9717  if (args.length () == 0 || args.length () == 1)
9718  {
9719  Matrix hlist = gh_manager::figure_handle_list (true);
9720 
9721  for (int i = 0; ! error_state && i < hlist.length (); i++)
9722  {
9723  graphics_handle h = gh_manager::lookup (hlist(i));
9724 
9725  if (h.ok () && h != 0)
9726  {
9728  figure::properties& fprops
9729  = dynamic_cast <figure::properties&> (go.get_properties ());
9730 
9731  if (fprops.is_modified ())
9732  {
9733  if (fprops.is_visible ())
9734  {
9735  gh_manager::unlock ();
9736 
9737  fprops.get_toolkit ().redraw_figure (go);
9738 
9739  gh_manager::lock ();
9740  }
9741 
9742  fprops.set_modified (false);
9743  }
9744  }
9745  }
9746 
9747  bool do_events = true;
9748 
9749  if (args.length () == 1)
9750  {
9751  caseless_str val (args(0).string_value ());
9752 
9753  if (! error_state && val.compare ("expose"))
9754  do_events = false;
9755  else
9756  {
9757  error ("drawnow: invalid argument, expected 'expose' as argument");
9758  return retval;
9759  }
9760  }
9761 
9762  if (do_events)
9763  {
9764  gh_manager::unlock ();
9765 
9767 
9768  gh_manager::lock ();
9769  }
9770  }
9771  else if (args.length () >= 2 && args.length () <= 4)
9772  {
9773  std::string term, file, debug_file;
9774  bool mono;
9775 
9776  term = args(0).string_value ();
9777 
9778  if (! error_state)
9779  {
9780  file = args(1).string_value ();
9781 
9782  if (! error_state)
9783  {
9784  size_t pos = file.find_first_not_of ("|");
9785  if (pos > 0)
9786  file = file.substr (pos);
9787  else
9788  {
9789  pos = file.find_last_of (file_ops::dir_sep_chars ());
9790 
9791  if (pos != std::string::npos)
9792  {
9793  std::string dirname = file.substr (0, pos+1);
9794 
9795  file_stat fs (dirname);
9796 
9797  if (! (fs && fs.is_dir ()))
9798  {
9799  error ("drawnow: nonexistent directory '%s'",
9800  dirname.c_str ());
9801 
9802  return retval;
9803  }
9804  }
9805  }
9806 
9807  mono = (args.length () >= 3 ? args(2).bool_value () : false);
9808 
9809  if (! error_state)
9810  {
9811  debug_file = (args.length () > 3 ? args(3).string_value ()
9812  : "");
9813 
9814  if (! error_state)
9815  {
9816  graphics_handle h = gcf ();
9817 
9818  if (h.ok ())
9819  {
9821 
9822  gh_manager::unlock ();
9823 
9824  go.get_toolkit ().print_figure (go, term, file,
9825  mono, debug_file);
9826 
9827  gh_manager::lock ();
9828  }
9829  else
9830  error ("drawnow: nothing to draw");
9831  }
9832  else
9833  error ("drawnow: invalid DEBUG_FILE, expected a string value");
9834  }
9835  else
9836  error ("drawnow: invalid colormode MONO, expected a boolean value");
9837  }
9838  else
9839  error ("drawnow: invalid FILE, expected a string value");
9840  }
9841  else
9842  error ("drawnow: invalid terminal TERM, expected a string value");
9843  }
9844  else
9845  print_usage ();
9846  }
9847 
9848  gh_manager::unlock ();
9849 
9850  return retval;
9851 }
9852 
9853 DEFUN (addlistener, args, ,
9854  "-*- texinfo -*-\n\
9855 @deftypefn {Built-in Function} {} addlistener (@var{h}, @var{prop}, @var{fcn})\n\
9856 Register @var{fcn} as listener for the property @var{prop} of the graphics\n\
9857 object @var{h}. Property listeners are executed (in order of registration)\n\
9858 when the property is set. The new value is already available when the\n\
9859 listeners are executed.\n\
9860 \n\
9861 @var{prop} must be a string naming a valid property in @var{h}.\n\
9862 \n\
9863 @var{fcn} can be a function handle, a string or a cell array whose first\n\
9864 element is a function handle. If @var{fcn} is a function handle, the\n\
9865 corresponding function should accept at least 2 arguments, that will be\n\
9866 set to the object handle and the empty matrix respectively. If @var{fcn}\n\
9867 is a string, it must be any valid octave expression. If @var{fcn} is a cell\n\
9868 array, the first element must be a function handle with the same signature\n\
9869 as described above. The next elements of the cell array are passed\n\
9870 as additional arguments to the function.\n\
9871 \n\
9872 Example:\n\
9873 \n\
9874 @example\n\
9875 @group\n\
9876 function my_listener (h, dummy, p1)\n\
9877  fprintf (\"my_listener called with p1=%s\\n\", p1);\n\
9878 endfunction\n\
9879 \n\
9880 addlistener (gcf, \"position\", @{@@my_listener, \"my string\"@})\n\
9881 @end group\n\
9882 @end example\n\
9883 \n\
9884 @seealso{addproperty, hggroup}\n\
9885 @end deftypefn")
9886 {
9887  gh_manager::auto_lock guard;
9888 
9889  octave_value retval;
9890 
9891  if (args.length () >= 3 && args.length () <= 4)
9892  {
9893  double h = args(0).double_value ();
9894 
9895  if (! error_state)
9896  {
9897  std::string pname = args(1).string_value ();
9898 
9899  if (! error_state)
9900  {
9902 
9903  if (gh.ok ())
9904  {
9906 
9907  go.add_property_listener (pname, args(2), POSTSET);
9908 
9909  if (args.length () == 4)
9910  {
9911  caseless_str persistent = args(3).string_value ();
9912  if (persistent.compare ("persistent"))
9913  go.add_property_listener (pname, args(2), PERSISTENT);
9914  }
9915  }
9916  else
9917  error ("addlistener: invalid graphics object (= %g)",
9918  h);
9919  }
9920  else
9921  error ("addlistener: invalid property name, expected a string value");
9922  }
9923  else
9924  error ("addlistener: invalid handle");
9925  }
9926  else
9927  print_usage ();
9928 
9929  return retval;
9930 }
9931 
9932 DEFUN (dellistener, args, ,
9933  "-*- texinfo -*-\n\
9934 @deftypefn {Built-in Function} {} dellistener (@var{h}, @var{prop}, @var{fcn})\n\
9935 Remove the registration of @var{fcn} as a listener for the property\n\
9936 @var{prop} of the graphics object @var{h}. The function @var{fcn} must\n\
9937 be the same variable (not just the same value), as was passed to the\n\
9938 original call to @code{addlistener}.\n\
9939 \n\
9940 If @var{fcn} is not defined then all listener functions of @var{prop}\n\
9941 are removed.\n\
9942 \n\
9943 Example:\n\
9944 \n\
9945 @example\n\
9946 @group\n\
9947 function my_listener (h, dummy, p1)\n\
9948  fprintf (\"my_listener called with p1=%s\\n\", p1);\n\
9949 endfunction\n\
9950 \n\
9951 c = @{@@my_listener, \"my string\"@};\n\
9952 addlistener (gcf, \"position\", c);\n\
9953 dellistener (gcf, \"position\", c);\n\
9954 @end group\n\
9955 @end example\n\
9956 \n\
9957 @end deftypefn")
9958 {
9959  gh_manager::auto_lock guard;
9960 
9961  octave_value retval;
9962 
9963  if (args.length () == 3 || args.length () == 2)
9964  {
9965  double h = args(0).double_value ();
9966 
9967  if (! error_state)
9968  {
9969  std::string pname = args(1).string_value ();
9970 
9971  if (! error_state)
9972  {
9974 
9975  if (gh.ok ())
9976  {
9978 
9979  if (args.length () == 2)
9980  go.delete_property_listener (pname, octave_value (),
9981  POSTSET);
9982  else
9983  {
9984  caseless_str persistent = args(2).string_value ();
9985  if (persistent.compare ("persistent"))
9986  {
9987  go.delete_property_listener (pname, octave_value (),
9988  PERSISTENT);
9989  go.delete_property_listener (pname, octave_value (),
9990  POSTSET);
9991  }
9992  else
9993  go.delete_property_listener (pname, args(2), POSTSET);
9994  }
9995  }
9996  else
9997  error ("dellistener: invalid graphics object (= %g)",
9998  h);
9999  }
10000  else
10001  error ("dellistener: invalid property name, expected a string value");
10002  }
10003  else
10004  error ("dellistener: invalid handle");
10005  }
10006  else
10007  print_usage ();
10008 
10009  return retval;
10010 }
10011 
10012 DEFUN (addproperty, args, ,
10013  "-*- texinfo -*-\n\
10014 @deftypefn {Built-in Function} {} addproperty (@var{name}, @var{h}, @var{type})\n\
10015 @deftypefnx {Built-in Function} {} addproperty (@var{name}, @var{h}, @var{type}, @var{arg}, @dots{})\n\
10016 Create a new property named @var{name} in graphics object @var{h}.\n\
10017 @var{type} determines the type of the property to create. @var{args}\n\
10018 usually contains the default value of the property, but additional\n\
10019 arguments might be given, depending on the type of the property.\n\
10020 \n\
10021 The supported property types are:\n\
10022 \n\
10023 @table @code\n\
10024 @item string\n\
10025 A string property. @var{arg} contains the default string value.\n\
10026 \n\
10027 @item any\n\
10028 An @nospell{un-typed} property. This kind of property can hold any octave\n\
10029 value. @var{args} contains the default value.\n\
10030 \n\
10031 @item radio\n\
10032 A string property with a limited set of accepted values. The first\n\
10033 argument must be a string with all accepted values separated by\n\
10034 a vertical bar ('|'). The default value can be marked by enclosing\n\
10035 it with a '@{' '@}' pair. The default value may also be given as\n\
10036 an optional second string argument.\n\
10037 \n\
10038 @item boolean\n\
10039 A boolean property. This property type is equivalent to a radio\n\
10040 property with \"on|off\" as accepted values. @var{arg} contains\n\
10041 the default property value.\n\
10042 \n\
10043 @item double\n\
10044 A scalar double property. @var{arg} contains the default value.\n\
10045 \n\
10046 @item handle\n\
10047 A handle property. This kind of property holds the handle of a\n\
10048 graphics object. @var{arg} contains the default handle value.\n\
10049 When no default value is given, the property is initialized to\n\
10050 the empty matrix.\n\
10051 \n\
10052 @item data\n\
10053 A data (matrix) property. @var{arg} contains the default data\n\
10054 value. When no default value is given, the data is initialized to\n\
10055 the empty matrix.\n\
10056 \n\
10057 @item color\n\
10058 A color property. @var{arg} contains the default color value.\n\
10059 When no default color is given, the property is set to black.\n\
10060 An optional second string argument may be given to specify an\n\
10061 additional set of accepted string values (like a radio property).\n\
10062 @end table\n\
10063 \n\
10064 @var{type} may also be the concatenation of a core object type and\n\
10065 a valid property name for that object type. The property created\n\
10066 then has the same characteristics as the referenced property (type,\n\
10067 possible values, hidden state@dots{}). This allows to clone an existing\n\
10068 property into the graphics object @var{h}.\n\
10069 \n\
10070 Examples:\n\
10071 \n\
10072 @example\n\
10073 @group\n\
10074 addproperty (\"my_property\", gcf, \"string\", \"a string value\");\n\
10075 addproperty (\"my_radio\", gcf, \"radio\", \"val_1|val_2|@{val_3@}\");\n\
10076 addproperty (\"my_style\", gcf, \"linelinestyle\", \"--\");\n\
10077 @end group\n\
10078 @end example\n\
10079 \n\
10080 @seealso{addlistener, hggroup}\n\
10081 @end deftypefn")
10082 {
10083  gh_manager::auto_lock guard;
10084 
10085  octave_value retval;
10086 
10087  if (args.length () >= 3)
10088  {
10089  std::string name = args(0).string_value ();
10090 
10091  if (! error_state)
10092  {
10093  double h = args(1).double_value ();
10094 
10095  if (! error_state)
10096  {
10098 
10099  if (gh.ok ())
10100  {
10102 
10103  std::string type = args(2).string_value ();
10104 
10105  if (! error_state)
10106  {
10107  if (! go.get_properties ().has_property (name))
10108  {
10109  property p = property::create (name, gh, type,
10110  args.splice (0, 3));
10111 
10112  if (! error_state)
10113  go.get_properties ().insert_property (name, p);
10114  }
10115  else
10116  error ("addproperty: a '%s' property already exists in the graphics object",
10117  name.c_str ());
10118  }
10119  else
10120  error ("addproperty: invalid property TYPE, expected a string value");
10121  }
10122  else
10123  error ("addproperty: invalid graphics object (= %g)", h);
10124  }
10125  else
10126  error ("addproperty: invalid handle value");
10127  }
10128  else
10129  error ("addproperty: invalid property NAME, expected a string value");
10130  }
10131  else
10132  print_usage ();
10133 
10134  return retval;
10135 }
10136 
10138 get_property_from_handle (double handle, const std::string& property,
10139  const std::string& func)
10140 {
10141  gh_manager::auto_lock guard;
10142 
10143  graphics_object obj = gh_manager::get_object (handle);
10144  octave_value retval;
10145 
10146  if (obj)
10147  retval = obj.get (caseless_str (property));
10148  else
10149  error ("%s: invalid handle (= %g)", func.c_str (), handle);
10150 
10151  return retval;
10152 }
10153 
10154 bool
10155 set_property_in_handle (double handle, const std::string& property,
10156  const octave_value& arg, const std::string& func)
10157 {
10158  gh_manager::auto_lock guard;
10159 
10160  graphics_object obj = gh_manager::get_object (handle);
10161  int ret = false;
10162 
10163  if (obj)
10164  {
10165  obj.set (caseless_str (property), arg);
10166 
10167  if (! error_state)
10168  ret = true;
10169  }
10170  else
10171  error ("%s: invalid handle (= %g)", func.c_str (), handle);
10172 
10173  return ret;
10174 }
10175 
10176 static bool
10178 {
10179  octave_value_list args (2);
10180 
10181  args(0) = o1;
10182  args(1) = o2;
10183 
10184  octave_value_list result = feval ("isequal", args, 1);
10185 
10186  if (! error_state && result.length () > 0)
10187  return result(0).bool_value ();
10188 
10189  return false;
10190 }
10191 
10192 static std::map<uint32_t, bool> waitfor_results;
10193 
10194 static void
10195 cleanup_waitfor_id (uint32_t id)
10196 {
10197  waitfor_results.erase (id);
10198 }
10199 
10200 static void
10202  listener_mode mode = POSTSET)
10203 {
10204  Cell c = listener.cell_value ();
10205 
10206  if (c.numel () >= 4)
10207  {
10208  double h = c(2).double_value ();
10209 
10210  if (! error_state)
10211  {
10212  caseless_str pname = c(3).string_value ();
10213 
10214  if (! error_state)
10215  {
10216  gh_manager::auto_lock guard;
10217 
10218  graphics_handle handle = gh_manager::lookup (h);
10219 
10220  if (handle.ok ())
10221  {
10223 
10224  if (go.get_properties ().has_property (pname))
10225  {
10226  go.get_properties ().delete_listener (pname, listener,
10227  mode);
10228  if (mode == POSTSET)
10229  go.get_properties ().delete_listener (pname, listener,
10230  PERSISTENT);
10231  }
10232  }
10233  }
10234  }
10235  }
10236 }
10237 
10238 static void
10240 { do_cleanup_waitfor_listener (listener, POSTSET); }
10241 
10242 static void
10244 { do_cleanup_waitfor_listener (listener, PREDELETE); }
10245 
10246 static octave_value_list
10248 {
10249  if (args.length () > 3)
10250  {
10251  uint32_t id = args(2).uint32_scalar_value ().value ();
10252 
10253  if (! error_state)
10254  {
10255  if (args.length () > 5)
10256  {
10257  double h = args(0).double_value ();
10258 
10259  if (! error_state)
10260  {
10261  caseless_str pname = args(4).string_value ();
10262 
10263  if (! error_state)
10264  {
10265  gh_manager::auto_lock guard;
10266 
10267  graphics_handle handle = gh_manager::lookup (h);
10268 
10269  if (handle.ok ())
10270  {
10272  octave_value pvalue = go.get (pname);
10273 
10274  if (compare_property_values (pvalue, args(5)))
10275  waitfor_results[id] = true;
10276  }
10277  }
10278  }
10279  }
10280  else
10281  waitfor_results[id] = true;
10282  }
10283  }
10284 
10285  return octave_value_list ();
10286 }
10287 
10288 static octave_value_list
10290 {
10291  if (args.length () > 2)
10292  {
10293  uint32_t id = args(2).uint32_scalar_value ().value ();
10294 
10295  if (! error_state)
10296  waitfor_results[id] = true;
10297  }
10298 
10299  return octave_value_list ();
10300 }
10301 
10302 DEFUN (waitfor, args, ,
10303  "-*- texinfo -*-\n\
10304 @deftypefn {Built-in Function} {} waitfor (@var{h})\n\
10305 @deftypefnx {Built-in Function} {} waitfor (@var{h}, @var{prop})\n\
10306 @deftypefnx {Built-in Function} {} waitfor (@var{h}, @var{prop}, @var{value})\n\
10307 @deftypefnx {Built-in Function} {} waitfor (@dots{}, \"timeout\", @var{timeout})\n\
10308 Suspend the execution of the current program until a condition is\n\
10309 satisfied on the graphics handle @var{h}.\n\
10310 \n\
10311 While the program is suspended graphics events are still being processed\n\
10312 normally, allowing callbacks to modify the state of graphics objects. This\n\
10313 function is reentrant and can be called from a callback, while another\n\
10314 @code{waitfor} call is pending at the top-level.\n\
10315 \n\
10316 In the first form, program execution is suspended until the graphics object\n\
10317 @var{h} is destroyed. If the graphics handle is invalid, the function\n\
10318 returns immediately.\n\
10319 \n\
10320 In the second form, execution is suspended until the graphics object is\n\
10321 destroyed or the property named @var{prop} is modified. If the graphics\n\
10322 handle is invalid or the property does not exist, the function returns\n\
10323 immediately.\n\
10324 \n\
10325 In the third form, execution is suspended until the graphics object is\n\
10326 destroyed or the property named @var{prop} is set to @var{value}. The\n\
10327 function @code{isequal} is used to compare property values. If the graphics\n\
10328 handle is invalid, the property does not exist or the property is already\n\
10329 set to @var{value}, the function returns immediately.\n\
10330 \n\
10331 An optional timeout can be specified using the property @code{timeout}.\n\
10332 This timeout value is the number of seconds to wait for the condition to be\n\
10333 true. @var{timeout} must be at least 1. If a smaller value is specified, a\n\
10334 warning is issued and a value of 1 is used instead. If the timeout value is\n\
10335 not an integer, it is truncated towards 0.\n\
10336 \n\
10337 To define a condition on a property named @code{timeout}, use the string\n\
10338 @code{\\timeout} instead.\n\
10339 \n\
10340 In all cases, typing CTRL-C stops program execution immediately.\n\
10341 @seealso{waitforbuttonpress, isequal}\n\
10342 @end deftypefn")
10343 {
10344  if (args.length () > 0)
10345  {
10346  double h = args(0).double_value ();
10347 
10348  if (! error_state)
10349  {
10350  caseless_str pname;
10351 
10352  unwind_protect frame;
10353 
10354  static uint32_t id_counter = 0;
10355  uint32_t id = 0;
10356 
10357  int max_arg_index = 0;
10358  int timeout_index = -1;
10359 
10360  int timeout = 0;
10361 
10362  if (args.length () > 1)
10363  {
10364  pname = args(1).string_value ();
10365  if (! error_state
10366  && ! pname.empty ()
10367  && ! pname.compare ("timeout"))
10368  {
10369  if (pname.compare ("\\timeout"))
10370  pname = "timeout";
10371 
10372  static octave_value wf_listener;
10373 
10374  if (! wf_listener.is_defined ())
10375  wf_listener =
10377  "waitfor_listener"));
10378 
10379  max_arg_index++;
10380  if (args.length () > 2)
10381  {
10382  if (args(2).is_string ())
10383  {
10384  caseless_str s = args(2).string_value ();
10385 
10386  if (! error_state)
10387  {
10388  if (s.compare ("timeout"))
10389  timeout_index = 2;
10390  else
10391  max_arg_index++;
10392  }
10393  }
10394  else
10395  max_arg_index++;
10396  }
10397 
10398  Cell listener (1, max_arg_index >= 2 ? 5 : 4);
10399 
10400  id = id_counter++;
10401  frame.add_fcn (cleanup_waitfor_id, id);
10402  waitfor_results[id] = false;
10403 
10404  listener(0) = wf_listener;
10405  listener(1) = octave_uint32 (id);
10406  listener(2) = h;
10407  listener(3) = pname;
10408 
10409  if (max_arg_index >= 2)
10410  listener(4) = args(2);
10411 
10412  octave_value ov_listener (listener);
10413 
10414  gh_manager::auto_lock guard;
10415 
10416  graphics_handle handle = gh_manager::lookup (h);
10417 
10418  if (handle.ok ())
10419  {
10421 
10422  if (max_arg_index >= 2
10423  && compare_property_values (go.get (pname),
10424  args(2)))
10425  waitfor_results[id] = true;
10426  else
10427  {
10428 
10430  ov_listener);
10431  go.add_property_listener (pname, ov_listener,
10432  POSTSET);
10433  go.add_property_listener (pname, ov_listener,
10434  PERSISTENT);
10435 
10436  if (go.get_properties ()
10437  .has_dynamic_property (pname))
10438  {
10439  static octave_value wf_del_listener;
10440 
10441  if (! wf_del_listener.is_defined ())
10442  wf_del_listener =
10445  "waitfor_del_listener"));
10446 
10447  Cell del_listener (1, 4);
10448 
10449  del_listener(0) = wf_del_listener;
10450  del_listener(1) = octave_uint32 (id);
10451  del_listener(2) = h;
10452  del_listener(3) = pname;
10453 
10454  octave_value ov_del_listener (del_listener);
10455 
10457  ov_del_listener);
10458  go.add_property_listener (pname, ov_del_listener,
10459  PREDELETE);
10460  }
10461  }
10462  }
10463  }
10464  else if (error_state || pname.empty ())
10465  error ("waitfor: invalid property name, expected a non-empty string value");
10466  }
10467 
10468  if (! error_state
10469  && timeout_index < 0
10470  && args.length () > (max_arg_index + 1))
10471  {
10472  caseless_str s = args(max_arg_index + 1).string_value ();
10473 
10474  if (! error_state)
10475  {
10476  if (s.compare ("timeout"))
10477  timeout_index = max_arg_index + 1;
10478  else
10479  error ("waitfor: invalid parameter '%s'", s.c_str ());
10480  }
10481  else
10482  error ("waitfor: invalid parameter, expected 'timeout'");
10483  }
10484 
10485  if (! error_state && timeout_index >= 0)
10486  {
10487  if (args.length () > (timeout_index + 1))
10488  {
10489  timeout = static_cast<int>
10490  (args(timeout_index + 1).scalar_value ());
10491 
10492  if (! error_state)
10493  {
10494  if (timeout < 1)
10495  {
10496  warning ("waitfor: the timeout value must be >= 1, using 1 instead");
10497  timeout = 1;
10498  }
10499  }
10500  else
10501  error ("waitfor: invalid timeout value, expected a value >= 1");
10502  }
10503  else
10504  error ("waitfor: missing timeout value");
10505  }
10506 
10507  // FIXME: There is still a "hole" in the following loop. The code
10508  // assumes that an object handle is unique, which is a fair
10509  // assumption, except for figures. If a figure is destroyed
10510  // then recreated with the same figure ID, within the same
10511  // run of event hooks, then the figure destruction won't be
10512  // caught and the loop will not stop. This is an unlikely
10513  // possibility in practice, though.
10514  //
10515  // Using deletefcn callback is also unreliable as it could be
10516  // modified during a callback execution and the waitfor loop
10517  // would not stop.
10518  //
10519  // The only "good" implementation would require object
10520  // listeners, similar to property listeners.
10521 
10522  time_t start = 0;
10523 
10524  if (timeout > 0)
10525  start = time (0);
10526 
10527  while (! error_state)
10528  {
10529  if (true)
10530  {
10531  gh_manager::auto_lock guard;
10532 
10533  graphics_handle handle = gh_manager::lookup (h);
10534 
10535  if (handle.ok ())
10536  {
10537  if (! pname.empty () && waitfor_results[id])
10538  break;
10539  }
10540  else
10541  break;
10542  }
10543 
10544  octave_usleep (100000);
10545 
10546  OCTAVE_QUIT;
10547 
10549 
10550  if (timeout > 0)
10551  {
10552  if (start + timeout < time (0))
10553  break;
10554  }
10555  }
10556  }
10557  else
10558  error ("waitfor: invalid handle value.");
10559  }
10560  else
10561  print_usage ();
10562 
10563  return octave_value ();
10564 }