GNU Octave  4.0.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-2015 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 #include "octave-default-image.h"
62 
63 // forward declarations
64 static octave_value xget (const graphics_handle& h, const caseless_str& name);
65 
66 static void
67 gripe_set_invalid (const std::string& pname)
68 {
69  error ("set: invalid value for %s property", pname.c_str ());
70 }
71 
72 // Check to see that PNAME matches just one of PNAMES uniquely.
73 // Return the full name of the match, or an empty caseless_str object
74 // if there is no match, or the match is ambiguous.
75 
76 static caseless_str
77 validate_property_name (const std::string& who, const std::string& what,
78  const std::set<std::string>& pnames,
79  const caseless_str& pname)
80 {
81  size_t len = pname.length ();
82  std::set<std::string> matches;
83 
84  for (std::set<std::string>::const_iterator p = pnames.begin ();
85  p != pnames.end (); p++)
86  {
87  if (pname.compare (*p, len))
88  {
89  if (len == p->length ())
90  {
91  // Exact match.
92  return pname;
93  }
94 
95  matches.insert (*p);
96  }
97  }
98 
99  size_t num_matches = matches.size ();
100 
101  if (num_matches == 0)
102  {
103  error ("%s: unknown %s property %s",
104  who.c_str (), what.c_str (), pname.c_str ());
105  }
106  else if (num_matches > 1)
107  {
108  string_vector sv (matches);
109 
110  std::ostringstream os;
111 
112  sv.list_in_columns (os);
113 
114  std::string match_list = os.str ();
115 
116  error ("%s: ambiguous %s property name %s; possible matches:\n\n%s",
117  who.c_str (), what.c_str (), pname.c_str (), match_list.c_str ());
118  }
119  else if (num_matches == 1)
120  {
121  // Exact match was handled above.
122 
123  std::string possible_match = *(matches.begin ());
124 
125  warning_with_id ("Octave:abbreviated-property-match",
126  "%s: allowing %s to match %s property %s",
127  who.c_str (), pname.c_str (), what.c_str (),
128  possible_match.c_str ());
129 
130  return possible_match;
131  }
132 
133  return caseless_str ();
134 }
135 
136 static Matrix
138 {
139  Matrix cmap (64, 3, 0.0);
140 
141  // Produce X in the same manner as linspace so that
142  // jet_colormap and jet.m produce *exactly* the same result.
143  double delta = 1.0 / 63.0;
144 
145  for (octave_idx_type i = 0; i < 64; i++)
146  {
147  // This is the jet colormap. It would be nice to be able
148  // to feval the jet function but since there is a static
149  // property object that includes a colormap_property
150  // object, we need to initialize this before main is even
151  // called, so calling an interpreted function is not
152  // possible.
153 
154  double x = i*delta;
155 
156  if (x >= 3.0/8.0 && x < 5.0/8.0)
157  cmap(i,0) = 4.0 * x - 3.0/2.0;
158  else if (x >= 5.0/8.0 && x < 7.0/8.0)
159  cmap(i,0) = 1.0;
160  else if (x >= 7.0/8.0)
161  cmap(i,0) = -4.0 * x + 9.0/2.0;
162 
163  if (x >= 1.0/8.0 && x < 3.0/8.0)
164  cmap(i,1) = 4.0 * x - 1.0/2.0;
165  else if (x >= 3.0/8.0 && x < 5.0/8.0)
166  cmap(i,1) = 1.0;
167  else if (x >= 5.0/8.0 && x < 7.0/8.0)
168  cmap(i,1) = -4.0 * x + 7.0/2.0;
169 
170  if (x < 1.0/8.0)
171  cmap(i,2) = 4.0 * x + 1.0/2.0;
172  else if (x >= 1.0/8.0 && x < 3.0/8.0)
173  cmap(i,2) = 1.0;
174  else if (x >= 3.0/8.0 && x < 5.0/8.0)
175  cmap(i,2) = -4.0 * x + 5.0/2.0;
176  }
177 
178  return cmap;
179 }
180 
181 static double
183 {
184  return display_info::depth ();
185 }
186 
187 static Matrix
189 {
190  Matrix retval (1, 4, 1.0);
191 
192  retval(2) = display_info::width ();
193  retval(3) = display_info::height ();
194 
195  return retval;
196 }
197 
198 static double
200 {
201  return (display_info::x_dpi () + display_info::y_dpi ()) / 2;
202 }
203 
204 static Matrix
206 {
207  Matrix retval (7, 3, 0.0);
208 
209  retval(0,2) = 1.0;
210 
211  retval(1,1) = 0.5;
212 
213  retval(2,0) = 1.0;
214 
215  retval(3,1) = 0.75;
216  retval(3,2) = 0.75;
217 
218  retval(4,0) = 0.75;
219  retval(4,2) = 0.75;
220 
221  retval(5,0) = 0.75;
222  retval(5,1) = 0.75;
223 
224  retval(6,0) = 0.25;
225  retval(6,1) = 0.25;
226  retval(6,2) = 0.25;
227 
228  return retval;
229 }
230 
231 static Matrix
232 default_lim (bool logscale = false)
233 {
234  Matrix m (1, 2, 0);
235 
236  if (logscale)
237  {
238  m(0) = 0.1;
239  m(1) = 1.0;
240  }
241  else
242  m(1) = 1;
243 
244  return m;
245 }
246 
247 static Matrix
249 {
250  Matrix retval (1, 2);
251 
252  retval(0) = 0;
253  retval(1) = 1;
254 
255  return retval;
256 }
257 
258 static Matrix
260 {
261  Matrix m (64, 64, 0.0);
262  int i = 0;
263  for (int col = 0; col < 64; col++)
264  for (int row = 0; row < 64; row++)
265  {
266  m(col,row) = static_cast<double> (default_im_data[i]);
267  i++;
268  }
269 
270  return m;
271 }
272 
273 static Matrix
275 {
276  Matrix m (3, 3, 0.0);
277  for (int col = 0; col < 3; col++)
278  for (int row = 0; row < 3; row++)
279  m(row,col) = col+1;
280 
281  return m;
282 }
283 
284 static Matrix
286 {
287  Matrix m (3, 3, 0.0);
288  for (int row = 0; row < 3; row++)
289  for (int col = 0; col < 3; col++)
290  m(row,col) = row+1;
291 
292  return m;
293 }
294 
295 static Matrix
297 {
298  Matrix m (3, 3, 0.0);
299  for (int row = 0; row < 3; row++)
300  m(row,row) = 1.0;
301  return m;
302 }
303 
304 static Matrix
306 {
307  return default_surface_zdata ();
308 }
309 
310 static Matrix
312 {
313  Matrix m (1, 3, 1.0);
314  m(1) = 2.0;
315  m(2) = 3.0;
316  return m;
317 }
318 
319 static Matrix
321 {
322  Matrix m (3, 2, 0);
323  m(1) = 1.0;
324  m(3) = 1.0;
325  m(4) = 1.0;
326  return m;
327 }
328 
329 static Matrix
331 {
332  Matrix m (3, 1, 0.0);
333  m(1) = 1.0;
334  return m;
335 }
336 
337 static Matrix
339 {
340  Matrix m (3, 1, 1.0);
341  m(2) = 0.0;
342  return m;
343 }
344 
345 static Matrix
347 {
348  Matrix m (1, 4, 0.0);
349  m(0) = 0.13;
350  m(1) = 0.11;
351  m(2) = 0.775;
352  m(3) = 0.815;
353  return m;
354 }
355 
356 static Matrix
358 {
359  Matrix m (1, 4, 0.0);
360  m(2) = m(3) = 1.0;
361  return m;
362 }
363 
364 static Matrix
366 {
367  Matrix m (1, 2, 0.0);
368  m(1) = 90.0;
369  return m;
370 }
371 
372 static Matrix
374 {
375  Matrix m (1, 6, 0.0);
376  m(0) = 0.0;
377  m(1) = 0.2;
378  m(2) = 0.4;
379  m(3) = 0.6;
380  m(4) = 0.8;
381  m(5) = 1.0;
382  return m;
383 }
384 
385 static Matrix
387 {
388  Matrix m (1, 2, 0.0);
389  m(0) = 0.01;
390  m(1) = 0.025;
391  return m;
392 }
393 
394 static Matrix
396 {
397  Matrix m (1, 4, 0.0);
398  m(0) = 300;
399  m(1) = 200;
400  m(2) = 560;
401  m(3) = 420;
402  return m;
403 }
404 
405 static Matrix
407 {
408  Matrix m (1, 2, 0.0);
409  m(0) = 8.5;
410  m(1) = 11.0;
411  return m;
412 }
413 
414 static Matrix
416 {
417  Matrix m (1, 4, 0.0);
418  m(0) = 0.25;
419  m(1) = 2.50;
420  m(2) = 8.00;
421  m(3) = 6.00;
422  return m;
423 }
424 
425 static Matrix
427 {
428  Matrix retval (1, 4, 0.0);
429 
430  retval(0) = 0;
431  retval(1) = 0;
432  retval(2) = 80;
433  retval(3) = 30;
434 
435  return retval;
436 }
437 
438 static Matrix
440 {
441  Matrix retval (1, 2, 0.0);
442 
443  retval(0) = 0.01;
444  retval(1) = 0.1;
445 
446  return retval;
447 }
448 
449 static Matrix
451 {
452  Matrix retval (1, 4, 0.0);
453 
454  retval(0) = 0;
455  retval(1) = 0;
456  retval(2) = 1;
457  retval(3) = 1;
458 
459  return retval;
460 }
461 
462 static double
463 convert_font_size (double font_size, const caseless_str& from_units,
464  const caseless_str& to_units, double parent_height = 0)
465 {
466  // Simple case where from_units == to_units
467 
468  if (from_units.compare (to_units))
469  return font_size;
470 
471  // Converts the given fontsize using the following transformation:
472  // <old_font_size> => points => <new_font_size>
473 
474  double points_size = 0;
475  double res = 0;
476 
477  if (from_units.compare ("points"))
478  points_size = font_size;
479  else
480  {
481  res = xget (0, "screenpixelsperinch").double_value ();
482 
483  if (from_units.compare ("pixels"))
484  points_size = font_size * 72.0 / res;
485  else if (from_units.compare ("inches"))
486  points_size = font_size * 72.0;
487  else if (from_units.compare ("centimeters"))
488  points_size = font_size * 72.0 / 2.54;
489  else if (from_units.compare ("normalized"))
490  points_size = font_size * parent_height * 72.0 / res;
491  }
492 
493  double new_font_size = 0;
494 
495  if (to_units.compare ("points"))
496  new_font_size = points_size;
497  else
498  {
499  if (res <= 0)
500  res = xget (0, "screenpixelsperinch").double_value ();
501 
502  if (to_units.compare ("pixels"))
503  new_font_size = points_size * res / 72.0;
504  else if (to_units.compare ("inches"))
505  new_font_size = points_size / 72.0;
506  else if (to_units.compare ("centimeters"))
507  new_font_size = points_size * 2.54 / 72.0;
508  else if (to_units.compare ("normalized"))
509  {
510  // Avoid setting font size to (0/0) = NaN
511 
512  if (parent_height > 0)
513  new_font_size = points_size * res / (parent_height * 72.0);
514  }
515  }
516 
517  return new_font_size;
518 }
519 
520 static Matrix
521 convert_position (const Matrix& pos, const caseless_str& from_units,
522  const caseless_str& to_units, const Matrix& parent_dim)
523 {
524  Matrix retval (1, pos.numel ());
525  double res = 0;
526  bool is_rectangle = (pos.numel () == 4);
527  bool is_2d = (pos.numel () == 2);
528 
529  if (from_units.compare ("pixels"))
530  retval = pos;
531  else if (from_units.compare ("normalized"))
532  {
533  retval(0) = pos(0) * parent_dim(0) + 1;
534  retval(1) = pos(1) * parent_dim(1) + 1;
535  if (is_rectangle)
536  {
537  retval(2) = pos(2) * parent_dim(0);
538  retval(3) = pos(3) * parent_dim(1);
539  }
540  else if (! is_2d)
541  retval(2) = 0;
542  }
543  else if (from_units.compare ("characters"))
544  {
545  if (res <= 0)
546  res = xget (0, "screenpixelsperinch").double_value ();
547 
548  double f = 0.0;
549 
550  // FIXME: this assumes the system font is Helvetica 10pt
551  // (for which "x" requires 6x12 pixels at 74.951 pixels/inch)
552  f = 12.0 * res / 74.951;
553 
554  if (f > 0)
555  {
556  retval(0) = 0.5 * pos(0) * f;
557  retval(1) = pos(1) * f;
558  if (is_rectangle)
559  {
560  retval(2) = 0.5 * pos(2) * f;
561  retval(3) = pos(3) * f;
562  }
563  else if (! is_2d)
564  retval(2) = 0;
565  }
566  }
567  else
568  {
569  if (res <= 0)
570  res = xget (0, "screenpixelsperinch").double_value ();
571 
572  double f = 0.0;
573 
574  if (from_units.compare ("points"))
575  f = res / 72.0;
576  else if (from_units.compare ("inches"))
577  f = res;
578  else if (from_units.compare ("centimeters"))
579  f = res / 2.54;
580 
581  if (f > 0)
582  {
583  retval(0) = pos(0) * f + 1;
584  retval(1) = pos(1) * f + 1;
585  if (is_rectangle)
586  {
587  retval(2) = pos(2) * f;
588  retval(3) = pos(3) * f;
589  }
590  else if (! is_2d)
591  retval(2) = 0;
592  }
593  }
594 
595  if (! to_units.compare ("pixels"))
596  {
597  if (to_units.compare ("normalized"))
598  {
599  retval(0) = (retval(0) - 1) / parent_dim(0);
600  retval(1) = (retval(1) - 1) / parent_dim(1);
601  if (is_rectangle)
602  {
603  retval(2) /= parent_dim(0);
604  retval(3) /= parent_dim(1);
605  }
606  else if (! is_2d)
607  retval(2) = 0;
608  }
609  else if (to_units.compare ("characters"))
610  {
611  if (res <= 0)
612  res = xget (0, "screenpixelsperinch").double_value ();
613 
614  double f = 0.0;
615 
616  f = 12.0 * res / 74.951;
617 
618  if (f > 0)
619  {
620  retval(0) = 2 * retval(0) / f;
621  retval(1) = retval(1) / f;
622  if (is_rectangle)
623  {
624  retval(2) = 2 * retval(2) / f;
625  retval(3) = retval(3) / f;
626  }
627  else if (! is_2d)
628  retval(2) = 0;
629  }
630  }
631  else
632  {
633  if (res <= 0)
634  res = xget (0, "screenpixelsperinch").double_value ();
635 
636  double f = 0.0;
637 
638  if (to_units.compare ("points"))
639  f = res / 72.0;
640  else if (to_units.compare ("inches"))
641  f = res;
642  else if (to_units.compare ("centimeters"))
643  f = res / 2.54;
644 
645  if (f > 0)
646  {
647  retval(0) = (retval(0) - 1) / f;
648  retval(1) = (retval(1) - 1) / f;
649  if (is_rectangle)
650  {
651  retval(2) /= f;
652  retval(3) /= f;
653  }
654  else if (! is_2d)
655  retval(2) = 0;
656  }
657  }
658  }
659  else if (! is_rectangle && ! is_2d)
660  retval(2) = 0;
661 
662  return retval;
663 }
664 
665 static Matrix
667  const caseless_str& from_units,
668  const caseless_str& to_units)
669 {
671  graphics_object ax = go.get_ancestor ("axes");
672 
673  Matrix retval;
674 
675  if (ax.valid_object ())
676  {
677  const axes::properties& ax_props =
678  dynamic_cast<const axes::properties&> (ax.get_properties ());
679  graphics_xform ax_xform = ax_props.get_transform ();
680  bool is_rectangle = (pos.numel () == 4);
681  Matrix ax_bbox = ax_props.get_boundingbox (true),
682  ax_size = ax_bbox.extract_n (0, 2, 1, 2);
683 
684  if (from_units.compare ("data"))
685  {
686  if (is_rectangle)
687  {
688  ColumnVector v1 = ax_xform.transform (pos(0), pos(1), 0),
689  v2 = ax_xform.transform (pos(0) + pos(2),
690  pos(1) + pos(3), 0);
691 
692  retval.resize (1, 4);
693 
694  retval(0) = v1(0) - ax_bbox(0) + 1;
695  retval(1) = ax_bbox(1) + ax_bbox(3) - v1(1) + 1;
696  retval(2) = v2(0) - v1(0);
697  retval(3) = v1(1) - v2(1);
698  }
699  else
700  {
701  ColumnVector v = ax_xform.transform (pos(0), pos(1), pos(2));
702 
703  retval.resize (1, 3);
704 
705  retval(0) = v(0) - ax_bbox(0) + 1;
706  retval(1) = ax_bbox(1) + ax_bbox(3) - v(1) + 1;
707  retval(2) = 0;
708  }
709  }
710  else
711  retval = convert_position (pos, from_units, "pixels", ax_size);
712 
713  if (! to_units.compare ("pixels"))
714  {
715  if (to_units.compare ("data"))
716  {
717  if (is_rectangle)
718  {
719  ColumnVector v1, v2;
720  v1 = ax_xform.untransform (
721  retval(0) + ax_bbox(0) - 1,
722  ax_bbox(1) + ax_bbox(3) - retval(1) + 1);
723  v2 = ax_xform.untransform (
724  retval(0) + retval(2) + ax_bbox(0) - 1,
725  ax_bbox(1) + ax_bbox(3) - (retval(1) + retval(3)) + 1);
726 
727  retval.resize (1, 4);
728 
729  retval(0) = v1(0);
730  retval(1) = v1(1);
731  retval(2) = v2(0) - v1(0);
732  retval(3) = v2(1) - v1(1);
733  }
734  else
735  {
736  ColumnVector v;
737  v = ax_xform.untransform (
738  retval(0) + ax_bbox(0) - 1,
739  ax_bbox(1) + ax_bbox(3) - retval(1) + 1);
740 
741  retval.resize (1, 3);
742 
743  retval(0) = v(0);
744  retval(1) = v(1);
745  retval(2) = v(2);
746  }
747  }
748  else
749  retval = convert_position (retval, "pixels", to_units, ax_size);
750  }
751  }
752 
753  return retval;
754 }
755 
756 // This function always returns the screensize in pixels
757 static Matrix
759 {
761  Matrix sz = obj.get ("screensize").matrix_value ();
762  return convert_position (sz, obj.get ("units").string_value (), "pixels",
763  sz.extract_n (0, 2, 1, 2)).extract_n (0, 2, 1, 2);
764 }
765 
766 static void
767 convert_cdata_2 (bool is_scaled, bool is_real, double clim_0, double clim_1,
768  const double *cmapv, double x, octave_idx_type lda,
769  octave_idx_type nc, octave_idx_type i, double *av)
770 {
771  if (is_scaled)
772  x = xround ((nc - 1) * (x - clim_0) / (clim_1 - clim_0));
773  else if (is_real)
774  x = xround (x - 1);
775 
776  if (xisnan (x))
777  {
778  av[i] = x;
779  av[i+lda] = x;
780  av[i+2*lda] = x;
781  }
782  else
783  {
784  if (x < 0)
785  x = 0;
786  else if (x >= nc)
787  x = (nc - 1);
788 
789  octave_idx_type idx = static_cast<octave_idx_type> (x);
790 
791  av[i] = cmapv[idx];
792  av[i+lda] = cmapv[idx+nc];
793  av[i+2*lda] = cmapv[idx+2*nc];
794  }
795 }
796 
797 template <class T>
798 void
799 convert_cdata_1 (bool is_scaled, bool is_real, double clim_0, double clim_1,
800  const double *cmapv, const T *cv, octave_idx_type lda,
801  octave_idx_type nc, double *av)
802 {
803  for (octave_idx_type i = 0; i < lda; i++)
804  convert_cdata_2 (is_scaled, is_real,
805  clim_0, clim_1, cmapv, cv[i], lda, nc, i, av);
806 }
807 
808 static octave_value
809 convert_cdata (const base_properties& props, const octave_value& cdata,
810  bool is_scaled, int cdim)
811 {
812  dim_vector dv (cdata.dims ());
813 
814  // TrueColor data doesn't require conversion
815  if (dv.length () == cdim && dv(cdim-1) == 3)
816  return cdata;
817 
818  Matrix cmap (1, 3, 0.0);
819  Matrix clim (1, 2, 0.0);
820 
822  graphics_object fig = go.get_ancestor ("figure");
823 
824  if (fig.valid_object ())
825  {
826  Matrix _cmap = fig.get (caseless_str ("colormap")).matrix_value ();
827 
828  if (! error_state)
829  cmap = _cmap;
830  }
831 
832  if (is_scaled)
833  {
834  graphics_object ax = go.get_ancestor ("axes");
835 
836  if (ax.valid_object ())
837  {
838  Matrix _clim = ax.get (caseless_str ("clim")).matrix_value ();
839 
840  if (! error_state)
841  clim = _clim;
842  }
843  }
844 
845  dv.resize (cdim);
846  dv(cdim-1) = 3;
847 
848  NDArray a (dv);
849 
850  octave_idx_type lda = a.numel () / static_cast<octave_idx_type> (3);
851  octave_idx_type nc = cmap.rows ();
852 
853  double *av = a.fortran_vec ();
854  const double *cmapv = cmap.data ();
855 
856  double clim_0 = clim(0);
857  double clim_1 = clim(1);
858 
859  // FIXME: There is a lot of processing time spent just on data conversion
860  // both here in graphics.cc and again in gl-render.cc. There must
861  // be room for improvement! Here a macro expands to a templated
862  // function which in turn calls another function (covert_cdata_2).
863  // And in gl-render.cc (opengl_renderer::draw_image), only GLfloat
864  // is supported anyways so there is another double for loop across
865  // height and width to convert all of the input data to GLfloat.
866 
867 #define CONVERT_CDATA_1(ARRAY_T, VAL_FN, IS_REAL) \
868  do \
869  { \
870  ARRAY_T tmp = cdata. VAL_FN ## array_value (); \
871  \
872  convert_cdata_1 (is_scaled, IS_REAL, clim_0, clim_1, cmapv, \
873  tmp.data (), lda, nc, av); \
874  } \
875  while (0)
876 
877  if (cdata.is_uint8_type ())
878  CONVERT_CDATA_1 (uint8NDArray, uint8_, false);
879  else if (cdata.is_uint16_type ())
880  CONVERT_CDATA_1 (uint16NDArray, uint16_, false);
881  else if (cdata.is_double_type ())
882  CONVERT_CDATA_1 (NDArray, , true);
883  else if (cdata.is_single_type ())
884  CONVERT_CDATA_1 (FloatNDArray, float_, true);
885  else if (cdata.is_bool_type ())
886  CONVERT_CDATA_1 (boolNDArray, bool_, false);
887  else
888  error ("unsupported type for cdata (= %s)", cdata.type_name ().c_str ());
889 
890 #undef CONVERT_CDATA_1
891 
892  return octave_value (a);
893 }
894 
895 template<class T>
896 static void
897 get_array_limits (const Array<T>& m, double& emin, double& emax,
898  double& eminp, double& emaxp)
899 {
900  const T *data = m.data ();
901  octave_idx_type n = m.numel ();
902 
903  for (octave_idx_type i = 0; i < n; i++)
904  {
905  double e = double (data[i]);
906 
907  // Don't need to test for NaN here as NaN>x and NaN<x is always false
908  if (! xisinf (e))
909  {
910  if (e < emin)
911  emin = e;
912 
913  if (e > emax)
914  emax = e;
915 
916  if (e > 0 && e < eminp)
917  eminp = e;
918 
919  if (e < 0 && e > emaxp)
920  emaxp = e;
921  }
922  }
923 }
924 
925 static bool
927  caseless_str& rest)
928 {
929  int len = name.length ();
930  int offset = 0;
931  bool result = false;
932 
933  if (len >= 4)
934  {
935  caseless_str pfx = name.substr (0, 4);
936 
937  if (pfx.compare ("axes") || pfx.compare ("line")
938  || pfx.compare ("text"))
939  offset = 4;
940  else if (len >= 5)
941  {
942  pfx = name.substr (0, 5);
943 
944  if (pfx.compare ("image") || pfx.compare ("patch"))
945  offset = 5;
946  else if (len >= 6)
947  {
948  pfx = name.substr (0, 6);
949 
950  if (pfx.compare ("figure") || pfx.compare ("uimenu"))
951  offset = 6;
952  else if (len >= 7)
953  {
954  pfx = name.substr (0, 7);
955 
956  if (pfx.compare ("surface") || pfx.compare ("hggroup")
957  || pfx.compare ("uipanel"))
958  offset = 7;
959  else if (len >= 9)
960  {
961  pfx = name.substr (0, 9);
962 
963  if (pfx.compare ("uicontrol")
964  || pfx.compare ("uitoolbar"))
965  offset = 9;
966  else if (len >= 10)
967  {
968  pfx = name.substr (0, 10);
969 
970  if (pfx.compare ("uipushtool"))
971  offset = 10;
972  else if (len >= 12)
973  {
974  pfx = name.substr (0, 12);
975 
976  if (pfx.compare ("uitoggletool"))
977  offset = 12;
978  else if (len >= 13)
979  {
980  pfx = name.substr (0, 13);
981 
982  if (pfx.compare ("uicontextmenu"))
983  offset = 13;
984  }
985  }
986  }
987  }
988  }
989  }
990  }
991 
992  if (offset > 0)
993  {
994  go_name = pfx;
995  rest = name.substr (offset);
996  result = true;
997  }
998  }
999 
1000  return result;
1001 }
1002 
1003 static base_graphics_object*
1005  const graphics_handle& h = graphics_handle (),
1006  const graphics_handle& p = graphics_handle ())
1007 {
1008  base_graphics_object *go = 0;
1009 
1010  if (type.compare ("figure"))
1011  go = new figure (h, p);
1012  else if (type.compare ("axes"))
1013  go = new axes (h, p);
1014  else if (type.compare ("line"))
1015  go = new line (h, p);
1016  else if (type.compare ("text"))
1017  go = new text (h, p);
1018  else if (type.compare ("image"))
1019  go = new image (h, p);
1020  else if (type.compare ("patch"))
1021  go = new patch (h, p);
1022  else if (type.compare ("surface"))
1023  go = new surface (h, p);
1024  else if (type.compare ("hggroup"))
1025  go = new hggroup (h, p);
1026  else if (type.compare ("uimenu"))
1027  go = new uimenu (h, p);
1028  else if (type.compare ("uicontrol"))
1029  go = new uicontrol (h, p);
1030  else if (type.compare ("uipanel"))
1031  go = new uipanel (h, p);
1032  else if (type.compare ("uicontextmenu"))
1033  go = new uicontextmenu (h, p);
1034  else if (type.compare ("uitoolbar"))
1035  go = new uitoolbar (h, p);
1036  else if (type.compare ("uipushtool"))
1037  go = new uipushtool (h, p);
1038  else if (type.compare ("uitoggletool"))
1039  go = new uitoggletool (h, p);
1040  return go;
1041 }
1042 
1043 // ---------------------------------------------------------------------
1044 
1045 bool
1046 base_property::set (const octave_value& v, bool do_run, bool do_notify_toolkit)
1047 {
1048  if (do_set (v))
1049  {
1050 
1051  // Notify graphics toolkit.
1052  if (id >= 0 && do_notify_toolkit)
1053  {
1055  if (go)
1056  go.update (id);
1057  }
1058 
1059  // run listeners
1060  if (do_run && ! error_state)
1062 
1063  return true;
1064  }
1065 
1066  return false;
1067 }
1068 
1069 
1070 void
1072 {
1073  const octave_value_list& l = listeners[mode];
1074 
1075  for (int i = 0; i < l.length (); i++)
1076  {
1078 
1079  if (error_state)
1080  break;
1081  }
1082 }
1083 
1084 radio_values::radio_values (const std::string& opt_string)
1085  : default_val (), possible_vals ()
1086 {
1087  size_t beg = 0;
1088  size_t len = opt_string.length ();
1089  bool done = len == 0;
1090 
1091  while (! done)
1092  {
1093  size_t end = opt_string.find ('|', beg);
1094 
1095  if (end == std::string::npos)
1096  {
1097  end = len;
1098  done = true;
1099  }
1100 
1101  std::string t = opt_string.substr (beg, end-beg);
1102 
1103  // Might want more error checking here...
1104  if (t[0] == '{')
1105  {
1106  t = t.substr (1, t.length () - 2);
1107  default_val = t;
1108  }
1109  else if (beg == 0) // ensure default value
1110  default_val = t;
1111 
1112  possible_vals.insert (t);
1113 
1114  beg = end + 1;
1115  }
1116 }
1117 
1118 std::string
1120 {
1121  std::string retval;
1122  for (std::set<caseless_str>::const_iterator it = possible_vals.begin ();
1123  it != possible_vals.end (); it++)
1124  {
1125  if (retval.empty ())
1126  {
1127  if (*it == default_value ())
1128  retval = "{" + *it + "}";
1129  else
1130  retval = *it;
1131  }
1132  else
1133  {
1134  if (*it == default_value ())
1135  retval += " | {" + *it + "}";
1136  else
1137  retval += " | " + *it;
1138  }
1139  }
1140 
1141  if (! retval.empty ())
1142  retval = "[ " + retval + " ]";
1143 
1144  return retval;
1145 }
1146 
1147 Cell
1149 {
1150  octave_idx_type i = 0;
1151  Cell retval (nelem (), 1);
1152  for (std::set<caseless_str>::const_iterator it = possible_vals.begin ();
1153  it != possible_vals.end (); it++)
1154  retval(i++) = std::string (*it);
1155  return retval;
1156 }
1157 
1158 bool
1159 color_values::str2rgb (const std::string& str_arg)
1160 {
1161  bool retval = true;
1162 
1163  double tmp_rgb[3] = {0, 0, 0};
1164 
1165  std::string str = str_arg;
1166  unsigned int len = str.length ();
1167 
1168  std::transform (str.begin (), str.end (), str.begin (), tolower);
1169 
1170  if (str.compare (0, len, "blue", 0, len) == 0)
1171  tmp_rgb[2] = 1;
1172  else if (str.compare (0, len, "black", 0, len) == 0
1173  || str.compare (0, len, "k", 0, len) == 0)
1174  tmp_rgb[0] = tmp_rgb[1] = tmp_rgb[2] = 0;
1175  else if (str.compare (0, len, "red", 0, len) == 0)
1176  tmp_rgb[0] = 1;
1177  else if (str.compare (0, len, "green", 0, len) == 0)
1178  tmp_rgb[1] = 1;
1179  else if (str.compare (0, len, "yellow", 0, len) == 0)
1180  tmp_rgb[0] = tmp_rgb[1] = 1;
1181  else if (str.compare (0, len, "magenta", 0, len) == 0)
1182  tmp_rgb[0] = tmp_rgb[2] = 1;
1183  else if (str.compare (0, len, "cyan", 0, len) == 0)
1184  tmp_rgb[1] = tmp_rgb[2] = 1;
1185  else if (str.compare (0, len, "white", 0, len) == 0
1186  || str.compare (0, len, "w", 0, len) == 0)
1187  tmp_rgb[0] = tmp_rgb[1] = tmp_rgb[2] = 1;
1188  else
1189  retval = false;
1190 
1191  if (retval)
1192  {
1193  for (int i = 0; i < 3; i++)
1194  xrgb(i) = tmp_rgb[i];
1195  }
1196 
1197  return retval;
1198 }
1199 
1200 bool
1202 {
1203  if (val.is_string ())
1204  {
1205  std::string s = val.string_value ();
1206 
1207  if (! s.empty ())
1208  {
1209  std::string match;
1210 
1211  if (radio_val.contains (s, match))
1212  {
1213  if (current_type != radio_t || match != current_val)
1214  {
1215  if (s.length () != match.length ())
1216  warning_with_id ("Octave:abbreviated-property-match",
1217  "%s: allowing %s to match %s value %s",
1218  "set", s.c_str (), get_name ().c_str (),
1219  match.c_str ());
1220  current_val = match;
1222  return true;
1223  }
1224  }
1225  else
1226  {
1227  color_values col (s);
1228  if (! error_state)
1229  {
1230  if (current_type != color_t || col != color_val)
1231  {
1232  color_val = col;
1234  return true;
1235  }
1236  }
1237  else
1238  error ("invalid value for color property \"%s\" (value = %s)",
1239  get_name ().c_str (), s.c_str ());
1240  }
1241  }
1242  else
1243  error ("invalid value for color property \"%s\"",
1244  get_name ().c_str ());
1245  }
1246  else if (val.is_numeric_type ())
1247  {
1248  Matrix m = val.matrix_value ();
1249 
1250  if (m.numel () == 3)
1251  {
1252  color_values col (m(0), m(1), m(2));
1253  if (! error_state)
1254  {
1255  if (current_type != color_t || col != color_val)
1256  {
1257  color_val = col;
1259  return true;
1260  }
1261  }
1262  }
1263  else
1264  error ("invalid value for color property \"%s\"",
1265  get_name ().c_str ());
1266  }
1267  else
1268  error ("invalid value for color property \"%s\"",
1269  get_name ().c_str ());
1270 
1271  return false;
1272 }
1273 
1274 bool
1276 {
1277  if (val.is_string ())
1278  {
1279  std::string s = val.string_value ();
1280  std::string match;
1281 
1282  if (! s.empty () && radio_val.contains (s, match))
1283  {
1284  if (current_type != radio_t || match != current_val)
1285  {
1286  if (s.length () != match.length ())
1287  warning_with_id ("Octave:abbreviated-property-match",
1288  "%s: allowing %s to match %s value %s",
1289  "set", s.c_str (), get_name ().c_str (),
1290  match.c_str ());
1291  current_val = match;
1293  return true;
1294  }
1295  }
1296  else
1297  error ("invalid value for double_radio property \"%s\"",
1298  get_name ().c_str ());
1299  }
1300  else if (val.is_scalar_type () && val.is_real_type ())
1301  {
1302  double new_dval = val.double_value ();
1303 
1304  if (current_type != double_t || new_dval != dval)
1305  {
1306  dval = new_dval;
1308  return true;
1309  }
1310  }
1311  else
1312  error ("invalid value for double_radio property \"%s\"",
1313  get_name ().c_str ());
1314 
1315  return false;
1316 }
1317 
1318 bool
1320 {
1321  bool xok = false;
1322 
1323  // check value type
1324  if (type_constraints.size () > 0)
1325  {
1326  if (type_constraints.find (v.class_name ()) != type_constraints.end ())
1327  xok = true;
1328 
1329  // check if complex is allowed (it's also of class "double", so
1330  // checking that alone is not enough to ensure real type)
1331  if (type_constraints.find ("real") != type_constraints.end ()
1332  && v.is_complex_type ())
1333  xok = false;
1334  }
1335  else
1336  xok = v.is_numeric_type () || v.is_bool_scalar ();
1337 
1338  if (xok)
1339  {
1340  if (size_constraints.size () == 0)
1341  return true;
1342 
1343  dim_vector vdims = v.dims ();
1344  int vlen = vdims.length ();
1345 
1346  xok = false;
1347 
1348  // check dimensional size constraints until a match is found
1349  for (std::list<dim_vector>::const_iterator it = size_constraints.begin ();
1350  ! xok && it != size_constraints.end (); ++it)
1351  {
1352  dim_vector itdims = (*it);
1353 
1354  if (itdims.length () == vlen)
1355  {
1356  xok = true;
1357 
1358  for (int i = 0; xok && i < vlen; i++)
1359  {
1360  if (itdims(i) > 0)
1361  {
1362  if (itdims(i) != vdims(i))
1363  xok = false;
1364  }
1365  else if (v.is_empty ())
1366  break;
1367  }
1368  }
1369  }
1370  }
1371 
1372  return xok;
1373 }
1374 
1375 bool
1377 {
1378  if (data.type_name () == v.type_name ())
1379  {
1380  if (data.dims () == v.dims ())
1381  {
1382 
1383 #define CHECK_ARRAY_EQUAL(T,F,A) \
1384  { \
1385  if (data.numel () == 1) \
1386  return data.F ## scalar_value () == \
1387  v.F ## scalar_value (); \
1388  else \
1389  { \
1390  /* Keep copy of array_value to allow sparse/bool arrays */ \
1391  /* that are converted, to not be deallocated early */ \
1392  const A m1 = data.F ## array_value (); \
1393  const T* d1 = m1.data (); \
1394  const A m2 = v.F ## array_value (); \
1395  const T* d2 = m2.data ();\
1396  \
1397  bool flag = true; \
1398  \
1399  for (int i = 0; flag && i < data.numel (); i++) \
1400  if (d1[i] != d2[i]) \
1401  flag = false; \
1402  \
1403  return flag; \
1404  } \
1405  }
1406 
1407  if (data.is_double_type () || data.is_bool_type ())
1408  CHECK_ARRAY_EQUAL (double, , NDArray)
1409  else if (data.is_single_type ())
1410  CHECK_ARRAY_EQUAL (float, float_, FloatNDArray)
1411  else if (data.is_int8_type ())
1413  else if (data.is_int16_type ())
1415  else if (data.is_int32_type ())
1417  else if (data.is_int64_type ())
1419  else if (data.is_uint8_type ())
1421  else if (data.is_uint16_type ())
1423  else if (data.is_uint32_type ())
1425  else if (data.is_uint64_type ())
1427  }
1428  }
1429 
1430  return false;
1431 }
1432 
1433 void
1435 {
1436  xmin = xminp = octave_Inf;
1437  xmax = xmaxp = -octave_Inf;
1438 
1439  if (! data.is_empty ())
1440  {
1441  if (data.is_integer_type ())
1442  {
1443  if (data.is_int8_type ())
1445  xmin, xmax, xminp, xmaxp);
1446  else if (data.is_uint8_type ())
1448  xmin, xmax, xminp, xmaxp);
1449  else if (data.is_int16_type ())
1451  xmin, xmax, xminp, xmaxp);
1452  else if (data.is_uint16_type ())
1454  xmin, xmax, xminp, xmaxp);
1455  else if (data.is_int32_type ())
1457  xmin, xmax, xminp, xmaxp);
1458  else if (data.is_uint32_type ())
1460  xmin, xmax, xminp, xmaxp);
1461  else if (data.is_int64_type ())
1463  xmin, xmax, xminp, xmaxp);
1464  else if (data.is_uint64_type ())
1466  xmin, xmax, xminp, xmaxp);
1467  }
1468  else
1470  }
1471 }
1472 
1473 bool
1475 {
1476  // Users may want to use empty matrix to reset a handle property
1477  if (v.is_empty ())
1478  {
1479  if (! get ().is_empty ())
1480  {
1482  return true;
1483  }
1484  else
1485  return false;
1486  }
1487 
1488  double dv = v.double_value ();
1489 
1490  if (! error_state)
1491  {
1493 
1494  if (xisnan (gh.value ()) || gh.ok ())
1495  {
1496  if (current_val != gh)
1497  {
1498  current_val = gh;
1499  return true;
1500  }
1501  }
1502  else
1503  error ("set: invalid graphics handle (= %g) for property \"%s\"",
1504  dv, get_name ().c_str ());
1505  }
1506  else
1507  error ("set: invalid graphics handle for property \"%s\"",
1508  get_name ().c_str ());
1509 
1510  return false;
1511 }
1512 
1513 Matrix
1514 children_property::do_get_children (bool return_hidden) const
1515 {
1516  Matrix retval (children_list.size (), 1);
1517  octave_idx_type k = 0;
1518 
1520 
1521  root_figure::properties& props =
1522  dynamic_cast<root_figure::properties&> (go.get_properties ());
1523 
1524  if (! props.is_showhiddenhandles ())
1525  {
1526  for (const_children_list_iterator p = children_list.begin ();
1527  p != children_list.end (); p++)
1528  {
1529  graphics_handle kid = *p;
1530 
1532  {
1533  if (! return_hidden)
1534  retval(k++) = *p;
1535  }
1536  else if (return_hidden)
1537  retval(k++) = *p;
1538  }
1539 
1540  retval.resize (k, 1);
1541  }
1542  else
1543  {
1544  for (const_children_list_iterator p = children_list.begin ();
1545  p != children_list.end (); p++)
1546  retval(k++) = *p;
1547  }
1548 
1549  return retval;
1550 }
1551 
1552 void
1554 {
1555  for (children_list_iterator p = children_list.begin ();
1556  p != children_list.end (); p++)
1557  {
1559 
1560  if (go.valid_object ())
1561  gh_manager::free (*p);
1562 
1563  }
1564 
1565  if (clear)
1566  children_list.clear ();
1567 }
1568 
1569 bool
1571 {
1572  // case 1: function handle
1573  // case 2: cell array with first element being a function handle
1574  // case 3: string corresponding to known function name
1575  // case 4: evaluatable string
1576  // case 5: empty matrix
1577 
1578  if (v.is_function_handle ())
1579  return true;
1580  else if (v.is_string ())
1581  // complete validation will be done at execution-time
1582  return true;
1583  else if (v.is_cell () && v.length () > 0
1584  && (v.rows () == 1 || v.columns () == 1)
1585  && v.cell_value ()(0).is_function_handle ())
1586  return true;
1587  else if (v.is_empty ())
1588  return true;
1589 
1590  return false;
1591 }
1592 
1593 // If TRUE, we are executing any callback function, or the functions it
1594 // calls. Used to determine handle visibility inside callback
1595 // functions.
1596 static bool executing_callback = false;
1597 
1598 void
1600 {
1601  unwind_protect frame;
1602 
1603  // We are executing the callback function associated with this
1604  // callback property. When set to true, we avoid recursive calls to
1605  // callback routines.
1606  frame.protect_var (executing);
1607 
1608  // We are executing a callback function, so allow handles that have
1609  // their handlevisibility property set to "callback" to be visible.
1610  frame.protect_var (executing_callback);
1611 
1612  if (! executing)
1613  {
1614  executing = true;
1615  executing_callback = true;
1616 
1617  if (callback.is_defined () && ! callback.is_empty ())
1619  }
1620 }
1621 
1622 // Used to cache dummy graphics objects from which dynamic
1623 // properties can be cloned.
1624 static std::map<caseless_str, graphics_object> dprop_obj_map;
1625 
1626 property
1627 property::create (const std::string& name, const graphics_handle& h,
1628  const caseless_str& type, const octave_value_list& args)
1629 {
1630  property retval;
1631 
1632  if (type.compare ("string"))
1633  {
1634  std::string val = (args.length () > 0 ? args(0).string_value () : "");
1635 
1636  if (! error_state)
1637  retval = property (new string_property (name, h, val));
1638  }
1639  else if (type.compare ("any"))
1640  {
1641  octave_value val = args.length () > 0 ? args(0)
1642  : octave_value (Matrix ());
1643 
1644  retval = property (new any_property (name, h, val));
1645  }
1646  else if (type.compare ("radio"))
1647  {
1648  if (args.length () > 0)
1649  {
1650  std::string vals = args(0).string_value ();
1651 
1652  if (! error_state)
1653  {
1654  retval = property (new radio_property (name, h, vals));
1655 
1656  if (args.length () > 1)
1657  retval.set (args(1));
1658  }
1659  else
1660  error ("addproperty: invalid argument for radio property, expected a string value");
1661  }
1662  else
1663  error ("addproperty: missing possible values for radio property");
1664  }
1665  else if (type.compare ("double"))
1666  {
1667  double d = (args.length () > 0 ? args(0).double_value () : 0);
1668 
1669  if (! error_state)
1670  retval = property (new double_property (name, h, d));
1671  }
1672  else if (type.compare ("handle"))
1673  {
1674  double hh = (args.length () > 0 ? args(0).double_value () : octave_NaN);
1675 
1676  if (! error_state)
1677  {
1678  graphics_handle gh (hh);
1679 
1680  retval = property (new handle_property (name, h, gh));
1681  }
1682  }
1683  else if (type.compare ("boolean"))
1684  {
1685  retval = property (new bool_property (name, h, false));
1686 
1687  if (args.length () > 0)
1688  retval.set (args(0));
1689  }
1690  else if (type.compare ("data"))
1691  {
1692  retval = property (new array_property (name, h, Matrix ()));
1693 
1694  if (args.length () > 0)
1695  {
1696  retval.set (args(0));
1697 
1698  // FIXME: additional argument could define constraints,
1699  // but is this really useful?
1700  }
1701  }
1702  else if (type.compare ("color"))
1703  {
1704  color_values cv (0, 0, 0);
1705  radio_values rv;
1706 
1707  if (args.length () > 1)
1708  rv = radio_values (args(1).string_value ());
1709 
1710  if (! error_state)
1711  {
1712  retval = property (new color_property (name, h, cv, rv));
1713 
1714  if (! error_state)
1715  {
1716  if (args.length () > 0 && ! args(0).is_empty ())
1717  retval.set (args(0));
1718  else
1719  retval.set (rv.default_value ());
1720  }
1721  }
1722  }
1723  else
1724  {
1725  caseless_str go_name, go_rest;
1726 
1727  if (lookup_object_name (type, go_name, go_rest))
1728  {
1729  graphics_object go;
1730 
1731  std::map<caseless_str, graphics_object>::const_iterator it =
1732  dprop_obj_map.find (go_name);
1733 
1734  if (it == dprop_obj_map.end ())
1735  {
1736  base_graphics_object *bgo =
1738 
1739  if (bgo)
1740  {
1741  go = graphics_object (bgo);
1742 
1743  dprop_obj_map[go_name] = go;
1744  }
1745  }
1746  else
1747  go = it->second;
1748 
1749  if (go.valid_object ())
1750  {
1751  property prop = go.get_properties ().get_property (go_rest);
1752 
1753  if (! error_state)
1754  {
1755  retval = prop.clone ();
1756 
1757  retval.set_parent (h);
1758  retval.set_name (name);
1759 
1760  if (args.length () > 0)
1761  retval.set (args(0));
1762  }
1763  }
1764  else
1765  error ("addproperty: invalid object type (= %s)",
1766  go_name.c_str ());
1767  }
1768  else
1769  error ("addproperty: unsupported type for dynamic property (= %s)",
1770  type.c_str ());
1771  }
1772 
1773  return retval;
1774 }
1775 
1776 static void
1778 {
1780 
1781  if (go)
1782  {
1783  Matrix children = go.get_properties ().get_all_children ();
1784 
1785  for (int k = 0; k < children.numel (); k++)
1786  finalize_r (children(k));
1787 
1788  go.finalize ();
1789  }
1790 }
1791 
1792 static void
1794 {
1796 
1797  if (go)
1798  {
1799  Matrix children = go.get_properties ().get_all_children ();
1800 
1801  go.initialize ();
1802 
1803  for (int k = 0; k < children.numel (); k++)
1804  initialize_r (children(k));
1805  }
1806 }
1807 
1808 void
1810 {
1811  if (toolkit)
1813 
1814  toolkit = b;
1816  __plot_stream__ = Matrix ();
1817 
1818  if (toolkit)
1820 
1821  mark_modified ();
1822 }
1823 
1824 void
1826 {
1827  if (! error_state)
1828  {
1829  std::string direction = "in";
1830 
1831  octave_value val = val_arg;
1832 
1833  if (val.is_string ())
1834  {
1835  std::string modestr = val.string_value ();
1836 
1837  if (modestr == "zoom in")
1838  {
1839  val = modestr = "zoom";
1840  direction = "in";
1841  }
1842  else if (modestr == "zoom out")
1843  {
1844  val = modestr = "zoom";
1845  direction = "out";
1846  }
1847 
1848  if (__mouse_mode__.set (val, true))
1849  {
1850  std::string mode = __mouse_mode__.current_value ();
1851 
1852  octave_scalar_map pm = get___pan_mode__ ().scalar_map_value ();
1853  pm.setfield ("Enable", mode == "pan" ? "on" : "off");
1854  set___pan_mode__ (pm);
1855 
1856  octave_scalar_map rm = get___rotate_mode__ ().scalar_map_value ();
1857  rm.setfield ("Enable", mode == "rotate" ? "on" : "off");
1858  set___rotate_mode__ (rm);
1859 
1860  octave_scalar_map zm = get___zoom_mode__ ().scalar_map_value ();
1861  zm.setfield ("Enable", mode == "zoom" ? "on" : "off");
1862  zm.setfield ("Direction", direction);
1863  set___zoom_mode__ (zm);
1864 
1865  mark_modified ();
1866  }
1867  else if (modestr == "zoom")
1868  {
1869  octave_scalar_map zm = get___zoom_mode__ ().scalar_map_value ();
1870  std::string curr_direction
1871  = zm.getfield ("Direction").string_value ();
1872 
1873  if (direction != curr_direction)
1874  {
1875  zm.setfield ("Direction", direction);
1876  set___zoom_mode__ (zm);
1877 
1878  mark_modified ();
1879  }
1880  }
1881  }
1882  }
1883 }
1884 
1885 // ---------------------------------------------------------------------
1886 
1887 void
1889 {
1890  size_t offset = 0;
1891 
1892  size_t len = name.length ();
1893 
1894  if (len > 4)
1895  {
1896  caseless_str pfx = name.substr (0, 4);
1897 
1898  if (pfx.compare ("axes") || pfx.compare ("line")
1899  || pfx.compare ("text"))
1900  offset = 4;
1901  else if (len > 5)
1902  {
1903  pfx = name.substr (0, 5);
1904 
1905  if (pfx.compare ("image") || pfx.compare ("patch"))
1906  offset = 5;
1907  else if (len > 6)
1908  {
1909  pfx = name.substr (0, 6);
1910 
1911  if (pfx.compare ("figure") || pfx.compare ("uimenu"))
1912  offset = 6;
1913  else if (len > 7)
1914  {
1915  pfx = name.substr (0, 7);
1916 
1917  if (pfx.compare ("surface") || pfx.compare ("hggroup")
1918  || pfx.compare ("uipanel"))
1919  offset = 7;
1920  else if (len > 9)
1921  {
1922  pfx = name.substr (0, 9);
1923 
1924  if (pfx.compare ("uicontrol")
1925  || pfx.compare ("uitoolbar"))
1926  offset = 9;
1927  else if (len > 10)
1928  {
1929  pfx = name.substr (0, 10);
1930 
1931  if (pfx.compare ("uipushtool"))
1932  offset = 10;
1933  else if (len > 12)
1934  {
1935  pfx = name.substr (0, 12);
1936 
1937  if (pfx.compare ("uitoogletool"))
1938  offset = 12;
1939  else if (len > 13)
1940  {
1941  pfx = name.substr (0, 13);
1942 
1943  if (pfx.compare ("uicontextmenu"))
1944  offset = 13;
1945  }
1946  }
1947  }
1948  }
1949  }
1950  }
1951  }
1952 
1953  if (offset > 0)
1954  {
1955  // FIXME: should we validate property names and values here?
1956 
1957  std::string pname = name.substr (offset);
1958 
1959  std::transform (pfx.begin (), pfx.end (), pfx.begin (), tolower);
1960  std::transform (pname.begin (), pname.end (), pname.begin (),
1961  tolower);
1962 
1963  bool has_property = false;
1964  if (pfx == "axes")
1965  has_property = axes::properties::has_core_property (pname);
1966  else if (pfx == "line")
1967  has_property = line::properties::has_core_property (pname);
1968  else if (pfx == "text")
1969  has_property = text::properties::has_core_property (pname);
1970  else if (pfx == "image")
1971  has_property = image::properties::has_core_property (pname);
1972  else if (pfx == "patch")
1973  has_property = patch::properties::has_core_property (pname);
1974  else if (pfx == "figure")
1975  has_property = figure::properties::has_core_property (pname);
1976  else if (pfx == "surface")
1977  has_property = surface::properties::has_core_property (pname);
1978  else if (pfx == "hggroup")
1979  has_property = hggroup::properties::has_core_property (pname);
1980  else if (pfx == "uimenu")
1981  has_property = uimenu::properties::has_core_property (pname);
1982  else if (pfx == "uicontrol")
1983  has_property = uicontrol::properties::has_core_property (pname);
1984  else if (pfx == "uipanel")
1985  has_property = uipanel::properties::has_core_property (pname);
1986  else if (pfx == "uicontextmenu")
1987  has_property = uicontextmenu::properties::has_core_property (pname);
1988  else if (pfx == "uitoolbar")
1989  has_property = uitoolbar::properties::has_core_property (pname);
1990  else if (pfx == "uipushtool")
1991  has_property = uipushtool::properties::has_core_property (pname);
1992 
1993  if (has_property)
1994  {
1995  bool remove = false;
1996  if (val.is_string ())
1997  {
1998  std::string tval = val.string_value ();
1999 
2000  remove = (tval.compare ("remove") == 0);
2001  }
2002 
2003  pval_map_type& pval_map = plist_map[pfx];
2004 
2005  if (remove)
2006  {
2007  pval_map_iterator p = pval_map.find (pname);
2008 
2009  if (p != pval_map.end ())
2010  pval_map.erase (p);
2011  }
2012  else
2013  pval_map[pname] = val;
2014  }
2015  else
2016  error ("invalid %s property '%s'", pfx.c_str (), pname.c_str ());
2017  }
2018  }
2019 
2020  if (! error_state && offset == 0)
2021  error ("invalid default property specification");
2022 }
2023 
2026 {
2027  octave_value retval;
2028 
2029  size_t offset = 0;
2030 
2031  size_t len = name.length ();
2032 
2033  if (len > 4)
2034  {
2035  caseless_str pfx = name.substr (0, 4);
2036 
2037  if (pfx.compare ("axes") || pfx.compare ("line")
2038  || pfx.compare ("text"))
2039  offset = 4;
2040  else if (len > 5)
2041  {
2042  pfx = name.substr (0, 5);
2043 
2044  if (pfx.compare ("image") || pfx.compare ("patch"))
2045  offset = 5;
2046  else if (len > 6)
2047  {
2048  pfx = name.substr (0, 6);
2049 
2050  if (pfx.compare ("figure") || pfx.compare ("uimenu"))
2051  offset = 6;
2052  else if (len > 7)
2053  {
2054  pfx = name.substr (0, 7);
2055 
2056  if (pfx.compare ("surface") || pfx.compare ("hggroup")
2057  || pfx.compare ("uipanel"))
2058  offset = 7;
2059  else if (len > 9)
2060  {
2061  pfx = name.substr (0, 9);
2062 
2063  if (pfx.compare ("uicontrol")
2064  || pfx.compare ("uitoolbar"))
2065  offset = 9;
2066  else if (len > 10)
2067  {
2068  pfx = name.substr (0, 10);
2069 
2070  if (pfx.compare ("uipushtool"))
2071  offset = 10;
2072  else if (len > 12)
2073  {
2074  pfx = name.substr (0, 12);
2075 
2076  if (pfx.compare ("uitoggletool"))
2077  offset = 12;
2078  else if (len > 13)
2079  {
2080  pfx = name.substr (0, 13);
2081 
2082  if (pfx.compare ("uicontextmenu"))
2083  offset = 13;
2084  }
2085  }
2086  }
2087  }
2088  }
2089  }
2090  }
2091 
2092  if (offset > 0)
2093  {
2094  std::string pname = name.substr (offset);
2095 
2096  std::transform (pfx.begin (), pfx.end (), pfx.begin (), tolower);
2097  std::transform (pname.begin (), pname.end (), pname.begin (),
2098  tolower);
2099 
2100  plist_map_const_iterator p = find (pfx);
2101 
2102  if (p != end ())
2103  {
2104  const pval_map_type& pval_map = p->second;
2105 
2106  pval_map_const_iterator q = pval_map.find (pname);
2107 
2108  if (q != pval_map.end ())
2109  retval = q->second;
2110  }
2111  }
2112  }
2113 
2114  return retval;
2115 }
2116 
2118 property_list::as_struct (const std::string& prefix_arg) const
2119 {
2121 
2122  for (plist_map_const_iterator p = begin (); p != end (); p++)
2123  {
2124  std::string prefix = prefix_arg + p->first;
2125 
2126  const pval_map_type pval_map = p->second;
2127 
2128  for (pval_map_const_iterator q = pval_map.begin ();
2129  q != pval_map.end ();
2130  q++)
2131  m.assign (prefix + q->first, q->second);
2132  }
2133 
2134  return m;
2135 }
2136 
2137 // Set properties given as a cs-list of name, value pairs.
2138 
2139 void
2141 {
2142  int nargin = args.length ();
2143 
2144  if (nargin == 0)
2145  error ("graphics_object::set: Nothing to set");
2146  else if (nargin % 2 == 0)
2147  {
2148  for (int i = 0; i < nargin; i += 2)
2149  {
2150  caseless_str name = args(i).string_value ();
2151 
2152  if (! error_state)
2153  {
2154  octave_value val = args(i+1);
2155 
2156  set_value_or_default (name, val);
2157 
2158  if (error_state)
2159  break;
2160  }
2161  else
2162  error ("set: expecting argument %d to be a property name", i);
2163  }
2164  }
2165  else
2166  error ("set: invalid number of arguments");
2167 }
2168 
2169 /*
2170 ## test set with name, value pairs
2171 %!test
2172 %! hf = figure ("visible", "off");
2173 %! h = plot (1:10, 10:-1:1);
2174 %! set (h, "linewidth", 10, "marker", "x");
2175 %! lw = get (h, "linewidth");
2176 %! mk = get (h, "marker");
2177 %! close (hf);
2178 %! assert (lw, 10);
2179 %! assert (mk, "x");
2180 */
2181 
2182 // Set properties given in two cell arrays containing names and values.
2183 void
2185  const Cell& values, octave_idx_type row)
2186 {
2187  if (names.numel () != values.columns ())
2188  {
2189  error ("set: number of names must match number of value columns (%d != %d)",
2190  names.numel (), values.columns ());
2191  }
2192 
2193  octave_idx_type k = names.columns ();
2194 
2195  for (octave_idx_type column = 0; column < k; column++)
2196  {
2197  caseless_str name = names(column);
2198  octave_value val = values(row, column);
2199 
2200  set_value_or_default (name, val);
2201 
2202  if (error_state)
2203  break;
2204  }
2205 }
2206 
2207 /*
2208 ## test set with cell array arguments
2209 %!test
2210 %! hf = figure ("visible", "off");
2211 %! h = plot (1:10, 10:-1:1);
2212 %! set (h, {"linewidth", "marker"}, {10, "x"});
2213 %! lw = get (h, "linewidth");
2214 %! mk = get (h, "marker");
2215 %! close (hf);
2216 %! assert (lw, 10);
2217 %! assert (mk, "x");
2218 
2219 ## test set with multiple handles and cell array arguments
2220 %!test
2221 %! hf = figure ("visible", "off");
2222 %! unwind_protect
2223 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2224 %! set (h, {"linewidth", "marker"}, {10, "x"; 5, "o"});
2225 %! assert (get (h, "linewidth"), {10; 5});
2226 %! assert (get (h, "marker"), {"x"; "o"});
2227 %! set (h, {"linewidth", "marker"}, {10, "x"});
2228 %! assert (get (h, "linewidth"), {10; 10});
2229 %! assert (get (h, "marker"), {"x"; "x"});
2230 %! unwind_protect_cleanup
2231 %! close (hf);
2232 %! end_unwind_protect;
2233 
2234 %!error <set: number of graphics handles must match number of value rows>
2235 %! hf = figure ("visible", "off");
2236 %! unwind_protect
2237 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2238 %! set (h, {"linewidth", "marker"}, {10, "x"; 5, "o"; 7, "."});
2239 %! unwind_protect_cleanup
2240 %! close (hf);
2241 %! end_unwind_protect
2242 
2243 %!error <set: number of names must match number of value columns>
2244 %! hf = figure ("visible", "off");
2245 %! unwind_protect
2246 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2247 %! set (h, {"linewidth"}, {10, "x"; 5, "o"});
2248 %! unwind_protect_cleanup
2249 %! close (hf);
2250 %! end_unwind_protect
2251 */
2252 
2253 // Set properties given in a struct array
2254 void
2256 {
2257  for (octave_idx_type p = 0; p < m.nfields (); p++)
2258  {
2259  caseless_str name = m.keys ()[p];
2260 
2261  octave_value val = octave_value (m.contents (name).elem (m.numel () - 1));
2262 
2263  set_value_or_default (name, val);
2264 
2265  if (error_state)
2266  break;
2267  }
2268 }
2269 
2270 /*
2271 ## test set ticklabels for compatibility
2272 %!test
2273 %! hf = figure ("visible", "off");
2274 %! set (gca (), "xticklabel", [0, 0.2, 0.4, 0.6, 0.8, 1]);
2275 %! xticklabel = get (gca (), "xticklabel");
2276 %! close (hf);
2277 %! assert (class (xticklabel), "char");
2278 %! assert (size (xticklabel), [6, 3]);
2279 
2280 %!test
2281 %! hf = figure ("visible", "off");
2282 %! set (gca (), "xticklabel", "0|0.2|0.4|0.6|0.8|1");
2283 %! xticklabel = get (gca (), "xticklabel");
2284 %! close (hf);
2285 %! assert (class (xticklabel), "char");
2286 %! assert (size (xticklabel), [6, 3]);
2287 
2288 %!test
2289 %! hf = figure ("visible", "off");
2290 %! set (gca (), "xticklabel", ["0 "; "0.2"; "0.4"; "0.6"; "0.8"; "1 "]);
2291 %! xticklabel = get (gca (), "xticklabel");
2292 %! close (hf);
2293 %! assert (class (xticklabel), "char");
2294 %! assert (size (xticklabel), [6, 3]);
2295 
2296 %!test
2297 %! hf = figure ("visible", "off");
2298 %! set (gca (), "xticklabel", {"0", "0.2", "0.4", "0.6", "0.8", "1"});
2299 %! xticklabel = get (gca (), "xticklabel");
2300 %! close (hf);
2301 %! assert (class (xticklabel), "cell");
2302 %! assert (size (xticklabel), [6, 1]);
2303 */
2304 
2305 /*
2306 ## test set with struct arguments
2307 %!test
2308 %! hf = figure ("visible", "off");
2309 %! unwind_protect
2310 %! h = plot (1:10, 10:-1:1);
2311 %! set (h, struct ("linewidth", 10, "marker", "x"));
2312 %! assert (get (h, "linewidth"), 10);
2313 %! assert (get (h, "marker"), "x");
2314 %! h = plot (1:10, 10:-1:1, 1:10, 1:10);
2315 %! set (h, struct ("linewidth", {5, 10}));
2316 %! assert (get (h, "linewidth"), {10; 10});
2317 %! unwind_protect_cleanup
2318 %! close (hf);
2319 %! end_unwind_protect
2320 
2321 ## test ordering
2322 %!test
2323 %! markchanged = @(h, foobar, name) set (h, "userdata", [get(h,"userdata"); {name}]);
2324 %! hf = figure ("visible", "off");
2325 %! unwind_protect
2326 %! h = line ();
2327 %! set (h, "userdata", {});
2328 %! addlistener (h, "color", {markchanged, "color"});
2329 %! addlistener (h, "linewidth", {markchanged, "linewidth"});
2330 %! ## "linewidth" first
2331 %! props.linewidth = 2;
2332 %! props.color = "r";
2333 %! set (h, props);
2334 %! assert (get (h, "userdata"), fieldnames (props));
2335 %! clear props;
2336 %! clf ();
2337 %! h = line ();
2338 %! set (h, "userdata", {});
2339 %! addlistener (h, "color", {markchanged, "color"});
2340 %! addlistener (h, "linewidth", {markchanged, "linewidth"});
2341 %! ## "color" first
2342 %! props.color = "r";
2343 %! props.linewidth = 2;
2344 %! set (h, props);
2345 %! assert (get (h, "userdata"), fieldnames (props));
2346 %! unwind_protect_cleanup
2347 %! close (hf);
2348 %! end_unwind_protect
2349 */
2350 
2351 // Set a property to a value or to its (factory) default value.
2352 
2353 void
2355  const octave_value& val)
2356 {
2357  if (val.is_string ())
2358  {
2359  std::string tval = val.string_value ();
2360 
2361  octave_value default_val;
2362 
2363  if (tval.compare ("default") == 0)
2364  {
2365  default_val = get_default (name);
2366 
2367  if (error_state)
2368  return;
2369 
2370  rep->set (name, default_val);
2371  }
2372  else if (tval.compare ("factory") == 0)
2373  {
2374  default_val = get_factory_default (name);
2375 
2376  if (error_state)
2377  return;
2378 
2379  rep->set (name, default_val);
2380  }
2381  else
2382  {
2383  // Matlab specifically uses "\default" to escape string setting
2384  if (tval.compare ("\\default") == 0)
2385  rep->set (name, "default");
2386  else if (tval.compare ("\\factory") == 0)
2387  rep->set (name, "factory");
2388  else
2389  rep->set (name, val);
2390  }
2391  }
2392  else
2393  rep->set (name, val);
2394 }
2395 
2396 /*
2397 ## test setting of default values
2398 %!test
2399 %! old_lw = get (0, "defaultlinelinewidth");
2400 %! unwind_protect
2401 %! hf = figure ("visible", "off");
2402 %! h = plot (1:10, 10:-1:1);
2403 %! set (0, "defaultlinelinewidth", 20);
2404 %! set (h, "linewidth", "default");
2405 %! assert (get (h, "linewidth"), 20);
2406 %! set (h, "linewidth", "factory");
2407 %! assert (get (h, "linewidth"), 0.5);
2408 %! unwind_protect_cleanup
2409 %! close (hf);
2410 %! set (0, "defaultlinelinewidth", old_lw);
2411 %! end_unwind_protect
2412 */
2413 
2414 static double
2416 {
2417  static double maxrand = RAND_MAX + 2.0;
2418 
2419  return (rand () + 1.0) / maxrand;
2420 }
2421 
2423 gh_manager::do_get_handle (bool integer_figure_handle)
2424 {
2425  graphics_handle retval;
2426 
2427  if (integer_figure_handle)
2428  {
2429  // Figure handles are positive integers corresponding to the
2430  // figure number.
2431 
2432  // We always want the lowest unused figure number.
2433 
2434  retval = 1;
2435 
2436  while (handle_map.find (retval) != handle_map.end ())
2437  retval++;
2438  }
2439  else
2440  {
2441  // Other graphics handles are negative integers plus some random
2442  // fractional part. To avoid running out of integers, we
2443  // recycle the integer part but tack on a new random part each
2444  // time.
2445 
2446  free_list_iterator p = handle_free_list.begin ();
2447 
2448  if (p != handle_free_list.end ())
2449  {
2450  retval = *p;
2451  handle_free_list.erase (p);
2452  }
2453  else
2454  {
2455  retval = graphics_handle (next_handle);
2456 
2457  next_handle = std::ceil (next_handle) - 1.0 - make_handle_fraction ();
2458  }
2459  }
2460 
2461  return retval;
2462 }
2463 
2464 void
2466 {
2467  if (h.ok ())
2468  {
2469  if (h.value () != 0)
2470  {
2471  iterator p = handle_map.find (h);
2472 
2473  if (p != handle_map.end ())
2474  {
2475  base_properties& bp = p->second.get_properties ();
2476 
2477  bp.set_beingdeleted (true);
2478 
2479  bp.delete_children ();
2480 
2481  octave_value val = bp.get_deletefcn ();
2482 
2483  bp.execute_deletefcn ();
2484 
2485  // Notify graphics toolkit.
2486  p->second.finalize ();
2487 
2488  // Note: this will be valid only for first explicitly
2489  // deleted object. All its children will then have an
2490  // unknown graphics toolkit.
2491 
2492  // Graphics handles for non-figure objects are negative
2493  // integers plus some random fractional part. To avoid
2494  // running out of integers, we recycle the integer part
2495  // but tack on a new random part each time.
2496 
2497  handle_map.erase (p);
2498 
2499  if (h.value () < 0)
2500  handle_free_list.insert
2501  (std::ceil (h.value ()) - make_handle_fraction ());
2502  }
2503  else
2504  error ("graphics_handle::free: invalid object %g", h.value ());
2505  }
2506  else
2507  error ("graphics_handle::free: can't delete root figure");
2508  }
2509 }
2510 
2511 void
2513  const graphics_handle& new_gh)
2514 {
2515  iterator p = handle_map.find (old_gh);
2516 
2517  if (p != handle_map.end ())
2518  {
2519  graphics_object go = p->second;
2520 
2521  handle_map.erase (p);
2522 
2523  handle_map[new_gh] = go;
2524 
2525  if (old_gh.value () < 0)
2526  handle_free_list.insert (std::ceil (old_gh.value ())
2527  - make_handle_fraction ());
2528  }
2529  else
2530  error ("graphics_handle::free: invalid object %g", old_gh.value ());
2531 
2532  for (figure_list_iterator q = figure_list.begin ();
2533  q != figure_list.end (); q++)
2534  {
2535  if (*q == old_gh)
2536  {
2537  *q = new_gh;
2538  break;
2539  }
2540  }
2541 }
2542 
2544 
2545 static void
2546 xset (const graphics_handle& h, const caseless_str& name,
2547  const octave_value& val)
2548 {
2550  obj.set (name, val);
2551 }
2552 
2553 static void
2554 xset (const graphics_handle& h, const octave_value_list& args)
2555 {
2556  if (args.length () > 0)
2557  {
2559  obj.set (args);
2560  }
2561 }
2562 
2563 static octave_value
2564 xget (const graphics_handle& h, const caseless_str& name)
2565 {
2567  return obj.get (name);
2568 }
2569 
2570 static graphics_handle
2571 reparent (const octave_value& ov, const std::string& who,
2572  const std::string& property, const graphics_handle& new_parent,
2573  bool adopt = true)
2574 {
2576 
2577  double val = ov.double_value ();
2578 
2579  if (! error_state)
2580  {
2581  h = gh_manager::lookup (val);
2582 
2583  if (h.ok ())
2584  {
2586 
2587  graphics_handle parent_h = obj.get_parent ();
2588 
2589  graphics_object parent_obj = gh_manager::get_object (parent_h);
2590 
2591  parent_obj.remove_child (h);
2592 
2593  if (adopt)
2594  obj.set ("parent", new_parent.value ());
2595  else
2596  obj.reparent (new_parent);
2597  }
2598  else
2599  error ("%s: invalid graphics handle (= %g) for %s",
2600  who.c_str (), val, property.c_str ());
2601  }
2602  else
2603  error ("%s: expecting %s to be a graphics handle",
2604  who.c_str (), property.c_str ());
2605 
2606  return h;
2607 }
2608 
2609 // This function is NOT equivalent to the scripting language function gcf.
2611 gcf (void)
2612 {
2613  octave_value val = xget (0, "currentfigure");
2614 
2615  return val.is_empty () ? octave_NaN : val.double_value ();
2616 }
2617 
2618 // This function is NOT equivalent to the scripting language function gca.
2620 gca (void)
2621 {
2622  octave_value val = xget (gcf (), "currentaxes");
2623 
2624  return val.is_empty () ? octave_NaN : val.double_value ();
2625 }
2626 
2627 static void
2629 {
2630  if (h.ok ())
2631  {
2633 
2634  // Don't do recursive deleting, due to callbacks
2635  if (! obj.get_properties ().is_beingdeleted ())
2636  {
2637  graphics_handle parent_h = obj.get_parent ();
2638 
2639  graphics_object parent_obj =
2640  gh_manager::get_object (parent_h);
2641 
2642  // NOTE: free the handle before removing it from its
2643  // parent's children, such that the object's
2644  // state is correct when the deletefcn callback
2645  // is executed
2646 
2647  gh_manager::free (h);
2648 
2649  // A callback function might have already deleted
2650  // the parent
2651  if (parent_obj.valid_object ())
2652  parent_obj.remove_child (h);
2653 
2654  Vdrawnow_requested = true;
2655  }
2656  }
2657 }
2658 
2659 static void
2661 {
2663 }
2664 
2665 static void
2667 {
2668  for (octave_idx_type i = 0; i < vals.numel (); i++)
2669  delete_graphics_object (vals.elem (i));
2670 }
2671 
2672 static void
2674 {
2675  octave_value closerequestfcn = xget (handle, "closerequestfcn");
2676 
2677  OCTAVE_SAFE_CALL (gh_manager::execute_callback, (handle, closerequestfcn));
2678 }
2679 
2680 static void
2682 {
2683  // Remove the deletefcn and closerequestfcn callbacks and delete the
2684  // object directly.
2685 
2686  xset (handle, "deletefcn", Matrix ());
2687  xset (handle, "closerequestfcn", Matrix ());
2688 
2689  delete_graphics_object (handle);
2690 }
2691 
2692 void
2694 {
2695  // FIXME: should we process or discard pending events?
2696 
2697  event_queue.clear ();
2698 
2699  // Don't use figure_list_iterator because we'll be removing elements
2700  // from the list elsewhere.
2701 
2702  Matrix hlist = do_figure_handle_list (true);
2703 
2704  for (octave_idx_type i = 0; i < hlist.numel (); i++)
2705  {
2706  graphics_handle h = gh_manager::lookup (hlist(i));
2707 
2708  if (h.ok ())
2709  close_figure (h);
2710  }
2711 
2712  // They should all be closed now. If not, force them to close.
2713 
2714  hlist = do_figure_handle_list (true);
2715 
2716  for (octave_idx_type i = 0; i < hlist.numel (); i++)
2717  {
2718  graphics_handle h = gh_manager::lookup (hlist(i));
2719 
2720  if (h.ok ())
2721  force_close_figure (h);
2722  }
2723 
2724  // None left now, right?
2725 
2726  hlist = do_figure_handle_list (true);
2727 
2728  assert (hlist.numel () == 0);
2729 
2730  // Clear all callback objects from our list.
2731 
2732  callback_objects.clear ();
2733 }
2734 
2735 static void
2737 {
2738  graphics_object parent_obj = gh_manager::get_object (p);
2739  parent_obj.adopt (h);
2740 }
2741 
2742 static bool
2744 {
2745  return h.ok ();
2746 }
2747 
2748 static bool
2749 is_handle (double val)
2750 {
2752 
2753  return h.ok ();
2754 }
2755 
2756 static octave_value
2758 {
2759  octave_value retval = false;
2760 
2761  if (val.is_real_scalar () && is_handle (val.double_value ()))
2762  retval = true;
2763  else if (val.is_numeric_type () && val.is_real_type ())
2764  {
2765  const NDArray handles = val.array_value ();
2766 
2767  if (! error_state)
2768  {
2769  boolNDArray result (handles.dims ());
2770 
2771  for (octave_idx_type i = 0; i < handles.numel (); i++)
2772  result.xelem (i) = is_handle (handles (i));
2773 
2774  retval = result;
2775  }
2776  }
2777 
2778  return retval;
2779 }
2780 
2781 static bool
2782 is_figure (double val)
2783 {
2785 
2786  return obj && obj.isa ("figure");
2787 }
2788 
2789 static void
2791 {
2794 }
2795 
2796 static void
2798 {
2800 
2801  if (go)
2802  go.initialize ();
2803 }
2804 
2805 // ---------------------------------------------------------------------
2806 
2807 void
2809 {
2811 
2812  update (go, id);
2813 }
2814 
2815 bool
2817 {
2819 
2820  return initialize (go);
2821 }
2822 
2823 void
2825 {
2827 
2828  finalize (go);
2829 }
2830 
2831 static void
2833  property_list::pval_map_type factory_pval)
2834 {
2836 
2837  // Replace factory defaults by user defined ones
2838  std::string go_name = obj.get_properties ().graphics_object_name ();
2840  obj.build_user_defaults_map (pval, go_name);
2841 
2842  for (property_list::pval_map_const_iterator p = pval.begin ();
2843  p != pval.end (); p++)
2844  {
2845  factory_pval[p->first] = p->second;
2846  }
2847 
2848 
2849  // Reset defaults
2850  for (property_list::pval_map_const_iterator it = factory_pval.begin ();
2851  it != factory_pval.end (); it++)
2852  {
2853  std::string pname = it->first;
2854 
2855  // Don't reset internal properties and handle_properties
2856  if (! obj.has_readonly_property (pname)
2857  && pname.find ("__") != 0 && pname.find ("current") != 0
2858  && pname != "uicontextmenu" && pname != "parent")
2859  {
2860  // Store *mode prop/val in order to set them last
2861  if (pname.find ("mode") == (pname.length () - 4))
2862  pval[pname] = it->second;
2863  else
2864  obj.set (pname, it->second);
2865  }
2866  }
2867 
2868  // set *mode properties
2869  for (property_list::pval_map_const_iterator it = pval.begin ();
2870  it != pval.end (); it++)
2871  obj.set (it->first, it->second);
2872 }
2873 
2874 // ---------------------------------------------------------------------
2875 
2876 void
2879 {
2880  std::string go_name = graphics_object_name ();
2881 
2882  property_list::plist_map_const_iterator p = defaults.find (go_name);
2883 
2884  if (p != defaults.end ())
2885  {
2886  const property_list::pval_map_type pval_map = p->second;
2887 
2888  for (property_list::pval_map_const_iterator q = pval_map.begin ();
2889  q != pval_map.end ();
2890  q++)
2891  {
2892  std::string pname = q->first;
2893 
2894  obj.set (pname, q->second);
2895 
2896  if (error_state)
2897  {
2898  error ("error setting default property %s", pname.c_str ());
2899  break;
2900  }
2901  }
2902  }
2903 }
2904 
2905 /*
2906 ## test defaults are set in the order they were stored
2907 %!test
2908 %! set(0, "defaultfigureunits", "normalized");
2909 %! set(0, "defaultfigureposition", [0.7 0 0.3 0.3]);
2910 %! hf = figure ("visible", "off");
2911 %! tol = 20 * eps;
2912 %! unwind_protect
2913 %! assert (get (hf, "position"), [0.7 0 0.3 0.3], tol);
2914 %! unwind_protect_cleanup
2915 %! close (hf);
2916 %! set(0, "defaultfigureunits", "remove");
2917 %! set(0, "defaultfigureposition", "remove");
2918 %! end_unwind_protect
2919 */
2920 
2923 {
2924  octave_value retval;
2925 
2926  std::map<caseless_str, property, cmp_caseless_str>::const_iterator it =
2927  all_props.find (name);
2928 
2929  if (it != all_props.end ())
2930  retval = it->second.get ();
2931  else
2932  error ("get: unknown property \"%s\"", name.c_str ());
2933 
2934  return retval;
2935 }
2936 
2939 {
2941 
2942  for (std::map<caseless_str, property, cmp_caseless_str>::const_iterator
2943  it = all_props.begin (); it != all_props.end (); ++it)
2944  if (all || ! it->second.is_hidden ())
2945  m.assign (it->second.get_name (), it->second.get ());
2946 
2947  return m;
2948 }
2949 
2950 std::set<std::string>
2952 {
2953  return dynamic_properties;
2954 }
2955 
2956 bool
2957 base_properties::has_dynamic_property (const std::string& pname)
2958 {
2959  const std::set<std::string>& dynprops = dynamic_property_names ();
2960 
2961  if (dynprops.find (pname) != dynprops.end ())
2962  return true;
2963  else
2964  return all_props.find (pname) != all_props.end ();
2965 }
2966 
2967 void
2969  const octave_value& val)
2970 {
2971  std::map<caseless_str, property, cmp_caseless_str>::iterator it =
2972  all_props.find (pname);
2973 
2974  if (it != all_props.end ())
2975  it->second.set (val);
2976  else
2977  error ("set: unknown property \"%s\"", pname.c_str ());
2978 
2979  if (! error_state)
2980  {
2981  dynamic_properties.insert (pname);
2982 
2983  mark_modified ();
2984  }
2985 }
2986 
2987 property
2989 {
2990  std::map<caseless_str, property, cmp_caseless_str>::const_iterator it =
2991  all_props.find (name);
2992 
2993  if (it == all_props.end ())
2994  {
2995  error ("get_property: unknown property \"%s\"", name.c_str ());
2996  return property ();
2997  }
2998  else
2999  return it->second;
3000 }
3001 
3002 void
3004 {
3005  double hnp = val.double_value ();
3006 
3007  graphics_handle new_parent = octave_NaN;
3008 
3009  if (! error_state)
3010  {
3011  if (hnp == __myhandle__)
3012  error ("set: can not set object parent to be object itself");
3013  else
3014  {
3015  new_parent = gh_manager::lookup (hnp);
3016 
3017  if (new_parent.ok ())
3018  {
3019  // Remove child from current parent
3020  graphics_object old_parent_obj;
3021  old_parent_obj = gh_manager::get_object (get_parent ());
3022 
3023 
3024  if (old_parent_obj.get_handle () != hnp)
3025  old_parent_obj.remove_child (__myhandle__);
3026  else
3027  // Do nothing more
3028  return;
3029 
3030  // Check new parent's parent is not this child to avoid recursion
3031  graphics_object new_parent_obj;
3032  new_parent_obj = gh_manager::get_object (new_parent);
3033  if (new_parent_obj.get_parent () == __myhandle__)
3034  {
3035  // new parent's parent gets child's original parent
3036  new_parent_obj.get_properties ().set_parent (get_parent ().as_octave_value ());
3037  }
3038 
3039  // Set parent property to new_parent and do adoption
3040  parent = new_parent.as_octave_value ();
3041  ::adopt (parent.handle_value (), __myhandle__);
3042  }
3043  else
3044  error ("set: invalid graphics handle (= %g) for parent", hnp);
3045  }
3046  }
3047  else
3048  error ("set: expecting parent to be a graphics handle");
3049 }
3050 
3051 /*
3052 %!test
3053 %! hf = figure ("visible", "off");
3054 %! unwind_protect
3055 %! hax = gca ();
3056 %! set (hax, "parent", gcf ())
3057 %! assert (gca (), hax)
3058 %! unwind_protect_cleanup
3059 %! close (hf);
3060 %! end_unwind_protect
3061 */
3062 
3063 void
3065 {
3066  __modified__ = "on";
3068  if (parent_obj)
3069  parent_obj.mark_modified ();
3070 }
3071 
3072 void
3074 {
3076 
3077  if (parent_obj)
3078  parent_obj.override_defaults (obj);
3079 }
3080 
3081 void
3082 base_properties::update_axis_limits (const std::string& axis_type) const
3083 {
3084  graphics_object obj = gh_manager::get_object (__myhandle__);
3085 
3086  if (obj)
3087  obj.update_axis_limits (axis_type);
3088 }
3089 
3090 void
3091 base_properties::update_axis_limits (const std::string& axis_type,
3092  const graphics_handle& h) const
3093 {
3094  graphics_object obj = gh_manager::get_object (__myhandle__);
3095 
3096  if (obj)
3097  obj.update_axis_limits (axis_type, h);
3098 }
3099 
3100 void
3102 {
3103  if (uicontextmenu.get ().is_empty ())
3104  return;
3105 
3107  if (obj && obj.isa ("uicontextmenu"))
3108  {
3109  uicontextmenu::properties& props =
3110  reinterpret_cast<uicontextmenu::properties&> (obj.get_properties ());
3111  props.add_dependent_obj (__myhandle__);
3112  }
3113 }
3114 
3115 bool
3117 {
3118  return (handlevisibility.is ("on")
3119  || (executing_callback && ! handlevisibility.is ("off")));
3120 }
3121 
3124 {
3126 
3127  if (go)
3128  return go.get_toolkit ();
3129  else
3130  return graphics_toolkit ();
3131 }
3132 
3133 void
3135 {
3136  Matrix kids = get_children ();
3137 
3138  for (int i = 0; i < kids.numel (); i++)
3139  {
3140  graphics_object go = gh_manager::get_object (kids(i));
3141 
3142  if (go.valid_object ())
3144  }
3145 }
3146 
3147 void
3148 base_properties::update_autopos (const std::string& elem_type)
3149 {
3151 
3152  if (parent_obj.valid_object ())
3153  parent_obj.get_properties ().update_autopos (elem_type);
3154 }
3155 
3156 void
3158  listener_mode mode)
3159 {
3160  property p = get_property (nm);
3161 
3162  if (! error_state && p.ok ())
3163  p.add_listener (v, mode);
3164 }
3165 
3166 void
3168  const octave_value& v, listener_mode mode)
3169 {
3170  property p = get_property (nm);
3171 
3172  if (! error_state && p.ok ())
3173  p.delete_listener (v, mode);
3174 }
3175 
3176 // ---------------------------------------------------------------------
3177 
3178 void
3179 base_graphics_object::update_axis_limits (const std::string& axis_type)
3180 {
3181  if (valid_object ())
3182  {
3184 
3185  if (parent_obj)
3186  parent_obj.update_axis_limits (axis_type);
3187  }
3188  else
3189  error ("base_graphics_object::update_axis_limits: invalid graphics object");
3190 }
3191 
3192 void
3193 base_graphics_object::update_axis_limits (const std::string& axis_type,
3194  const graphics_handle& h)
3195 {
3196  if (valid_object ())
3197  {
3199 
3200  if (parent_obj)
3201  parent_obj.update_axis_limits (axis_type, h);
3202  }
3203  else
3204  error ("base_graphics_object::update_axis_limits: invalid graphics object");
3205 }
3206 
3207 void
3209 {
3210  octave_map m = get (true).map_value ();
3211 
3212  for (octave_map::const_iterator pa = m.begin (); pa != m.end (); pa++)
3213  {
3214  // FIXME: there has to be a better way. I think we want to
3215  // ask whether it is OK to delete the listener for the given
3216  // property. How can we know in advance that it will be OK?
3217 
3218  unwind_protect frame;
3219 
3220  frame.protect_var (error_state);
3222  frame.protect_var (Vdebug_on_error);
3224 
3225  discard_error_messages = true;
3226  Vdebug_on_error = false;
3227  Vdebug_on_warning = false;
3228 
3229  property p = get_properties ().get_property (pa->first);
3230 
3231  if (! error_state && p.ok ())
3232  p.delete_listener ();
3233  }
3234 }
3235 
3236 void
3238 {
3239  property_list local_defaults = get_defaults_list ();
3241  local_defaults.find (go_name);
3242 
3243  if (p != local_defaults.end ())
3244  {
3245  property_list::pval_map_type pval = p->second;
3246  for (property_list::pval_map_const_iterator q = pval.begin ();
3247  q != pval.end (); q++)
3248  {
3249  std::string pname = q->first;
3250  if (def.find (pname) == def.end ())
3251  def[pname] = q->second;
3252  }
3253  }
3254 
3256 
3257  if (parent_obj)
3258  parent_obj.build_user_defaults_map (def, go_name);
3259 
3260 }
3261 
3262 void
3264 {
3265  if (valid_object ())
3266  {
3267  property_list::pval_map_type factory_pval =
3269  .find (type ())->second;
3270 
3271  xreset_default_properties (get_handle (), factory_pval);
3272  }
3273 }
3274 
3275 std::string
3277 {
3278  std::string retval;
3279 
3280  if (valid_object ())
3281  {
3282  octave_map m = get ().map_value ();
3284 
3285  for (octave_map::const_iterator pa = m.begin (); pa != m.end (); pa++)
3286  {
3287  if (pa->first != "children"
3288  && ! obj.has_readonly_property (pa->first))
3289  {
3290  property p = get_properties ().get_property (pa->first);
3291 
3292  if (p.ok () && ! p.is_hidden ())
3293  {
3294  retval += "\n\t" + std::string (pa->first) + ": ";
3295  if (p.is_radio ())
3296  retval += p.values_as_string ();
3297  }
3298  }
3299  }
3300 
3301  if (! retval.empty ())
3302  retval += "\n";
3303  }
3304  else
3305  error ("base_graphics_object::values_as_string: invalid graphics object");
3306 
3307  return retval;
3308 }
3309 
3310 std::string
3311 base_graphics_object::value_as_string (const std::string& prop)
3312 {
3313  std::string retval;
3314 
3315  if (valid_object ())
3316  {
3318 
3319  if (prop != "children" && ! obj.has_readonly_property (prop))
3320  {
3321  property p = get_properties ().get_property (prop);
3322 
3323  if (p.ok () && ! p.is_hidden ())
3324  {
3325  if (p.is_radio ())
3326  retval += p.values_as_string ();
3327  }
3328  }
3329 
3330  if (! retval.empty ())
3331  retval += "\n";
3332  }
3333  else
3334  error ("base_graphics_object::value_as_string: invalid graphics object");
3335 
3336  return retval;
3337 }
3338 
3341 {
3342  octave_scalar_map retval;
3343 
3344  if (valid_object ())
3345  {
3346  octave_scalar_map m = get ().scalar_map_value ();
3348 
3350  pa != m.end (); pa++)
3351  {
3352  if (pa->first != "children"
3353  && ! obj.has_readonly_property (pa->first))
3354  {
3355  property p = get_properties ().get_property (pa->first);
3356 
3357  if (p.ok () && ! p.is_hidden ())
3358  {
3359  if (p.is_radio ())
3360  retval.assign (p.get_name (), p.values_as_cell ());
3361  else
3362  retval.assign (p.get_name (), Cell ());
3363  }
3364  }
3365  }
3366  }
3367  else
3368  error ("base_graphics_object::values_as_struct: invalid graphics object");
3369 
3370  return retval;
3371 }
3372 
3373 /*
3374 %!test
3375 %! hfig = figure ("visible", "off");
3376 %! unwind_protect
3377 %! hax = axes ();
3378 %! ret = set (hax, "tightinset");
3379 %! assert (isempty (ret));
3380 %! ret = set (hax, "type");
3381 %! assert (isempty (ret));
3382 %! ret = set (hfig, "tag");
3383 %! assert (isempty (ret));
3384 %! ret = set (0, "commandwindowsize");
3385 %! assert (isempty (ret));
3386 %! ret = set (0);
3387 %! assert (! isfield (ret, "commandwindowsize"));
3388 %! unwind_protect_cleanup
3389 %! close (hfig);
3390 %! end_unwind_protect
3391 */
3392 
3394 graphics_object::get_ancestor (const std::string& obj_type) const
3395 {
3396  if (valid_object ())
3397  {
3398  if (isa (obj_type))
3399  return *this;
3400  else
3401  return gh_manager::get_object (get_parent ()).get_ancestor (obj_type);
3402  }
3403  else
3404  return graphics_object ();
3405 }
3406 
3407 // ---------------------------------------------------------------------
3408 
3409 #include "graphics-props.cc"
3410 
3411 // ---------------------------------------------------------------------
3412 
3413 void
3415 {
3416  graphics_handle val (v);
3417 
3418  if (error_state)
3419  return;
3420 
3421  if (xisnan (val.value ()))
3422  {
3423  if (! cbo_stack.empty ())
3424  {
3425  val = cbo_stack.front ();
3426 
3427  cbo_stack.pop_front ();
3428  }
3429 
3430  callbackobject = val;
3431  }
3432  else if (is_handle (val))
3433  {
3434  if (get_callbackobject ().ok ())
3435  cbo_stack.push_front (get_callbackobject ());
3436 
3437  callbackobject = val;
3438  }
3439  else
3440  gripe_set_invalid ("callbackobject");
3441 }
3442 
3443 void
3445 {
3446  graphics_handle val (v);
3447 
3448  if (error_state)
3449  return;
3450 
3451  if (xisnan (val.value ()) || is_handle (val))
3452  {
3453  currentfigure = val;
3454 
3455  if (val.ok ())
3457  }
3458  else
3459  gripe_set_invalid ("currentfigure");
3460 }
3461 
3462 std::string
3464 {
3465  bool is_diary_on = F__diarystate__ ()(0).bool_value ();
3466  if (is_diary_on)
3467  return std::string ("on");
3468  else
3469  return std::string ("off");
3470 }
3471 
3472 void
3474 {
3475  if (! error_state)
3476  {
3477  // Input checking and abrev. matching
3478  diary.set (val, false);
3479 
3480  if (! error_state)
3481  {
3482  Fdiary (ovl (diary.current_value ()));
3483 
3484  diary.run_listeners ();
3485  }
3486  }
3487 }
3488 
3489 std::string
3491 {
3492  return F__diaryfile__ ()(0).string_value ();
3493 }
3494 
3495 void
3497 {
3498  if (! error_state)
3499  {
3500  // Input checking and abrev. matching
3501  diaryfile.set (val, false);
3502 
3503  if (! error_state)
3504  {
3505  Fdiary (ovl (diaryfile.string_value ()));
3506 
3507  diaryfile.run_listeners ();
3508  }
3509  }
3510 }
3511 
3512 std::string
3514 {
3515  bool is_echo_on = F__echostate__ ()(0).bool_value ();
3516  if (is_echo_on)
3517  return std::string ("on");
3518  else
3519  return std::string ("off");
3520 }
3521 
3522 void
3524 {
3525  if (! error_state)
3526  {
3527  // Input checking and abrev. matching
3528  echo.set (val, false);
3529 
3530  if (! error_state)
3531  {
3532  Fecho (ovl (echo.current_value ()));
3533 
3534  echo.run_listeners ();
3535  }
3536  }
3537 }
3538 
3539 std::string
3541 {
3542  return Flasterr ()(0).string_value ();
3543 }
3544 
3545 std::string
3547 {
3548  return F__formatstring__ ()(0).string_value ();
3549 }
3550 
3551 void
3553 {
3554  if (! error_state)
3555  {
3556  // Input checking and abrev. matching
3557  format.set (val, false);
3558 
3559  if (! error_state)
3560  {
3561  Fformat (ovl (format.current_value ()));
3562 
3563  format.run_listeners ();
3564  }
3565  }
3566 }
3567 
3568 std::string
3570 {
3571  bool iscompact = F__compactformat__ ()(0).bool_value ();
3572  if (iscompact)
3573  return std::string ("compact");
3574  else
3575  return std::string ("loose");
3576 }
3577 
3578 void
3580 {
3581  if (! error_state)
3582  {
3583  // Input checking and abrev. matching
3584  formatspacing.set (val, false);
3585 
3586  if (! error_state)
3587  {
3588  std::string strval = formatspacing.current_value ();
3589 
3590  if (strval == "compact")
3591  F__compactformat__ (ovl (true));
3592  else
3593  F__compactformat__ (ovl (false));
3594 
3595  formatspacing.run_listeners ();
3596  }
3597  }
3598 }
3599 
3600 
3601 double
3603 {
3604  return Fmax_recursion_depth ()(0).double_value ();
3605 }
3606 
3607 void
3609 {
3610  if (! error_state)
3611  {
3612  // Input checking and abrev. matching
3613  recursionlimit.set (val, false);
3614 
3615  if (! error_state)
3616  {
3617  double dval = recursionlimit.double_value ();
3618 
3619  Fmax_recursion_depth (ovl (dval));
3620 
3621  recursionlimit.run_listeners ();
3622  }
3623  }
3624 }
3625 
3626 void
3628 {
3629  if (! error_state)
3630  {
3631  if (integerhandle.set (val, true))
3632  {
3633  bool int_fig_handle = integerhandle.is_on ();
3634 
3635  graphics_object this_go = gh_manager::get_object (__myhandle__);
3636 
3637  graphics_handle old_myhandle = __myhandle__;
3638 
3639  __myhandle__ = gh_manager::get_handle (int_fig_handle);
3640 
3641  gh_manager::renumber_figure (old_myhandle, __myhandle__);
3642 
3644 
3645  base_properties& props = parent_go.get_properties ();
3646 
3647  props.renumber_child (old_myhandle, __myhandle__);
3648 
3649  Matrix kids = get_children ();
3650 
3651  for (octave_idx_type i = 0; i < kids.numel (); i++)
3652  {
3653  graphics_object kid = gh_manager::get_object (kids(i));
3654 
3655  kid.get_properties ().renumber_parent (__myhandle__);
3656  }
3657 
3659 
3660  if (__myhandle__ == cf)
3661  xset (0, "currentfigure", __myhandle__.value ());
3662 
3663  this_go.update (integerhandle.get_id ());
3664 
3665  mark_modified ();
3666  }
3667  }
3668 }
3669 
3670 // FIXME: This should update monitorpositions and pointerlocation, but
3671 // as these properties are yet used, and so it doesn't matter that they
3672 // aren't set yet.
3673 void
3675 {
3676  caseless_str xunits = get_units ();
3677 
3678  Matrix ss = default_screensize ();
3679 
3680  double dpi = get_screenpixelsperinch ();
3681 
3682  if (xunits.compare ("inches"))
3683  {
3684  ss(0) = 0;
3685  ss(1) = 0;
3686  ss(2) /= dpi;
3687  ss(3) /= dpi;
3688  }
3689  else if (xunits.compare ("centimeters"))
3690  {
3691  ss(0) = 0;
3692  ss(1) = 0;
3693  ss(2) *= 2.54 / dpi;
3694  ss(3) *= 2.54 / dpi;
3695  }
3696  else if (xunits.compare ("normalized"))
3697  {
3698  ss = Matrix (1, 4, 1.0);
3699  ss(0) = 0;
3700  ss(1) = 0;
3701  }
3702  else if (xunits.compare ("points"))
3703  {
3704  ss(0) = 0;
3705  ss(1) = 0;
3706  ss(2) *= 72 / dpi;
3707  ss(3) *= 72 / dpi;
3708  }
3709 
3710  set_screensize (ss);
3711 }
3712 
3713 Matrix
3715 {
3716  Matrix screen_size = screen_size_pixels ();
3717  Matrix pos = Matrix (1, 4, 0);
3718  pos(2) = screen_size(0);
3719  pos(3) = screen_size(1);
3720  return pos;
3721 }
3722 
3723 /*
3724 %!test
3725 %! old_units = get (0, "units");
3726 %! unwind_protect
3727 %! set (0, "units", "pixels");
3728 %! sz = get (0, "screensize") - [1, 1, 0, 0];
3729 %! dpi = get (0, "screenpixelsperinch");
3730 %! set (0, "units", "inches");
3731 %! assert (get (0, "screensize"), sz / dpi, 0.5 / dpi);
3732 %! set (0, "units", "centimeters");
3733 %! assert (get (0, "screensize"), sz / dpi * 2.54, 0.5 / dpi * 2.54);
3734 %! set (0, "units", "points");
3735 %! assert (get (0, "screensize"), sz / dpi * 72, 0.5 / dpi * 72);
3736 %! set (0, "units", "normalized");
3737 %! assert (get (0, "screensize"), [0.0, 0.0, 1.0, 1.0]);
3738 %! set (0, "units", "pixels");
3739 %! assert (get (0, "screensize"), sz + [1, 1, 0, 0]);
3740 %! unwind_protect_cleanup
3741 %! set (0, "units", old_units);
3742 %! end_unwind_protect
3743 */
3744 
3745 void
3747 {
3749 
3751 
3752  xset (0, "currentfigure", cf.value ());
3753 
3755 }
3756 
3759 
3760 void
3762 {
3763  // empty list of local defaults
3765 
3768 }
3769 
3770 // ---------------------------------------------------------------------
3771 
3772 void
3774 {
3775  graphics_handle val (v);
3776 
3777  if (error_state)
3778  return;
3779 
3780  if (xisnan (val.value ()) || is_handle (val))
3781  currentaxes = val;
3782  else
3783  gripe_set_invalid ("currentaxes");
3784 }
3785 
3786 void
3788 {
3790 
3791  if (gh == currentaxes.handle_value ())
3792  {
3793  graphics_handle new_currentaxes;
3794 
3795  Matrix kids = get_children ();
3796 
3797  for (octave_idx_type i = 0; i < kids.numel (); i++)
3798  {
3799  graphics_handle kid = kids(i);
3800 
3802 
3803  if (go.isa ("axes"))
3804  {
3805  new_currentaxes = kid;
3806  break;
3807  }
3808  }
3809 
3810  currentaxes = new_currentaxes;
3811  }
3812 }
3813 
3814 void
3816 {
3818 
3819  if (! get_currentaxes ().ok ())
3820  {
3822 
3823  if (go.type () == "axes")
3824  set_currentaxes (h.as_octave_value ());
3825  }
3826 }
3827 
3828 /*
3829 %!test
3830 %! hf1 = figure ("visible", "off");
3831 %! ax1 = subplot (1,2,1);
3832 %! ax2 = subplot (1,2,2);
3833 %! hf2 = figure ("visible", "off");
3834 %! unwind_protect
3835 %! set (ax2, "parent", hf2);
3836 %! assert (get (hf2, "currentaxes"), ax2);
3837 %! assert (get (hf1, "currentaxes"), ax1);
3838 %! set (ax1, "parent", hf2);
3839 %! assert (get (hf2, "currentaxes"), ax2);
3840 %! unwind_protect_cleanup
3841 %! close (hf1);
3842 %! close (hf2);
3843 %! end_unwind_protect
3844 */
3845 
3846 void
3848 {
3849  std::string s = val.string_value ();
3850 
3851  if (! error_state)
3852  {
3853  if (s == "on")
3854  xset (0, "currentfigure", __myhandle__.value ());
3855 
3856  visible = val;
3857  }
3858 }
3859 
3860 Matrix
3861 figure::properties::get_boundingbox (bool internal, const Matrix&) const
3862 {
3863  Matrix screen_size = screen_size_pixels ();
3864  Matrix pos = (internal ?
3865  get_position ().matrix_value () :
3866  get_outerposition ().matrix_value ());
3867 
3868  pos = convert_position (pos, get_units (), "pixels", screen_size);
3869 
3870  pos(0)--;
3871  pos(1)--;
3872  pos(1) = screen_size(1) - pos(1) - pos(3);
3873 
3874  return pos;
3875 }
3876 
3877 void
3879  bool do_notify_toolkit)
3880 {
3881  Matrix screen_size = screen_size_pixels ();
3882  Matrix pos = bb;
3883 
3884  pos(1) = screen_size(1) - pos(1) - pos(3);
3885  pos(1)++;
3886  pos(0)++;
3887  pos = convert_position (pos, "pixels", get_units (), screen_size);
3888 
3889  if (internal)
3890  set_position (pos, do_notify_toolkit);
3891  else
3892  set_outerposition (pos, do_notify_toolkit);
3893 }
3894 
3895 Matrix
3897 {
3898  Matrix bb = get_boundingbox (true);
3899  Matrix pos (1, 2, 0);
3900 
3901  pos(0) = x;
3902  pos(1) = y;
3903 
3904  pos(1) = bb(3) - pos(1);
3905  pos(0)++;
3906  pos = convert_position (pos, "pixels", get_units (),
3907  bb.extract_n (0, 2, 1, 2));
3908 
3909  return pos;
3910 }
3911 
3912 Matrix
3914 {
3915  Matrix bb = get_boundingbox (true);
3916  Matrix pos (1, 2, 0);
3917 
3918  pos(0) = x;
3919  pos(1) = y;
3920 
3921  pos = convert_position (pos, get_units (), "pixels",
3922  bb.extract_n (0, 2, 1, 2));
3923  pos(0)--;
3924  pos(1) = bb(3) - pos(1);
3925 
3926  return pos;
3927 }
3928 
3929 void
3931  bool do_notify_toolkit)
3932 {
3933  if (! error_state)
3934  {
3935  Matrix old_bb, new_bb;
3936  bool modified = false;
3937 
3938  old_bb = get_boundingbox (true);
3939  modified = position.set (v, false, do_notify_toolkit);
3940  new_bb = get_boundingbox (true);
3941 
3942  if (old_bb != new_bb)
3943  {
3944  if (old_bb(2) != new_bb(2) || old_bb(3) != new_bb(3))
3945  {
3946  execute_resizefcn ();
3947  update_boundingbox ();
3948  }
3949  }
3950 
3951  if (modified)
3952  {
3953  position.run_listeners (POSTSET);
3954  mark_modified ();
3955  }
3956 
3957  if (paperpositionmode.is ("auto"))
3958  paperposition.set (get_auto_paperposition ());
3959  }
3960 }
3961 
3962 void
3964  bool do_notify_toolkit)
3965 {
3966  if (! error_state)
3967  {
3968  if (outerposition.set (v, true, do_notify_toolkit))
3969  {
3970  mark_modified ();
3971  }
3972  }
3973 }
3974 
3975 void
3977 {
3978  if (! error_state)
3979  {
3980  caseless_str typ = get_papertype ();
3981  caseless_str punits = v.string_value ();
3982  if (! error_state)
3983  {
3984  if (punits.compare ("normalized") && typ.compare ("<custom>"))
3985  error ("set: can't set the paperunits to normalized when the papertype is custom");
3986  else
3987  {
3988  caseless_str old_paperunits = get_paperunits ();
3989  if (paperunits.set (v, true))
3990  {
3991  update_paperunits (old_paperunits);
3992  mark_modified ();
3993  }
3994  }
3995  }
3996  }
3997 }
3998 
3999 void
4001 {
4002  if (! error_state)
4003  {
4004  caseless_str typ = v.string_value ();
4005  caseless_str punits = get_paperunits ();
4006  if (! error_state)
4007  {
4008  if (punits.compare ("normalized") && typ.compare ("<custom>"))
4009  error ("set: can't set the paperunits to normalized when the papertype is custom");
4010  else
4011  {
4012  if (papertype.set (v, true))
4013  {
4014  update_papertype ();
4015  mark_modified ();
4016  }
4017  }
4018  }
4019  }
4020 }
4021 
4022 static Matrix
4024 {
4025  Matrix ret (1, 2, 1.0);
4026 
4027  if (! punits.compare ("normalized"))
4028  {
4029  double in2units;
4030  double mm2units;
4031 
4032  if (punits.compare ("inches"))
4033  {
4034  in2units = 1.0;
4035  mm2units = 1 / 25.4 ;
4036  }
4037  else if (punits.compare ("centimeters"))
4038  {
4039  in2units = 2.54;
4040  mm2units = 1 / 10.0;
4041  }
4042  else // points
4043  {
4044  in2units = 72.0;
4045  mm2units = 72.0 / 25.4;
4046  }
4047 
4048  if (typ.compare ("usletter"))
4049  {
4050  ret (0) = 8.5 * in2units;
4051  ret (1) = 11.0 * in2units;
4052  }
4053  else if (typ.compare ("uslegal"))
4054  {
4055  ret (0) = 8.5 * in2units;
4056  ret (1) = 14.0 * in2units;
4057  }
4058  else if (typ.compare ("tabloid"))
4059  {
4060  ret (0) = 11.0 * in2units;
4061  ret (1) = 17.0 * in2units;
4062  }
4063  else if (typ.compare ("a0"))
4064  {
4065  ret (0) = 841.0 * mm2units;
4066  ret (1) = 1189.0 * mm2units;
4067  }
4068  else if (typ.compare ("a1"))
4069  {
4070  ret (0) = 594.0 * mm2units;
4071  ret (1) = 841.0 * mm2units;
4072  }
4073  else if (typ.compare ("a2"))
4074  {
4075  ret (0) = 420.0 * mm2units;
4076  ret (1) = 594.0 * mm2units;
4077  }
4078  else if (typ.compare ("a3"))
4079  {
4080  ret (0) = 297.0 * mm2units;
4081  ret (1) = 420.0 * mm2units;
4082  }
4083  else if (typ.compare ("a4"))
4084  {
4085  ret (0) = 210.0 * mm2units;
4086  ret (1) = 297.0 * mm2units;
4087  }
4088  else if (typ.compare ("a5"))
4089  {
4090  ret (0) = 148.0 * mm2units;
4091  ret (1) = 210.0 * mm2units;
4092  }
4093  else if (typ.compare ("b0"))
4094  {
4095  ret (0) = 1029.0 * mm2units;
4096  ret (1) = 1456.0 * mm2units;
4097  }
4098  else if (typ.compare ("b1"))
4099  {
4100  ret (0) = 728.0 * mm2units;
4101  ret (1) = 1028.0 * mm2units;
4102  }
4103  else if (typ.compare ("b2"))
4104  {
4105  ret (0) = 514.0 * mm2units;
4106  ret (1) = 728.0 * mm2units;
4107  }
4108  else if (typ.compare ("b3"))
4109  {
4110  ret (0) = 364.0 * mm2units;
4111  ret (1) = 514.0 * mm2units;
4112  }
4113  else if (typ.compare ("b4"))
4114  {
4115  ret (0) = 257.0 * mm2units;
4116  ret (1) = 364.0 * mm2units;
4117  }
4118  else if (typ.compare ("b5"))
4119  {
4120  ret (0) = 182.0 * mm2units;
4121  ret (1) = 257.0 * mm2units;
4122  }
4123  else if (typ.compare ("arch-a"))
4124  {
4125  ret (0) = 9.0 * in2units;
4126  ret (1) = 12.0 * in2units;
4127  }
4128  else if (typ.compare ("arch-b"))
4129  {
4130  ret (0) = 12.0 * in2units;
4131  ret (1) = 18.0 * in2units;
4132  }
4133  else if (typ.compare ("arch-c"))
4134  {
4135  ret (0) = 18.0 * in2units;
4136  ret (1) = 24.0 * in2units;
4137  }
4138  else if (typ.compare ("arch-d"))
4139  {
4140  ret (0) = 24.0 * in2units;
4141  ret (1) = 36.0 * in2units;
4142  }
4143  else if (typ.compare ("arch-e"))
4144  {
4145  ret (0) = 36.0 * in2units;
4146  ret (1) = 48.0 * in2units;
4147  }
4148  else if (typ.compare ("a"))
4149  {
4150  ret (0) = 8.5 * in2units;
4151  ret (1) = 11.0 * in2units;
4152  }
4153  else if (typ.compare ("b"))
4154  {
4155  ret (0) = 11.0 * in2units;
4156  ret (1) = 17.0 * in2units;
4157  }
4158  else if (typ.compare ("c"))
4159  {
4160  ret (0) = 17.0 * in2units;
4161  ret (1) = 22.0 * in2units;
4162  }
4163  else if (typ.compare ("d"))
4164  {
4165  ret (0) = 22.0 * in2units;
4166  ret (1) = 34.0 * in2units;
4167  }
4168  else if (typ.compare ("e"))
4169  {
4170  ret (0) = 34.0 * in2units;
4171  ret (1) = 43.0 * in2units;
4172  }
4173  }
4174 
4175  return ret;
4176 }
4177 
4178 
4179 Matrix
4181 {
4182  Matrix pos = get_position ().matrix_value ();
4183  Matrix sz;
4184 
4185  caseless_str funits = get_units ();
4186  caseless_str punits = get_paperunits ();
4187 
4188  // Convert position from figure units to paperunits
4189  if (funits == "normalized" || punits == "normalized")
4190  {
4191  sz = screen_size_pixels ();
4192  pos = convert_position (pos, funits, "inches", sz);
4193 
4194  if (punits == "normalized")
4195  sz = papersize_from_type ("points", get_papertype ());
4196 
4197  pos = convert_position (pos, "inches", punits, sz);
4198  }
4199  else
4200  pos = convert_position (pos, funits, punits, sz);
4201 
4202  // Center the figure on the page
4203  sz = get_papersize ().matrix_value ();
4204 
4205  pos(0) = sz(0)/2 - pos(2)/2;
4206  pos(1) = sz(1)/2 - pos(3)/2;
4207 
4208  return pos;
4209 }
4210 
4211 /*
4212 %!test
4213 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4214 %! in_pos = [0 0 4 5];
4215 %! tol = 20 * eps ();
4216 %! unwind_protect
4217 %! ## paperpositionmode "auto" converts figure size to paper units
4218 %! set (hf, "units", "inches");
4219 %! set (hf, "position", in_pos);
4220 %! set (hf, "paperunits", "centimeters");
4221 %! psz = get (hf, "papersize");
4222 %! fsz = in_pos(3:4) * 2.54;
4223 %! pos = [(psz/2 .- fsz/2) fsz];
4224 %! set (hf, "paperpositionmode", "auto");
4225 %! assert (get (hf, "paperposition"), pos, tol)
4226 %! unwind_protect_cleanup
4227 %! close (hf);
4228 %! end_unwind_protect
4229 
4230 %!test
4231 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4232 %! in_pos = [0 0 4 5];
4233 %! tol = 20 * eps ();
4234 %! unwind_protect
4235 %! ## likewise with normalized units
4236 %! set (hf, "units", "inches");
4237 %! set (hf, "position", in_pos);
4238 %! psz = get (hf, "papersize");
4239 %! set (hf, "paperunits", "normalized");
4240 %! fsz = in_pos(3:4) ./ psz;
4241 %! pos = [([0.5 0.5] .- fsz/2) fsz];
4242 %! assert (get (hf, "paperposition"), pos, tol)
4243 %! unwind_protect_cleanup
4244 %! close (hf);
4245 %! end_unwind_protect
4246 
4247 %!test
4248 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4249 %! in_pos = [0 0 4 5];
4250 %! tol = 20 * eps ();
4251 %! unwind_protect
4252 %! ## changing papertype updates paperposition
4253 %! set (hf, "units", "inches");
4254 %! set (hf, "position", in_pos);
4255 %! set (hf, "papertype", "a4");
4256 %! psz = get (hf, "papersize");
4257 %! fsz = in_pos(3:4);
4258 %! pos = [(psz/2 .- fsz/2) fsz];
4259 %! assert (get (hf, "paperposition"), pos, tol)
4260 %! unwind_protect_cleanup
4261 %! close (hf);
4262 %! end_unwind_protect
4263 
4264 %!test
4265 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4266 %! in_pos = [0 0 4 5];
4267 %! tol = 20 * eps ();
4268 %! unwind_protect
4269 %! ## lanscape updates paperposition
4270 %! set (hf, "units", "inches");
4271 %! set (hf, "position", in_pos);
4272 %! set (hf, "paperorientation", "landscape");
4273 %! psz = get (hf, "papersize");
4274 %! fsz = in_pos(3:4);
4275 %! pos = [(psz/2 .- fsz/2) fsz];
4276 %! assert (get (hf, "paperposition"), pos, tol)
4277 %! unwind_protect_cleanup
4278 %! close (hf);
4279 %! end_unwind_protect
4280 
4281 %!test
4282 %! hf = figure ("visible", "off", "paperpositionmode", "auto");
4283 %! in_pos = [0 0 4 5];
4284 %! unwind_protect
4285 %! ## back to manual mode
4286 %! set (hf, "paperposition", in_pos * 1.1)
4287 %! assert (get (hf, "paperpositionmode"), "manual")
4288 %! assert (get (hf, "paperposition"), in_pos * 1.1)
4289 %! unwind_protect_cleanup
4290 %! close (hf);
4291 %! end_unwind_protect
4292 */
4293 
4294 void
4296 {
4297  Matrix pos = get_paperposition ().matrix_value ();
4298  Matrix sz = get_papersize ().matrix_value ();
4299 
4300  pos(0) /= sz(0);
4301  pos(1) /= sz(1);
4302  pos(2) /= sz(0);
4303  pos(3) /= sz(1);
4304 
4305  std::string porient = get_paperorientation ();
4306  caseless_str punits = get_paperunits ();
4307  caseless_str typ = get_papertype ();
4308 
4309  if (typ.compare ("<custom>"))
4310  {
4311  if (old_paperunits.compare ("centimeters"))
4312  {
4313  sz(0) /= 2.54;
4314  sz(1) /= 2.54;
4315  }
4316  else if (old_paperunits.compare ("points"))
4317  {
4318  sz(0) /= 72.0;
4319  sz(1) /= 72.0;
4320  }
4321 
4322  if (punits.compare ("centimeters"))
4323  {
4324  sz(0) *= 2.54;
4325  sz(1) *= 2.54;
4326  }
4327  else if (punits.compare ("points"))
4328  {
4329  sz(0) *= 72.0;
4330  sz(1) *= 72.0;
4331  }
4332  }
4333  else
4334  {
4335  sz = papersize_from_type (punits, typ);
4336  if (porient == "landscape")
4337  std::swap (sz(0), sz(1));
4338  }
4339 
4340  pos(0) *= sz(0);
4341  pos(1) *= sz(1);
4342  pos(2) *= sz(0);
4343  pos(3) *= sz(1);
4344 
4345  papersize.set (octave_value (sz));
4346  paperposition.set (octave_value (pos));
4347 }
4348 
4349 void
4351 {
4352  caseless_str typ = get_papertype ();
4353  if (! typ.compare ("<custom>"))
4354  {
4355  Matrix sz = papersize_from_type (get_paperunits (), typ);
4356  if (get_paperorientation () == "landscape")
4357  std::swap (sz(0), sz(1));
4358  // Call papersize.set rather than set_papersize to avoid loops
4359  // between update_papersize and update_papertype
4360  papersize.set (octave_value (sz));
4361  }
4362 
4363  if (paperpositionmode.is ("auto"))
4364  paperposition.set (get_auto_paperposition ());
4365 }
4366 
4367 void
4369 {
4370  Matrix sz = get_papersize ().matrix_value ();
4371  if (sz(0) > sz(1))
4372  {
4373  std::swap (sz(0), sz(1));
4374  papersize.set (octave_value (sz));
4375  paperorientation.set (octave_value ("landscape"));
4376  }
4377  else
4378  {
4379  paperorientation.set ("portrait");
4380  }
4381  std::string punits = get_paperunits ();
4382  if (punits == "centimeters")
4383  {
4384  sz(0) /= 2.54;
4385  sz(1) /= 2.54;
4386  }
4387  else if (punits == "points")
4388  {
4389  sz(0) /= 72.0;
4390  sz(1) /= 72.0;
4391  }
4392  if (punits == "normalized")
4393  {
4394  caseless_str typ = get_papertype ();
4395  if (get_papertype () == "<custom>")
4396  error ("set: can't set the papertype to <custom> when the paperunits is normalized");
4397  }
4398  else
4399  {
4400  // TODO - the papersizes info is also in papersize_from_type().
4401  // Both should be rewritten to avoid the duplication.
4402  std::string typ = "<custom>";
4403  const double mm2in = 1.0 / 25.4;
4404  const double tol = 0.01;
4405 
4406  if (std::abs (sz(0) - 8.5) + std::abs (sz(1) - 11.0) < tol)
4407  typ = "usletter";
4408  else if (std::abs (sz(0) - 8.5) + std::abs (sz(1) - 14.0) < tol)
4409  typ = "uslegal";
4410  else if (std::abs (sz(0) - 11.0) + std::abs (sz(1) - 17.0) < tol)
4411  typ = "tabloid";
4412  else if (std::abs (sz(0) - 841.0 * mm2in)
4413  + std::abs (sz(1) - 1198.0 * mm2in) < tol)
4414  typ = "a0";
4415  else if (std::abs (sz(0) - 594.0 * mm2in)
4416  + std::abs (sz(1) - 841.0 * mm2in) < tol)
4417  typ = "a1";
4418  else if (std::abs (sz(0) - 420.0 * mm2in)
4419  + std::abs (sz(1) - 594.0 * mm2in) < tol)
4420  typ = "a2";
4421  else if (std::abs (sz(0) - 297.0 * mm2in)
4422  + std::abs (sz(1) - 420.0 * mm2in) < tol)
4423  typ = "a3";
4424  else if (std::abs (sz(0) - 210.0 * mm2in)
4425  + std::abs (sz(1) - 297.0 * mm2in) < tol)
4426  typ = "a4";
4427  else if (std::abs (sz(0) - 148.0 * mm2in)
4428  + std::abs (sz(1) - 210.0 * mm2in) < tol)
4429  typ = "a5";
4430  else if (std::abs (sz(0) - 1029.0 * mm2in)
4431  + std::abs (sz(1) - 1456.0 * mm2in) < tol)
4432  typ = "b0";
4433  else if (std::abs (sz(0) - 728.0 * mm2in)
4434  + std::abs (sz(1) - 1028.0 * mm2in) < tol)
4435  typ = "b1";
4436  else if (std::abs (sz(0) - 514.0 * mm2in)
4437  + std::abs (sz(1) - 728.0 * mm2in) < tol)
4438  typ = "b2";
4439  else if (std::abs (sz(0) - 364.0 * mm2in)
4440  + std::abs (sz(1) - 514.0 * mm2in) < tol)
4441  typ = "b3";
4442  else if (std::abs (sz(0) - 257.0 * mm2in)
4443  + std::abs (sz(1) - 364.0 * mm2in) < tol)
4444  typ = "b4";
4445  else if (std::abs (sz(0) - 182.0 * mm2in)
4446  + std::abs (sz(1) - 257.0 * mm2in) < tol)
4447  typ = "b5";
4448  else if (std::abs (sz(0) - 9.0)
4449  + std::abs (sz(1) - 12.0) < tol)
4450  typ = "arch-a";
4451  else if (std::abs (sz(0) - 12.0)
4452  + std::abs (sz(1) - 18.0) < tol)
4453  typ = "arch-b";
4454  else if (std::abs (sz(0) - 18.0)
4455  + std::abs (sz(1) - 24.0) < tol)
4456  typ = "arch-c";
4457  else if (std::abs (sz(0) - 24.0)
4458  + std::abs (sz(1) - 36.0) < tol)
4459  typ = "arch-d";
4460  else if (std::abs (sz(0) - 36.0)
4461  + std::abs (sz(1) - 48.0) < tol)
4462  typ = "arch-e";
4463  else if (std::abs (sz(0) - 8.5)
4464  + std::abs (sz(1) - 11.0) < tol)
4465  typ = "a";
4466  else if (std::abs (sz(0) - 11.0)
4467  + std::abs (sz(1) - 17.0) < tol)
4468  typ = "b";
4469  else if (std::abs (sz(0) - 17.0)
4470  + std::abs (sz(1) - 22.0) < tol)
4471  typ = "c";
4472  else if (std::abs (sz(0) - 22.0)
4473  + std::abs (sz(1) - 34.0) < tol)
4474  typ = "d";
4475  else if (std::abs (sz(0) - 34.0)
4476  + std::abs (sz(1) - 43.0) < tol)
4477  typ = "e";
4478  // Call papertype.set rather than set_papertype to avoid loops between
4479  // update_papersize and update_papertype
4480  papertype.set (typ);
4481  }
4482  if (punits == "centimeters")
4483  {
4484  sz(0) *= 2.54;
4485  sz(1) *= 2.54;
4486  }
4487  else if (punits == "points")
4488  {
4489  sz(0) *= 72.0;
4490  sz(1) *= 72.0;
4491  }
4492  if (get_paperorientation () == "landscape")
4493  {
4494  std::swap (sz(0), sz(1));
4495  papersize.set (octave_value (sz));
4496  }
4497 
4498  if (paperpositionmode.is ("auto"))
4499  paperposition.set (get_auto_paperposition ());
4500 }
4501 
4502 /*
4503 %!test
4504 %! hf = figure ("visible", "off");
4505 %! unwind_protect
4506 %! set (hf, "paperunits", "inches");
4507 %! set (hf, "papersize", [5, 4]);
4508 %! set (hf, "paperunits", "points");
4509 %! assert (get (hf, "papersize"), [5, 4] * 72, 1);
4510 %! papersize = get (hf, "papersize");
4511 %! set (hf, "papersize", papersize + 1);
4512 %! set (hf, "papersize", papersize);
4513 %! assert (get (hf, "papersize"), [5, 4] * 72, 1);
4514 %! unwind_protect_cleanup
4515 %! close (hf);
4516 %! end_unwind_protect
4517 
4518 %!test
4519 %! hf = figure ("visible", "off");
4520 %! unwind_protect
4521 %! set (hf, "paperunits", "inches");
4522 %! set (hf, "papersize", [5, 4]);
4523 %! set (hf, "paperunits", "centimeters");
4524 %! assert (get (hf, "papersize"), [5, 4] * 2.54, 2.54/72);
4525 %! papersize = get (hf, "papersize");
4526 %! set (hf, "papersize", papersize + 1);
4527 %! set (hf, "papersize", papersize);
4528 %! assert (get (hf, "papersize"), [5, 4] * 2.54, 2.54/72);
4529 %! unwind_protect_cleanup
4530 %! close (hf);
4531 %! end_unwind_protect
4532 */
4533 
4534 void
4536 {
4537  std::string porient = get_paperorientation ();
4538  Matrix sz = get_papersize ().matrix_value ();
4539  Matrix pos = get_paperposition ().matrix_value ();
4540  if ((sz(0) > sz(1) && porient == "portrait")
4541  || (sz(0) < sz(1) && porient == "landscape"))
4542  {
4543  std::swap (sz(0), sz(1));
4544  std::swap (pos(0), pos(1));
4545  std::swap (pos(2), pos(3));
4546  // Call papertype.set rather than set_papertype to avoid loops
4547  // between update_papersize and update_papertype
4548  papersize.set (octave_value (sz));
4549  paperposition.set (octave_value (pos));
4550  }
4551 
4552  if (paperpositionmode.is ("auto"))
4553  paperposition.set (get_auto_paperposition ());
4554 }
4555 
4556 /*
4557 %!test
4558 %! hf = figure ("visible", "off");
4559 %! unwind_protect
4560 %! tol = 100 * eps ();
4561 %! ## UPPER case and MiXed case is part of test and should not be changed.
4562 %! set (hf, "paperorientation", "PORTRAIT");
4563 %! set (hf, "paperunits", "inches");
4564 %! set (hf, "papertype", "USletter");
4565 %! assert (get (hf, "papersize"), [8.5, 11.0], tol);
4566 %! set (hf, "paperorientation", "Landscape");
4567 %! assert (get (hf, "papersize"), [11.0, 8.5], tol);
4568 %! set (hf, "paperunits", "centimeters");
4569 %! assert (get (hf, "papersize"), [11.0, 8.5] * 2.54, tol);
4570 %! set (hf, "papertype", "a4");
4571 %! assert (get (hf, "papersize"), [29.7, 21.0], tol);
4572 %! set (hf, "paperunits", "inches", "papersize", [8.5, 11.0]);
4573 %! assert (get (hf, "papertype"), "usletter");
4574 %! assert (get (hf, "paperorientation"), "portrait");
4575 %! set (hf, "papersize", [11.0, 8.5]);
4576 %! assert (get (hf, "papertype"), "usletter");
4577 %! assert (get (hf, "paperorientation"), "landscape");
4578 %! unwind_protect_cleanup
4579 %! close (hf);
4580 %! end_unwind_protect
4581 */
4582 
4583 void
4585 {
4586  if (! error_state)
4587  {
4588  caseless_str old_units = get_units ();
4589  if (units.set (v, true))
4590  {
4591  update_units (old_units);
4592  mark_modified ();
4593  }
4594  }
4595 }
4596 
4597 void
4599 {
4600  position.set (convert_position (get_position ().matrix_value (), old_units,
4601  get_units (), screen_size_pixels ()), false);
4602 }
4603 
4604 /*
4605 %!test
4606 %! hf = figure ("visible", "off");
4607 %! old_units = get (0, "units");
4608 %! unwind_protect
4609 %! set (0, "units", "pixels");
4610 %! rsz = get (0, "screensize");
4611 %! set (gcf (), "units", "pixels");
4612 %! fsz = get (gcf (), "position");
4613 %! set (gcf (), "units", "normalized");
4614 %! pos = get (gcf (), "position");
4615 %! assert (pos, (fsz - [1, 1, 0, 0]) ./ rsz([3, 4, 3, 4]));
4616 %! unwind_protect_cleanup
4617 %! close (hf);
4618 %! set (0, "units", old_units);
4619 %! end_unwind_protect
4620 */
4621 
4622 std::string
4624 {
4625  if (is_numbertitle ())
4626  {
4627  std::ostringstream os;
4628  std::string nm = get_name ();
4629 
4630  os << "Figure " << __myhandle__.value ();
4631  if (! nm.empty ())
4632  os << ": " << get_name ();
4633 
4634  return os.str ();
4635  }
4636  else
4637  return get_name ();
4638 }
4639 
4642 {
4643  octave_value retval = default_properties.lookup (name);
4644 
4645  if (retval.is_undefined ())
4646  {
4647  graphics_handle parent = get_parent ();
4648  graphics_object parent_obj = gh_manager::get_object (parent);
4649 
4650  retval = parent_obj.get_default (name);
4651  }
4652 
4653  return retval;
4654 }
4655 
4656 void
4658 {
4659  // empty list of local defaults
4661 
4663  plist.erase ("units");
4664  plist.erase ("position");
4665  plist.erase ("outerposition");
4666  plist.erase ("paperunits");
4667  plist.erase ("paperposition");
4668  plist.erase ("windowstyle");
4669 
4671 }
4672 
4673 // ---------------------------------------------------------------------
4674 
4675 void
4677 {
4678  position.add_constraint (dim_vector (1, 4));
4679  outerposition.add_constraint (dim_vector (1, 4));
4680  tightinset.add_constraint (dim_vector (1, 4));
4681  looseinset.add_constraint (dim_vector (1, 4));
4682  colororder.add_constraint (dim_vector (-1, 3));
4683  dataaspectratio.add_constraint (dim_vector (1, 3));
4684  plotboxaspectratio.add_constraint (dim_vector (1, 3));
4685  alim.add_constraint (2);
4686  clim.add_constraint (2);
4687  xlim.add_constraint (2);
4688  ylim.add_constraint (2);
4689  zlim.add_constraint (2);
4690  xtick.add_constraint (dim_vector (1, -1));
4691  ytick.add_constraint (dim_vector (1, -1));
4692  ztick.add_constraint (dim_vector (1, -1));
4693  ticklength.add_constraint (dim_vector (1, 2));
4694  Matrix vw (1, 2, 0);
4695  vw(1) = 90;
4696  view = vw;
4697  view.add_constraint (dim_vector (1, 2));
4698  cameraposition.add_constraint (dim_vector (1, 3));
4699  cameratarget.add_constraint (dim_vector (1, 3));
4700  Matrix upv (1, 3, 0.0);
4701  upv(2) = 1.0;
4702  cameraupvector = upv;
4703  cameraupvector.add_constraint (dim_vector (1, 3));
4704  currentpoint.add_constraint (dim_vector (2, 3));
4705  // No constraints for hidden transform properties
4706  update_font ();
4707 
4708  x_zlim.resize (1, 2);
4709 
4710  sx = "linear";
4711  sy = "linear";
4712  sz = "linear";
4713 
4714  calc_ticklabels (xtick, xticklabel, xscale.is ("log"));
4715  calc_ticklabels (ytick, yticklabel, yscale.is ("log"));
4716  calc_ticklabels (ztick, zticklabel, zscale.is ("log"));
4717 
4718  xset (xlabel.handle_value (), "handlevisibility", "off");
4719  xset (ylabel.handle_value (), "handlevisibility", "off");
4720  xset (zlabel.handle_value (), "handlevisibility", "off");
4721  xset (title.handle_value (), "handlevisibility", "off");
4722 
4723  xset (xlabel.handle_value (), "horizontalalignment", "center");
4724  xset (xlabel.handle_value (), "horizontalalignmentmode", "auto");
4725  xset (ylabel.handle_value (), "horizontalalignment", "center");
4726  xset (ylabel.handle_value (), "horizontalalignmentmode", "auto");
4727  xset (zlabel.handle_value (), "horizontalalignment", "right");
4728  xset (zlabel.handle_value (), "horizontalalignmentmode", "auto");
4729  xset (title.handle_value (), "horizontalalignment", "center");
4730  xset (title.handle_value (), "horizontalalignmentmode", "auto");
4731 
4732  xset (xlabel.handle_value (), "verticalalignment", "top");
4733  xset (xlabel.handle_value (), "verticalalignmentmode", "auto");
4734  xset (ylabel.handle_value (), "verticalalignment", "bottom");
4735  xset (ylabel.handle_value (), "verticalalignmentmode", "auto");
4736  xset (title.handle_value (), "verticalalignment", "bottom");
4737  xset (title.handle_value (), "verticalalignmentmode", "auto");
4738 
4739  xset (ylabel.handle_value (), "rotation", 90.0);
4740  xset (ylabel.handle_value (), "rotationmode", "auto");
4741 
4742  xset (zlabel.handle_value (), "visible", "off");
4743 
4744  xset (xlabel.handle_value (), "clipping", "off");
4745  xset (ylabel.handle_value (), "clipping", "off");
4746  xset (zlabel.handle_value (), "clipping", "off");
4747  xset (title.handle_value (), "clipping", "off");
4748 
4749  xset (xlabel.handle_value (), "autopos_tag", "xlabel");
4750  xset (ylabel.handle_value (), "autopos_tag", "ylabel");
4751  xset (zlabel.handle_value (), "autopos_tag", "zlabel");
4752  xset (title.handle_value (), "autopos_tag", "title");
4753 
4754  adopt (xlabel.handle_value ());
4755  adopt (ylabel.handle_value ());
4756  adopt (zlabel.handle_value ());
4757  adopt (title.handle_value ());
4758 
4759  Matrix tlooseinset = default_axes_position ();
4760  tlooseinset(2) = 1-tlooseinset(0)-tlooseinset(2);
4761  tlooseinset(3) = 1-tlooseinset(1)-tlooseinset(3);
4762  looseinset = tlooseinset;
4763 }
4764 
4765 Matrix
4767 {
4768  Matrix pos = init_pos;
4770  Matrix parent_bb = obj.get_properties ().get_boundingbox (true);
4771  Matrix ext = get_extent (true, true);
4772  ext(1) = parent_bb(3) - ext(1) - ext(3);
4773  ext(0)++;
4774  ext(1)++;
4775  ext = convert_position (ext, "pixels", get_units (),
4776  parent_bb.extract_n (0, 2, 1, 2));
4777  if (ext(0) < pos(0))
4778  {
4779  pos(2) += pos(0)-ext(0);
4780  pos(0) = ext(0);
4781  }
4782  if (ext(0)+ext(2) > pos(0)+pos(2))
4783  pos(2) = ext(0)+ext(2)-pos(0);
4784 
4785  if (ext(1) < pos(1))
4786  {
4787  pos(3) += pos(1)-ext(1);
4788  pos(1) = ext(1);
4789  }
4790  if (ext(1)+ext(3) > pos(1)+pos(3))
4791  pos(3) = ext(1)+ext(3)-pos(1);
4792  return pos;
4793 }
4794 
4795 void
4797 {
4798  // First part is equivalent to `update_tightinset ()'
4799  if (activepositionproperty.is ("position"))
4800  update_position ();
4801  else
4802  update_outerposition ();
4803  caseless_str old_units = get_units ();
4804  set_units ("normalized");
4805  Matrix pos = position.get ().matrix_value ();
4806  Matrix outpos = outerposition.get ().matrix_value ();
4807  Matrix tightpos = calc_tightbox (pos);
4808  Matrix tinset (1, 4, 1.0);
4809  tinset(0) = pos(0)-tightpos(0);
4810  tinset(1) = pos(1)-tightpos(1);
4811  tinset(2) = tightpos(0)+tightpos(2)-pos(0)-pos(2);
4812  tinset(3) = tightpos(1)+tightpos(3)-pos(1)-pos(3);
4813  tightinset = tinset;
4814  set_units (old_units);
4815  update_transform ();
4816  if (activepositionproperty.is ("position"))
4817  update_position ();
4818  else
4819  update_outerposition ();
4820 }
4821 
4822 /*
4823 %!testif HAVE_FLTK
4824 %! hf = figure ("visible", "off");
4825 %! graphics_toolkit (hf, "fltk");
4826 %! unwind_protect
4827 %! subplot(2,1,1); plot(rand(10,1)); subplot(2,1,2); plot(rand(10,1));
4828 %! hax = findall (gcf (), "type", "axes");
4829 %! positions = cell2mat (get (hax, "position"));
4830 %! outerpositions = cell2mat (get (hax, "outerposition"));
4831 %! looseinsets = cell2mat (get (hax, "looseinset"));
4832 %! tightinsets = cell2mat (get (hax, "tightinset"));
4833 %! subplot(2,1,1); plot(rand(10,1)); subplot(2,1,2); plot(rand(10,1));
4834 %! hax = findall (gcf (), "type", "axes");
4835 %! assert (cell2mat (get (hax, "position")), positions, 1e-4);
4836 %! assert (cell2mat (get (hax, "outerposition")), outerpositions, 1e-4);
4837 %! assert (cell2mat (get (hax, "looseinset")), looseinsets, 1e-4);
4838 %! assert (cell2mat (get (hax, "tightinset")), tightinsets, 1e-4);
4839 %! unwind_protect_cleanup
4840 %! close (hf);
4841 %! end_unwind_protect
4842 %!testif HAVE_FLTK
4843 %! hf = figure ("visible", "off");
4844 %! graphics_toolkit (hf, "fltk");
4845 %! fpos = get (hf, "position");
4846 %! unwind_protect
4847 %! plot (rand (3))
4848 %! position = get (gca, "position");
4849 %! outerposition = get (gca, "outerposition");
4850 %! looseinset = get (gca, "looseinset");
4851 %! tightinset = get (gca, "tightinset");
4852 %! set (hf, "position", [fpos(1:2), 2*fpos(3:4)])
4853 %! set (hf, "position", fpos);
4854 %! assert (get (gca, "outerposition"), outerposition, 0.001)
4855 %! assert (get (gca, "position"), position, 0.001)
4856 %! assert (get (gca, "looseinset"), looseinset, 0.001)
4857 %! assert (get (gca, "tightinset"), tightinset, 0.001)
4858 %! unwind_protect_cleanup
4859 %! close (hf);
4860 %! end_unwind_protect
4861 %!testif HAVE_FLTK
4862 %! hf = figure ("visible", "off");
4863 %! graphics_toolkit (hf, "fltk");
4864 %! fpos = get (hf, "position");
4865 %! set (gca, "activepositionproperty", "position")
4866 %! unwind_protect
4867 %! plot (rand (3))
4868 %! position = get (gca, "position");
4869 %! outerposition = get (gca, "outerposition");
4870 %! looseinset = get (gca, "looseinset");
4871 %! tightinset = get (gca, "tightinset");
4872 %! set (hf, "position", [fpos(1:2), 2*fpos(3:4)])
4873 %! set (hf, "position", fpos);
4874 %! assert (get (gca, "position"), position, 0.001)
4875 %! assert (get (gca, "outerposition"), outerposition, 0.001)
4876 %! assert (get (gca, "looseinset"), looseinset, 0.001)
4877 %! assert (get (gca, "tightinset"), tightinset, 0.001)
4878 %! unwind_protect_cleanup
4879 %! close (hf);
4880 %! end_unwind_protect
4881 */
4882 
4883 void
4885  const std::string& who,
4886  const octave_value& v)
4887 {
4888  if (v.is_string ())
4889  {
4890  xset (hp.handle_value (), "string", v);
4891  return;
4892  }
4893 
4894  graphics_handle val;
4896 
4897  if (go.isa ("text"))
4898  val = ::reparent (v, "set", who, __myhandle__, false);
4899  else
4900  {
4901  std::string cname = v.class_name ();
4902 
4903  error ("set: expecting text graphics object or character string for %s property, found %s",
4904  who.c_str (), cname.c_str ());
4905  }
4906 
4907  if (! error_state)
4908  {
4909  xset (val, "handlevisibility", "off");
4910 
4912 
4914 
4915  hp = val;
4916 
4917  adopt (hp.handle_value ());
4918  }
4919 }
4920 
4921 void
4923 {
4924  set_text_child (xlabel, "xlabel", v);
4925  xset (xlabel.handle_value (), "positionmode", "auto");
4926  xset (xlabel.handle_value (), "rotationmode", "auto");
4927  xset (xlabel.handle_value (), "horizontalalignmentmode", "auto");
4928  xset (xlabel.handle_value (), "verticalalignmentmode", "auto");
4929  xset (xlabel.handle_value (), "clipping", "off");
4930  xset (xlabel.handle_value (), "color", get_xcolor ());
4931  xset (xlabel.handle_value (), "autopos_tag", "xlabel");
4932  update_xlabel_position ();
4933 }
4934 
4935 void
4937 {
4938  set_text_child (ylabel, "ylabel", v);
4939  xset (ylabel.handle_value (), "positionmode", "auto");
4940  xset (ylabel.handle_value (), "rotationmode", "auto");
4941  xset (ylabel.handle_value (), "horizontalalignmentmode", "auto");
4942  xset (ylabel.handle_value (), "verticalalignmentmode", "auto");
4943  xset (ylabel.handle_value (), "clipping", "off");
4944  xset (ylabel.handle_value (), "color", get_ycolor ());
4945  xset (ylabel.handle_value (), "autopos_tag", "ylabel");
4946  update_ylabel_position ();
4947 }
4948 
4949 void
4951 {
4952  set_text_child (zlabel, "zlabel", v);
4953  xset (zlabel.handle_value (), "positionmode", "auto");
4954  xset (zlabel.handle_value (), "rotationmode", "auto");
4955  xset (zlabel.handle_value (), "horizontalalignmentmode", "auto");
4956  xset (zlabel.handle_value (), "verticalalignmentmode", "auto");
4957  xset (zlabel.handle_value (), "clipping", "off");
4958  xset (zlabel.handle_value (), "color", get_zcolor ());
4959  xset (zlabel.handle_value (), "autopos_tag", "zlabel");
4960  update_zlabel_position ();
4961 }
4962 
4963 void
4965 {
4966  set_text_child (title, "title", v);
4967  xset (title.handle_value (), "positionmode", "auto");
4968  xset (title.handle_value (), "horizontalalignment", "center");
4969  xset (title.handle_value (), "horizontalalignmentmode", "auto");
4970  xset (title.handle_value (), "verticalalignment", "bottom");
4971  xset (title.handle_value (), "verticalalignmentmode", "auto");
4972  xset (title.handle_value (), "clipping", "off");
4973  xset (title.handle_value (), "autopos_tag", "title");
4974  update_title_position ();
4975 }
4976 
4977 void
4979  const std::string& mode)
4980 {
4981  box = "on";
4982  colororder = default_colororder ();
4983  // Note: dataspectratio will be set through update_aspectratios
4984  dataaspectratiomode = "auto";
4985  layer = "bottom";
4986 
4987  Matrix tlim (1, 2, 0.0);
4988  tlim(1) = 1;
4989  xlim = tlim;
4990  ylim = tlim;
4991  zlim = tlim;
4992 
4993  Matrix cl (1, 2, 0);
4994  cl(1) = 1;
4995  clim = cl;
4996 
4997  alim = tlim;
4998 
4999  xlimmode = "auto";
5000  ylimmode = "auto";
5001  zlimmode = "auto";
5002  climmode = "auto";
5003  alimmode = "auto";
5004 
5005  xgrid = "off";
5006  ygrid = "off";
5007  zgrid = "off";
5008  xminorgrid = "off";
5009  yminorgrid = "off";
5010  zminorgrid = "off";
5011  xtick = Matrix ();
5012  ytick = Matrix ();
5013  ztick = Matrix ();
5014  xtickmode = "auto";
5015  ytickmode = "auto";
5016  ztickmode = "auto";
5017  xminortick = "off";
5018  yminortick = "off";
5019  zminortick = "off";
5020  xticklabel = "";
5021  yticklabel = "";
5022  zticklabel = "";
5023  xticklabelmode = "auto";
5024  yticklabelmode = "auto";
5025  zticklabelmode = "auto";
5026 
5027  interpreter = "none";
5028 
5029  color = color_values ("white");
5030  xcolor = color_values ("black");
5031  ycolor = color_values ("black");
5032  zcolor = color_values ("black");
5033  xscale = "linear";
5034  yscale = "linear";
5035  zscale = "linear";
5036  xdir = "normal";
5037  ydir = "normal";
5038  zdir = "normal";
5039  yaxislocation = "left";
5040  xaxislocation = "bottom";
5041 
5042  Matrix tview (1, 2, 0.0);
5043  tview(1) = 90;
5044  view = tview;
5045 
5046  __hold_all__ = "off";
5047  nextplot = "replace";
5048 
5049  ambientlightcolor = Matrix (1, 3, 1.0);
5050 
5051  // Note: camera properties (not mode) will be set in update_transform
5052  camerapositionmode = "auto";
5053  cameratargetmode = "auto";
5054  cameraupvectormode = "auto";
5055  cameraviewanglemode = "auto";
5056 
5057  drawmode = "normal";
5058 
5059  fontangle = "normal";
5060  fontname = OCTAVE_DEFAULT_FONTNAME;
5061  fontsize = 10;
5062  fontunits = "points";
5063  fontweight = "normal";
5064 
5065  gridlinestyle = ":";
5066  linestyleorder = "-";
5067  linewidth = 0.5;
5068  minorgridlinestyle = ":";
5069 
5070  // Note: plotboxaspectratio will be set through update_aspectratios
5071  plotboxaspectratiomode = "auto";
5072  projection = "orthographic";
5073 
5074  tickdir = "in";
5075  tickdirmode = "auto";
5076  ticklength = default_axes_ticklength ();
5077 
5078  tightinset = Matrix (1, 4, 0.0);
5079 
5080  sx = "linear";
5081  sy = "linear";
5082  sz = "linear";
5083 
5084  visible = "on";
5085 
5086  // Replace/Reset preserves Position and Units properties
5087  if (mode != "replace" && mode != "reset")
5088  {
5089  outerposition = default_axes_outerposition ();
5090  position = default_axes_position ();
5091  activepositionproperty = "outerposition";
5092  }
5093 
5094  if (mode != "reset")
5095  {
5096  delete_children (true);
5097 
5098  xlabel.invalidate ();
5099  ylabel.invalidate ();
5100  zlabel.invalidate ();
5101  title.invalidate ();
5102 
5103  xlabel = gh_manager::make_graphics_handle ("text", __myhandle__,
5104  false, false, false);
5105  ylabel = gh_manager::make_graphics_handle ("text", __myhandle__,
5106  false, false, false);
5107  zlabel = gh_manager::make_graphics_handle ("text", __myhandle__,
5108  false, false, false);
5109  title = gh_manager::make_graphics_handle ("text", __myhandle__,
5110  false, false, false);
5111 
5112  adopt (xlabel.handle_value ());
5113  adopt (ylabel.handle_value ());
5114  adopt (zlabel.handle_value ());
5115  adopt (title.handle_value ());
5116 
5117  update_xlabel_position ();
5118  update_ylabel_position ();
5119  update_zlabel_position ();
5120  update_title_position ();
5121  }
5122  else
5123  {
5124  graphics_object go = gh_manager::get_object (xlabel.handle_value ());
5126  go = gh_manager::get_object (ylabel.handle_value ());
5128  go = gh_manager::get_object (zlabel.handle_value ());
5130  go = gh_manager::get_object (title.handle_value ());
5132  }
5133 
5134  xset (xlabel.handle_value (), "handlevisibility", "off");
5135  xset (ylabel.handle_value (), "handlevisibility", "off");
5136  xset (zlabel.handle_value (), "handlevisibility", "off");
5137  xset (title.handle_value (), "handlevisibility", "off");
5138 
5139  xset (xlabel.handle_value (), "horizontalalignment", "center");
5140  xset (xlabel.handle_value (), "horizontalalignmentmode", "auto");
5141  xset (ylabel.handle_value (), "horizontalalignment", "center");
5142  xset (ylabel.handle_value (), "horizontalalignmentmode", "auto");
5143  xset (zlabel.handle_value (), "horizontalalignment", "right");
5144  xset (zlabel.handle_value (), "horizontalalignmentmode", "auto");
5145  xset (title.handle_value (), "horizontalalignment", "center");
5146  xset (title.handle_value (), "horizontalalignmentmode", "auto");
5147 
5148  xset (xlabel.handle_value (), "verticalalignment", "top");
5149  xset (xlabel.handle_value (), "verticalalignmentmode", "auto");
5150  xset (ylabel.handle_value (), "verticalalignment", "bottom");
5151  xset (ylabel.handle_value (), "verticalalignmentmode", "auto");
5152  xset (title.handle_value (), "verticalalignment", "bottom");
5153  xset (title.handle_value (), "verticalalignmentmode", "auto");
5154 
5155  xset (ylabel.handle_value (), "rotation", 90.0);
5156  xset (ylabel.handle_value (), "rotationmode", "auto");
5157 
5158  xset (zlabel.handle_value (), "visible", "off");
5159 
5160  xset (xlabel.handle_value (), "clipping", "off");
5161  xset (ylabel.handle_value (), "clipping", "off");
5162  xset (zlabel.handle_value (), "clipping", "off");
5163  xset (title.handle_value (), "clipping", "off");
5164 
5165  xset (xlabel.handle_value (), "autopos_tag", "xlabel");
5166  xset (ylabel.handle_value (), "autopos_tag", "ylabel");
5167  xset (zlabel.handle_value (), "autopos_tag", "zlabel");
5168  xset (title.handle_value (), "autopos_tag", "title");
5169 
5170  update_transform ();
5171  sync_positions ();
5172  override_defaults (obj);
5173 }
5174 
5175 void
5177 {
5178  graphics_handle h = hp.handle_value ();
5179 
5180  if (h.ok ())
5181  {
5183 
5184  if (go.valid_object ())
5185  gh_manager::free (h);
5186 
5188  }
5189 
5190  // FIXME: is it necessary to check whether the axes object is
5191  // being deleted now? I think this function is only called when an
5192  // individual child object is delete and not when the parent axes
5193  // object is deleted.
5194 
5195  if (! is_beingdeleted ())
5196  {
5197  hp = gh_manager::make_graphics_handle ("text", __myhandle__,
5198  false, false);
5199 
5200  xset (hp.handle_value (), "handlevisibility", "off");
5201 
5202  adopt (hp.handle_value ());
5203  }
5204 }
5205 
5206 void
5208 {
5209  if (xlabel.handle_value ().ok () && h == xlabel.handle_value ())
5210  {
5211  delete_text_child (xlabel);
5212  update_xlabel_position ();
5213  }
5214  else if (ylabel.handle_value ().ok () && h == ylabel.handle_value ())
5215  {
5216  delete_text_child (ylabel);
5217  update_ylabel_position ();
5218  }
5219  else if (zlabel.handle_value ().ok () && h == zlabel.handle_value ())
5220  {
5221  delete_text_child (zlabel);
5222  update_zlabel_position ();
5223  }
5224  else if (title.handle_value ().ok () && h == title.handle_value ())
5225  {
5226  delete_text_child (title);
5227  update_title_position ();
5228  }
5229  else
5231 }
5232 
5233 inline Matrix
5235 {
5236  Matrix m (4, 4, 0.0);
5237  for (int i = 0; i < 4; i++)
5238  m(i,i) = 1;
5239  return m;
5240 }
5241 
5242 inline ColumnVector
5244 {
5245  ColumnVector v (4, 0.0);
5246  v(3) = 1;
5247  return v;
5248 }
5249 
5250 inline ColumnVector
5251 xform_vector (double x, double y, double z)
5252 {
5253  ColumnVector v (4, 1.0);
5254  v(0) = x; v(1) = y; v(2) = z;
5255  return v;
5256 }
5257 
5258 inline ColumnVector
5259 transform (const Matrix& m, double x, double y, double z)
5260 {
5261  return (m * xform_vector (x, y, z));
5262 }
5263 
5264 inline Matrix
5265 xform_scale (double x, double y, double z)
5266 {
5267  Matrix m (4, 4, 0.0);
5268  m(0,0) = x; m(1,1) = y; m(2,2) = z; m(3,3) = 1;
5269  return m;
5270 }
5271 
5272 inline Matrix
5273 xform_translate (double x, double y, double z)
5274 {
5275  Matrix m = xform_matrix ();
5276  m(0,3) = x; m(1,3) = y; m(2,3) = z; m(3,3) = 1;
5277  return m;
5278 }
5279 
5280 inline void
5281 scale (Matrix& m, double x, double y, double z)
5282 {
5283  m = m * xform_scale (x, y, z);
5284 }
5285 
5286 inline void
5287 translate (Matrix& m, double x, double y, double z)
5288 {
5289  m = m * xform_translate (x, y, z);
5290 }
5291 
5292 inline void
5293 xform (ColumnVector& v, const Matrix& m)
5294 {
5295  v = m*v;
5296 }
5297 
5298 inline void
5299 scale (ColumnVector& v, double x, double y, double z)
5300 {
5301  v(0) *= x;
5302  v(1) *= y;
5303  v(2) *= z;
5304 }
5305 
5306 inline void
5307 translate (ColumnVector& v, double x, double y, double z)
5308 {
5309  v(0) += x;
5310  v(1) += y;
5311  v(2) += z;
5312 }
5313 
5314 inline void
5316 {
5317  double fact = 1.0 / sqrt (v(0)*v(0)+v(1)*v(1)+v(2)*v(2));
5318  scale (v, fact, fact, fact);
5319 }
5320 
5321 inline double
5323 {
5324  return (v1(0)*v2(0)+v1(1)*v2(1)+v1(2)*v2(2));
5325 }
5326 
5327 inline double
5328 norm (const ColumnVector& v)
5329 {
5330  return sqrt (dot (v, v));
5331 }
5332 
5333 inline ColumnVector
5335 {
5336  ColumnVector r = xform_vector ();
5337  r(0) = v1(1)*v2(2)-v1(2)*v2(1);
5338  r(1) = v1(2)*v2(0)-v1(0)*v2(2);
5339  r(2) = v1(0)*v2(1)-v1(1)*v2(0);
5340  return r;
5341 }
5342 
5343 inline Matrix
5345 {
5346  static double data[32] =
5347  {
5348  0,0,0,1,
5349  1,0,0,1,
5350  0,1,0,1,
5351  0,0,1,1,
5352  1,1,0,1,
5353  1,0,1,1,
5354  0,1,1,1,
5355  1,1,1,1
5356  };
5357  Matrix m (4, 8);
5358  memcpy (m.fortran_vec (), data, sizeof (double)*32);
5359  return m;
5360 }
5361 
5362 inline ColumnVector
5364 {
5365  ColumnVector retval (4, 1.0);
5366  memcpy (retval.fortran_vec (), m.fortran_vec (), sizeof (double)*3);
5367  return retval;
5368 }
5369 
5370 inline RowVector
5372 {
5373  return v.extract_n (0, 3).transpose ();
5374 }
5375 
5376 void
5378 {
5379  double xd = (xdir_is ("normal") ? 1 : -1);
5380  double yd = (ydir_is ("normal") ? 1 : -1);
5381  double zd = (zdir_is ("normal") ? 1 : -1);
5382 
5383  Matrix xlimits = sx.scale (get_xlim ().matrix_value ());
5384  Matrix ylimits = sy.scale (get_ylim ().matrix_value ());
5385  Matrix zlimits = sz.scale (get_zlim ().matrix_value ());
5386 
5387  double xo = xlimits(xd > 0 ? 0 : 1);
5388  double yo = ylimits(yd > 0 ? 0 : 1);
5389  double zo = zlimits(zd > 0 ? 0 : 1);
5390 
5391  Matrix pb = get_plotboxaspectratio ().matrix_value ();
5392 
5393  bool autocam = (camerapositionmode_is ("auto")
5394  && cameratargetmode_is ("auto")
5395  && cameraupvectormode_is ("auto")
5396  && cameraviewanglemode_is ("auto"));
5397  bool dowarp = (autocam && dataaspectratiomode_is ("auto")
5398  && plotboxaspectratiomode_is ("auto"));
5399 
5400  ColumnVector c_eye (xform_vector ());
5401  ColumnVector c_center (xform_vector ());
5402  ColumnVector c_upv (xform_vector ());
5403 
5404  if (cameratargetmode_is ("auto"))
5405  {
5406  c_center(0) = (xlimits(0)+xlimits(1))/2;
5407  c_center(1) = (ylimits(0)+ylimits(1))/2;
5408  c_center(2) = (zlimits(0)+zlimits(1))/2;
5409 
5410  cameratarget = xform2cam (c_center);
5411  }
5412  else
5413  c_center = cam2xform (get_cameratarget ().matrix_value ());
5414 
5415  if (camerapositionmode_is ("auto"))
5416  {
5417  Matrix tview = get_view ().matrix_value ();
5418  double az = tview(0);
5419  double el = tview(1);
5420  double d = 5 * sqrt (pb(0)*pb(0)+pb(1)*pb(1)+pb(2)*pb(2));
5421 
5422  if (el == 90 || el == -90)
5423  c_eye(2) = d*signum (el);
5424  else
5425  {
5426  az *= M_PI/180.0;
5427  el *= M_PI/180.0;
5428  c_eye(0) = d * cos (el) * sin (az);
5429  c_eye(1) = -d* cos (el) * cos (az);
5430  c_eye(2) = d * sin (el);
5431  }
5432  c_eye(0) = c_eye(0)*(xlimits(1)-xlimits(0))/(xd*pb(0))+c_center(0);
5433  c_eye(1) = c_eye(1)*(ylimits(1)-ylimits(0))/(yd*pb(1))+c_center(1);
5434  c_eye(2) = c_eye(2)*(zlimits(1)-zlimits(0))/(zd*pb(2))+c_center(2);
5435 
5436  cameraposition = xform2cam (c_eye);
5437  }
5438  else
5439  c_eye = cam2xform (get_cameraposition ().matrix_value ());
5440 
5441  if (cameraupvectormode_is ("auto"))
5442  {
5443  Matrix tview = get_view ().matrix_value ();
5444  double az = tview(0);
5445  double el = tview(1);
5446 
5447  if (el == 90 || el == -90)
5448  {
5449  c_upv(0) =
5450  -signum (el) *sin (az*M_PI/180.0)*(xlimits(1)-xlimits(0))/pb(0);
5451  c_upv(1) =
5452  signum (el) * cos (az*M_PI/180.0)*(ylimits(1)-ylimits(0))/pb(1);
5453  }
5454  else
5455  c_upv(2) = 1;
5456 
5457  cameraupvector = xform2cam (c_upv);
5458  }
5459  else
5460  c_upv = cam2xform (get_cameraupvector ().matrix_value ());
5461 
5462  Matrix x_view = xform_matrix ();
5463  Matrix x_projection = xform_matrix ();
5464  Matrix x_viewport = xform_matrix ();
5465  Matrix x_normrender = xform_matrix ();
5466  Matrix x_pre = xform_matrix ();
5467 
5468  x_render = xform_matrix ();
5469  x_render_inv = xform_matrix ();
5470 
5471  scale (x_pre, pb(0), pb(1), pb(2));
5472  translate (x_pre, -0.5, -0.5, -0.5);
5473  scale (x_pre, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
5474  zd/(zlimits(1)-zlimits(0)));
5475  translate (x_pre, -xo, -yo, -zo);
5476 
5477  xform (c_eye, x_pre);
5478  xform (c_center, x_pre);
5479  scale (c_upv, pb(0)/(xlimits(1)-xlimits(0)), pb(1)/(ylimits(1)-ylimits(0)),
5480  pb(2)/(zlimits(1)-zlimits(0)));
5481  translate (c_center, -c_eye(0), -c_eye(1), -c_eye(2));
5482 
5483  ColumnVector F (c_center), f (F), UP (c_upv);
5484  normalize (f);
5485  normalize (UP);
5486 
5487  if (std::abs (dot (f, UP)) > 1e-15)
5488  {
5489  double fa = 1 / sqrt(1-f(2)*f(2));
5490  scale (UP, fa, fa, fa);
5491  }
5492 
5493  ColumnVector s = cross (f, UP);
5494  ColumnVector u = cross (s, f);
5495 
5496  scale (x_view, 1, 1, -1);
5497  Matrix l = xform_matrix ();
5498  l(0,0) = s(0); l(0,1) = s(1); l(0,2) = s(2);
5499  l(1,0) = u(0); l(1,1) = u(1); l(1,2) = u(2);
5500  l(2,0) = -f(0); l(2,1) = -f(1); l(2,2) = -f(2);
5501  x_view = x_view * l;
5502  translate (x_view, -c_eye(0), -c_eye(1), -c_eye(2));
5503  scale (x_view, pb(0), pb(1), pb(2));
5504  translate (x_view, -0.5, -0.5, -0.5);
5505 
5506  Matrix x_cube = x_view * unit_cube ();
5507  ColumnVector cmin = x_cube.row_min ();
5508  ColumnVector cmax = x_cube.row_max ();
5509  double xM = cmax(0)-cmin(0);
5510  double yM = cmax(1)-cmin(1);
5511 
5512  Matrix bb = get_boundingbox (true);
5513 
5514  double v_angle;
5515 
5516  if (cameraviewanglemode_is ("auto"))
5517  {
5518  double af;
5519 
5520  // FIXME: was this really needed? When compared to Matlab, it
5521  // does not seem to be required. Need investigation with concrete
5522  // graphics toolkit to see results visually.
5523  if (false && dowarp)
5524  af = 1.0 / (xM > yM ? xM : yM);
5525  else
5526  {
5527  if ((bb(2)/bb(3)) > (xM/yM))
5528  af = 1.0 / yM;
5529  else
5530  af = 1.0 / xM;
5531  }
5532  v_angle = 2 * (180.0 / M_PI) * atan (1 / (2 * af * norm (F)));
5533 
5534  cameraviewangle = v_angle;
5535  }
5536  else
5537  v_angle = get_cameraviewangle ();
5538 
5539  double pf = 1 / (2 * tan ((v_angle / 2) * M_PI / 180.0) * norm (F));
5540  scale (x_projection, pf, pf, 1);
5541 
5542  if (dowarp)
5543  {
5544  xM *= pf;
5545  yM *= pf;
5546  translate (x_viewport, bb(0)+bb(2)/2, bb(1)+bb(3)/2, 0);
5547  scale (x_viewport, bb(2)/xM, -bb(3)/yM, 1);
5548  }
5549  else
5550  {
5551  double pix = 1;
5552  if (autocam)
5553  {
5554  if ((bb(2)/bb(3)) > (xM/yM))
5555  pix = bb(3);
5556  else
5557  pix = bb(2);
5558  }
5559  else
5560  pix = (bb(2) < bb(3) ? bb(2) : bb(3));
5561  translate (x_viewport, bb(0)+bb(2)/2, bb(1)+bb(3)/2, 0);
5562  scale (x_viewport, pix, -pix, 1);
5563  }
5564 
5565  x_normrender = x_viewport * x_projection * x_view;
5566 
5567  x_cube = x_normrender * unit_cube ();
5568  cmin = x_cube.row_min ();
5569  cmax = x_cube.row_max ();
5570  x_zlim.resize (1, 2);
5571  x_zlim(0) = cmin(2);
5572  x_zlim(1) = cmax(2);
5573 
5574  x_render = x_normrender;
5575  scale (x_render, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
5576  zd/(zlimits(1)-zlimits(0)));
5577  translate (x_render, -xo, -yo, -zo);
5578 
5579  x_viewtransform = x_view;
5580  x_projectiontransform = x_projection;
5581  x_viewporttransform = x_viewport;
5582  x_normrendertransform = x_normrender;
5583  x_rendertransform = x_render;
5584 
5585  x_render_inv = x_render.inverse ();
5586 
5587  // Note: these matrices are a slight modified version of the regular
5588  // matrices, more suited for OpenGL rendering (x_gl_mat1 => light
5589  // => x_gl_mat2)
5590  x_gl_mat1 = x_view;
5591  scale (x_gl_mat1, xd/(xlimits(1)-xlimits(0)), yd/(ylimits(1)-ylimits(0)),
5592  zd/(zlimits(1)-zlimits(0)));
5593  translate (x_gl_mat1, -xo, -yo, -zo);
5594  x_gl_mat2 = x_viewport * x_projection;
5595 }
5596 
5597 static bool updating_axes_layout = false;
5598 
5599 void
5601 {
5602  if (updating_axes_layout)
5603  return;
5604 
5605  graphics_xform xform = get_transform ();
5606 
5607  double xd = (xdir_is ("normal") ? 1 : -1);
5608  double yd = (ydir_is ("normal") ? 1 : -1);
5609  double zd = (zdir_is ("normal") ? 1 : -1);
5610 
5611  const Matrix xlims = xform.xscale (get_xlim ().matrix_value ());
5612  const Matrix ylims = xform.yscale (get_ylim ().matrix_value ());
5613  const Matrix zlims = xform.zscale (get_zlim ().matrix_value ());
5614 
5615  double x_min, x_max, y_min, y_max, z_min, z_max;
5616  x_min = xlims(0), x_max = xlims(1);
5617  y_min = ylims(0), y_max = ylims(1);
5618  z_min = zlims(0), z_max = zlims(1);
5619 
5620  ColumnVector p1, p2, dir (3);
5621 
5622  xstate = ystate = zstate = AXE_ANY_DIR;
5623 
5624  p1 = xform.transform (x_min, (y_min+y_max)/2, (z_min+z_max)/2, false);
5625  p2 = xform.transform (x_max, (y_min+y_max)/2, (z_min+z_max)/2, false);
5626  dir(0) = xround (p2(0)-p1(0));
5627  dir(1) = xround (p2(1)-p1(1));
5628  dir(2) = (p2(2)-p1(2));
5629  if (dir(0) == 0 && dir(1) == 0)
5630  xstate = AXE_DEPTH_DIR;
5631  else if (dir(2) == 0)
5632  {
5633  if (dir(0) == 0)
5634  xstate = AXE_VERT_DIR;
5635  else if (dir(1) == 0)
5636  xstate = AXE_HORZ_DIR;
5637  }
5638 
5639  if (dir(2) == 0)
5640  {
5641  if (dir(1) == 0)
5642  xPlane = (dir(0) > 0 ? x_max : x_min);
5643  else
5644  xPlane = (dir(1) < 0 ? x_max : x_min);
5645  }
5646  else
5647  xPlane = (dir(2) < 0 ? x_min : x_max);
5648 
5649  xPlaneN = (xPlane == x_min ? x_max : x_min);
5650  fx = (x_max-x_min) / sqrt (dir(0)*dir(0)+dir(1)*dir(1));
5651 
5652  p1 = xform.transform ((x_min+x_max)/2, y_min, (z_min+z_max)/2, false);
5653  p2 = xform.transform ((x_min+x_max)/2, y_max, (z_min+z_max)/2, false);
5654  dir(0) = xround (p2(0)-p1(0));
5655  dir(1) = xround (p2(1)-p1(1));
5656  dir(2) = (p2(2)-p1(2));
5657  if (dir(0) == 0 && dir(1) == 0)
5658  ystate = AXE_DEPTH_DIR;
5659  else if (dir(2) == 0)
5660  {
5661  if (dir(0) == 0)
5662  ystate = AXE_VERT_DIR;
5663  else if (dir(1) == 0)
5664  ystate = AXE_HORZ_DIR;
5665  }
5666 
5667  if (dir(2) == 0)
5668  {
5669  if (dir(1) == 0)
5670  yPlane = (dir(0) > 0 ? y_max : y_min);
5671  else
5672  yPlane = (dir(1) < 0 ? y_max : y_min);
5673  }
5674  else
5675  yPlane = (dir(2) < 0 ? y_min : y_max);
5676 
5677  yPlaneN = (yPlane == y_min ? y_max : y_min);
5678  fy = (y_max-y_min) / sqrt (dir(0)*dir(0)+dir(1)*dir(1));
5679 
5680  p1 = xform.transform ((x_min+x_max)/2, (y_min+y_max)/2, z_min, false);
5681  p2 = xform.transform ((x_min+x_max)/2, (y_min+y_max)/2, z_max, false);
5682  dir(0) = xround (p2(0)-p1(0));
5683  dir(1) = xround (p2(1)-p1(1));
5684  dir(2) = (p2(2)-p1(2));
5685  if (dir(0) == 0 && dir(1) == 0)
5686  zstate = AXE_DEPTH_DIR;
5687  else if (dir(2) == 0)
5688  {
5689  if (dir(0) == 0)
5690  zstate = AXE_VERT_DIR;
5691  else if (dir(1) == 0)
5692  zstate = AXE_HORZ_DIR;
5693  }
5694 
5695  if (dir(2) == 0)
5696  {
5697  if (dir(1) == 0)
5698  zPlane = (dir(0) > 0 ? z_min : z_max);
5699  else
5700  zPlane = (dir(1) < 0 ? z_min : z_max);
5701  }
5702  else
5703  zPlane = (dir(2) < 0 ? z_min : z_max);
5704 
5705  zPlaneN = (zPlane == z_min ? z_max : z_min);
5706  fz = (z_max-z_min) / sqrt (dir(0)*dir(0)+dir(1)*dir(1));
5707 
5708  unwind_protect frame;
5709  frame.protect_var (updating_axes_layout);
5710  updating_axes_layout = true;
5711 
5712  xySym = (xd*yd*(xPlane-xPlaneN)*(yPlane-yPlaneN) > 0);
5713  zSign = (zd*(zPlane-zPlaneN) <= 0);
5714  xyzSym = zSign ? xySym : !xySym;
5715  xpTick = (zSign ? xPlaneN : xPlane);
5716  ypTick = (zSign ? yPlaneN : yPlane);
5717  zpTick = (zSign ? zPlane : zPlaneN);
5718  xpTickN = (zSign ? xPlane : xPlaneN);
5719  ypTickN = (zSign ? yPlane : yPlaneN);
5720  zpTickN = (zSign ? zPlaneN : zPlane);
5721 
5722  /* 2D mode */
5723  x2Dtop = false;
5724  y2Dright = false;
5725  layer2Dtop = false;
5726  if (xstate == AXE_HORZ_DIR && ystate == AXE_VERT_DIR)
5727  {
5728  if (xaxislocation_is ("top"))
5729  {
5730  std::swap (yPlane, yPlaneN);
5731  x2Dtop = true;
5732  }
5733  ypTick = yPlaneN;
5734  ypTickN = yPlane;
5735  if (yaxislocation_is ("right"))
5736  {
5737  std::swap (xPlane, xPlaneN);
5738  y2Dright = true;
5739  }
5740  xpTick = xPlaneN;
5741  xpTickN = xPlane;
5742  if (layer_is ("top"))
5743  {
5744  zpTick = zPlaneN;
5745  layer2Dtop = true;
5746  }
5747  else
5748  zpTick = zPlane;
5749  }
5750 
5751  Matrix viewmat = get_view ().matrix_value ();
5752  nearhoriz = std::abs (viewmat(1)) <= 5;
5753  is2D = viewmat(1) == 90;
5754 
5755  update_ticklength ();
5756 }
5757 
5758 void
5760 {
5761  bool mode2d = (((xstate > AXE_DEPTH_DIR ? 1 : 0) +
5762  (ystate > AXE_DEPTH_DIR ? 1 : 0) +
5763  (zstate > AXE_DEPTH_DIR ? 1 : 0)) == 2);
5764 
5765  if (tickdirmode_is ("auto"))
5766  tickdir.set (mode2d ? "in" : "out", true);
5767 
5768  double ticksign = (tickdir_is ("in") ? -1 : 1);
5769 
5770  Matrix bbox = get_boundingbox (true);
5771  Matrix ticklen = get_ticklength ().matrix_value ();
5772  ticklen(0) = ticklen(0) * std::max (bbox(2), bbox(3));
5773  ticklen(1) = ticklen(1) * std::max (bbox(2), bbox(3));
5774 
5775  xticklen = ticksign * (mode2d ? ticklen(0) : ticklen(1));
5776  yticklen = ticksign * (mode2d ? ticklen(0) : ticklen(1));
5777  zticklen = ticksign * (mode2d ? ticklen(0) : ticklen(1));
5778 
5779  xtickoffset = (mode2d ? std::max (0., xticklen) : std::abs (xticklen)) + 5;
5780  ytickoffset = (mode2d ? std::max (0., yticklen) : std::abs (yticklen)) + 5;
5781  ztickoffset = (mode2d ? std::max (0., zticklen) : std::abs (zticklen)) + 5;
5782 
5783  update_xlabel_position ();
5784  update_ylabel_position ();
5785  update_zlabel_position ();
5786  update_title_position ();
5787 }
5788 
5789 /*
5790 ## FIXME: A demo can't be called in a C++ file. This should be made a test
5791 ## or moved to a .m file where it can be called.
5792 %!demo
5793 %! clf;
5794 %! subplot (2,1,1);
5795 %! plot (rand (3));
5796 %! xlabel xlabel;
5797 %! ylabel ylabel;
5798 %! title title;
5799 %! subplot (2,1,2);
5800 %! plot (rand (3));
5801 %! set (gca, "ticklength", get (gca, "ticklength") * 2, "tickdir", "out");
5802 %! xlabel xlabel;
5803 %! ylabel ylabel;
5804 %! title title;
5805 */
5806 
5807 static ColumnVector
5809  const text::properties& props,
5810  const graphics_xform& xform,
5811  const Matrix& bbox)
5812 {
5813  ColumnVector retval;
5814 
5815  caseless_str to_units = props.get_units ();
5816 
5817  if (! to_units.compare ("data"))
5818  {
5819  ColumnVector v = xform.transform (p(0), p(1), p(2));
5820 
5821  retval.resize (3);
5822 
5823  retval(0) = v(0) - bbox(0) + 1;
5824  retval(1) = bbox(1) + bbox(3) - v(1) + 1;
5825  retval(2) = 0;
5826 
5827  retval = convert_position (retval, "pixels", to_units,
5828  bbox.extract_n (0, 2, 1, 2));
5829  }
5830  else
5831  retval = p;
5832 
5833  return retval;
5834 }
5835 
5836 static bool updating_xlabel_position = false;
5837 
5838 void
5840 {
5841  if (updating_xlabel_position)
5842  return;
5843 
5844  graphics_object obj = gh_manager::get_object (get_xlabel ());
5845 
5846  if (! obj.valid_object ())
5847  return;
5848 
5849  text::properties& xlabel_props
5850  = reinterpret_cast<text::properties&> (obj.get_properties ());
5851 
5852  bool is_empty = xlabel_props.get_string ().is_empty ();
5853 
5854  unwind_protect frame;
5855  frame.protect_var (updating_xlabel_position);
5856  updating_xlabel_position = true;
5857 
5858  if (! is_empty)
5859  {
5860  if (xlabel_props.horizontalalignmentmode_is ("auto"))
5861  {
5862  xlabel_props.set_horizontalalignment
5863  (xstate > AXE_DEPTH_DIR
5864  ? "center" : (xyzSym ? "left" : "right"));
5865 
5866  xlabel_props.set_horizontalalignmentmode ("auto");
5867  }
5868 
5869  if (xlabel_props.verticalalignmentmode_is ("auto"))
5870  {
5871  xlabel_props.set_verticalalignment
5872  (xstate == AXE_VERT_DIR || x2Dtop ? "bottom" : "top");
5873 
5874  xlabel_props.set_verticalalignmentmode ("auto");
5875  }
5876  }
5877 
5878  if (xlabel_props.positionmode_is ("auto")
5879  || xlabel_props.rotationmode_is ("auto"))
5880  {
5881  graphics_xform xform = get_transform ();
5882 
5883  Matrix ext (1, 2, 0.0);
5884  ext = get_ticklabel_extents (get_xtick ().matrix_value (),
5885  get_xticklabel ().all_strings (),
5886  get_xlim ().matrix_value ());
5887 
5888  double wmax = ext(0);
5889  double hmax = ext(1);
5890  double angle = 0;
5891  ColumnVector p =
5892  graphics_xform::xform_vector ((xpTickN+xpTick)/2, ypTick, zpTick);
5893 
5894  bool tick_along_z = nearhoriz || xisinf (fy);
5895  if (tick_along_z)
5896  p(2) += (signum (zpTick-zpTickN)*fz*xtickoffset);
5897  else
5898  p(1) += (signum (ypTick-ypTickN)*fy*xtickoffset);
5899 
5900  p = xform.transform (p(0), p(1), p(2), false);
5901 
5902  switch (xstate)
5903  {
5904  case AXE_ANY_DIR:
5905  p(0) += (xyzSym ? wmax : -wmax);
5906  p(1) += hmax;
5907  break;
5908 
5909  case AXE_VERT_DIR:
5910  p(0) -= wmax;
5911  angle = 90;
5912  break;
5913 
5914  case AXE_HORZ_DIR:
5915  p(1) += (x2Dtop ? -hmax : hmax);
5916  break;
5917  }
5918 
5919  if (xlabel_props.positionmode_is ("auto"))
5920  {
5921  p = xform.untransform (p(0), p(1), p(2), true);
5922 
5923  p = convert_label_position (p, xlabel_props, xform,
5924  get_extent (false));
5925 
5926  xlabel_props.set_position (p.extract_n (0, 3).transpose ());
5927  xlabel_props.set_positionmode ("auto");
5928  }
5929 
5930  if (! is_empty && xlabel_props.rotationmode_is ("auto"))
5931  {
5932  xlabel_props.set_rotation (angle);
5933  xlabel_props.set_rotationmode ("auto");
5934  }
5935  }
5936 }
5937 
5938 static bool updating_ylabel_position = false;
5939 
5940 void
5942 {
5943  if (updating_ylabel_position)
5944  return;
5945 
5946  graphics_object obj = gh_manager::get_object (get_ylabel ());
5947 
5948  if (! obj.valid_object ())
5949  return;
5950 
5951  text::properties& ylabel_props
5952  = reinterpret_cast<text::properties&> (obj.get_properties ());
5953 
5954  bool is_empty = ylabel_props.get_string ().is_empty ();
5955 
5956  unwind_protect frame;
5957  frame.protect_var (updating_ylabel_position);
5958  updating_ylabel_position = true;
5959 
5960  if (! is_empty)
5961  {
5962  if (ylabel_props.horizontalalignmentmode_is ("auto"))
5963  {
5964  ylabel_props.set_horizontalalignment
5965  (ystate > AXE_DEPTH_DIR
5966  ? "center" : (!xyzSym ? "left" : "right"));
5967 
5968  ylabel_props.set_horizontalalignmentmode ("auto");
5969  }
5970 
5971  if (ylabel_props.verticalalignmentmode_is ("auto"))
5972  {
5973  ylabel_props.set_verticalalignment
5974  (ystate == AXE_VERT_DIR && !y2Dright ? "bottom" : "top");
5975 
5976  ylabel_props.set_verticalalignmentmode ("auto");
5977  }
5978  }
5979 
5980  if (ylabel_props.positionmode_is ("auto")
5981  || ylabel_props.rotationmode_is ("auto"))
5982  {
5983  graphics_xform xform = get_transform ();
5984 
5985  Matrix ext (1, 2, 0.0);
5986 
5987  // The underlying get_extents() from FreeType produces mismatched values.
5988  // x-extent accurately measures the width of the glyphs.
5989  // y-extent instead measures from baseline-to-baseline.
5990  // Pad x-extent (+4) so that it approximately matches y-extent.
5991  // This keeps ylabels about the same distance from y-axis as
5992  // xlabels are from x-axis.
5993  // ALWAYS use an even number for padding or horizontal alignment
5994  // will be off.
5995  ext = get_ticklabel_extents (get_ytick ().matrix_value (),
5996  get_yticklabel ().all_strings (),
5997  get_ylim ().matrix_value ());
5998 
5999  double wmax = ext(0)+4;
6000  double hmax = ext(1);
6001  double angle = 0;
6002  ColumnVector p =
6003  graphics_xform::xform_vector (xpTick, (ypTickN+ypTick)/2, zpTick);
6004 
6005  bool tick_along_z = nearhoriz || xisinf (fx);
6006  if (tick_along_z)
6007  p(2) += (signum (zpTick-zpTickN)*fz*ytickoffset);
6008  else
6009  p(0) += (signum (xpTick-xpTickN)*fx*ytickoffset);
6010 
6011  p = xform.transform (p(0), p(1), p(2), false);
6012 
6013  switch (ystate)
6014  {
6015  case AXE_ANY_DIR:
6016  p(0) += (!xyzSym ? wmax : -wmax);
6017  p(1) += hmax;
6018  break;
6019 
6020  case AXE_VERT_DIR:
6021  p(0) += (y2Dright ? wmax : -wmax);
6022  angle = 90;
6023  break;
6024 
6025  case AXE_HORZ_DIR:
6026  p(1) += hmax;
6027  break;
6028  }
6029 
6030  if (ylabel_props.positionmode_is ("auto"))
6031  {
6032  p = xform.untransform (p(0), p(1), p(2), true);
6033 
6034  p = convert_label_position (p, ylabel_props, xform,
6035  get_extent (false));
6036 
6037  ylabel_props.set_position (p.extract_n (0, 3).transpose ());
6038  ylabel_props.set_positionmode ("auto");
6039  }
6040 
6041  if (! is_empty && ylabel_props.rotationmode_is ("auto"))
6042  {
6043  ylabel_props.set_rotation (angle);
6044  ylabel_props.set_rotationmode ("auto");
6045  }
6046  }
6047 }
6048 
6049 static bool updating_zlabel_position = false;
6050 
6051 void
6053 {
6054  if (updating_zlabel_position)
6055  return;
6056 
6057  graphics_object obj = gh_manager::get_object (get_zlabel ());
6058 
6059  if (! obj.valid_object ())
6060  return;
6061 
6062  text::properties& zlabel_props
6063  = reinterpret_cast<text::properties&> (obj.get_properties ());
6064 
6065  bool camAuto = cameraupvectormode_is ("auto");
6066  bool is_empty = zlabel_props.get_string ().is_empty ();
6067 
6068  unwind_protect frame;
6069  frame.protect_var (updating_zlabel_position);
6070  updating_zlabel_position = true;
6071 
6072  if (! is_empty)
6073  {
6074  if (zlabel_props.horizontalalignmentmode_is ("auto"))
6075  {
6076  zlabel_props.set_horizontalalignment
6077  ((zstate > AXE_DEPTH_DIR || camAuto) ? "center" : "right");
6078 
6079  zlabel_props.set_horizontalalignmentmode ("auto");
6080  }
6081 
6082  if (zlabel_props.verticalalignmentmode_is ("auto"))
6083  {
6084  zlabel_props.set_verticalalignment
6085  (zstate == AXE_VERT_DIR
6086  ? "bottom" : ((zSign || camAuto) ? "bottom" : "top"));
6087 
6088  zlabel_props.set_verticalalignmentmode ("auto");
6089  }
6090  }
6091 
6092  if (zlabel_props.positionmode_is ("auto")
6093  || zlabel_props.rotationmode_is ("auto"))
6094  {
6095  graphics_xform xform = get_transform ();
6096 
6097  Matrix ext (1, 2, 0.0);
6098  ext = get_ticklabel_extents (get_ztick ().matrix_value (),
6099  get_zticklabel ().all_strings (),
6100  get_zlim ().matrix_value ());
6101 
6102  double wmax = ext(0);
6103  double hmax = ext(1);
6104  double angle = 0;
6105  ColumnVector p;
6106 
6107  if (xySym)
6108  {
6109  p = graphics_xform::xform_vector (xPlaneN, yPlane,
6110  (zpTickN+zpTick)/2);
6111  if (xisinf (fy))
6112  p(0) += (signum (xPlaneN-xPlane)*fx*ztickoffset);
6113  else
6114  p(1) += (signum (yPlane-yPlaneN)*fy*ztickoffset);
6115  }
6116  else
6117  {
6118  p = graphics_xform::xform_vector (xPlane, yPlaneN,
6119  (zpTickN+zpTick)/2);
6120  if (xisinf (fx))
6121  p(1) += (signum (yPlaneN-yPlane)*fy*ztickoffset);
6122  else
6123  p(0) += (signum (xPlane-xPlaneN)*fx*ztickoffset);
6124  }
6125 
6126  p = xform.transform (p(0), p(1), p(2), false);
6127 
6128  switch (zstate)
6129  {
6130  case AXE_ANY_DIR:
6131  if (camAuto)
6132  {
6133  p(0) -= wmax;
6134  angle = 90;
6135  }
6136 
6137  // FIXME: what's the correct offset?
6138  //
6139  // p[0] += (!xySym ? wmax : -wmax);
6140  // p[1] += (zSign ? hmax : -hmax);
6141 
6142  break;
6143 
6144  case AXE_VERT_DIR:
6145  p(0) -= wmax;
6146  angle = 90;
6147  break;
6148 
6149  case AXE_HORZ_DIR:
6150  p(1) += hmax;
6151  break;
6152  }
6153 
6154  if (zlabel_props.positionmode_is ("auto"))
6155  {
6156  p = xform.untransform (p(0), p(1), p(2), true);
6157 
6158  p = convert_label_position (p, zlabel_props, xform,
6159  get_extent (false));
6160 
6161  zlabel_props.set_position (p.extract_n (0, 3).transpose ());
6162  zlabel_props.set_positionmode ("auto");
6163  }
6164 
6165  if (! is_empty && zlabel_props.rotationmode_is ("auto"))
6166  {
6167  zlabel_props.set_rotation (angle);
6168  zlabel_props.set_rotationmode ("auto");
6169  }
6170  }
6171 }
6172 
6173 static bool updating_title_position = false;
6174 
6175 void